CVE-2025-65018
001-ok-libpng-png_combine_row-overflow
cve.md の Android/Pixel 脆弱性情報
| Bulletin | Android Security Bulletin |
|---|---|
| Source | https://source.android.com/docs/security/bulletin/2026/2026-06-01 |
| Published/updated | June 1, 2026; updated June 3, 2026 |
| Security patch level | 2026-06-01 or later |
| Component | Framework |
| Type | EoP |
| Severity | Critical |
| Updated AOSP versions | 14, 15, 16, 16-qpr2 |
| References | A-463998243 |
| Included | Android Security Rewards candidate: AOSP Framework/System item, not a Linux kernel, OSS upstream, chipset, or other-vendor section. |
| Exclusion policy applied for this workspace | Linux/upstream kernel, general OSS/upstream-only issues, chipset/vendor sections, and other-vendor components were not created as target folders. |
| Short Summary | CVE-2025-65018 is listed in the Android Security Bulletin as a Critical severity EoP issue in Framework. |
特定した具体的な脆弱性
特定できた。CVE-2025-65018 は Android Framework 枠で告知されているが、修正対象の実体は platform/external/libpng の simplified read API である。脆弱性は、16-bit Adam7 interlace PNG を png_image_finish_read() で 8-bit 出力形式、例: PNG_FORMAT_RGBA、へ変換して読むときに、png_combine_row() が変換前の IHDR bit depth に基づく row 幅でユーザー出力バッファへ書き、PNG_IMAGE_SIZE(image) で確保された 8-bit 出力サイズを超えるヒープバッファオーバーフローである。
Android の分類は Framework / EoP / Critical、参照 ID は A-463998243。OSV では GHSA-7wv6-48j4-hj3g と対応し、詳細は「heap buffer overflow による remote code execution / remote escalation of privilege の可能性」とされている。
原因
根本原因は simplified API の公開契約と interlace 内部処理の不整合である。
PNG_IMAGE_SIZE(image) は要求された出力形式を基準にバッファサイズを返す。通常これは png_image_finish_read() の出力サイズと一致するべきだが、16-bit interlaced PNG の 16-to-8 変換では、Adam7 pass の合成処理が bit-depth 変換完了前に走る。そこで png_combine_row() は IHDR の 16-bit depth を基準に destination row へ書くため、destination がすでに最終 8-bit 出力バッファである場合に境界外書き込みになる。
非 interlace PNG は同じ interlace 合成経路を通らないため、この特定の overflow は起きない。これが初期修正の「全 16-to-8 変換拒否」が過剰で、最終修正が interlace の場合だけ中間 row バッファを使う設計になった理由である。
どこから特定したか
パッチ解析: Android Bulletin の対象行には libpng の 6 コミットが並ぶ。うち Bug: 463998243 として直接この CVE を直すのは次の 2 つ。
保存した付帯ファイル: ・2cafeca8_rearchitect.diff: Android 最終修正 diff。
・960064f_initial_fix.diff: Android 初期修正 diff。
・upstream_218612dd_final.patch: upstream 最終修正 patch。
・upstream_16b5e382_initial.patch: upstream 初期修正 patch。
・pngread_vulnerable_parent_2113a245.c: 対象修正直前の pngread.c。
・pngread_after_initial_960064f.c: 初期修正後の pngread.c。
・pngread_after_final_2cafeca8.c: 最終修正後の pngread.…
URL: https://source.android.com/docs/security/bulletin/2026/2026-06-01 / https://osv.dev/vulnerability/ASB-A-463998243 / https://github.com/pnggroup/libpng/security/advisories/GHSA-7wv6-48j4-hj3g / https://android.googlesource.com/platform/external/libpng/+/960064fdb5f41cc085ab81cd97443dd51…
validated.md を表示
# CVE-2025-65018 検証メモ ## 結論 特定できた。CVE-2025-65018 は Android Framework 枠で告知されているが、修正対象の実体は `platform/external/libpng` の simplified read API である。脆弱性は、16-bit Adam7 interlace PNG を `png_image_finish_read()` で 8-bit 出力形式、例: `PNG_FORMAT_RGBA`、へ変換して読むときに、`png_combine_row()` が変換前の IHDR bit depth に基づく row 幅でユーザー出力バッファへ書き、`PNG_IMAGE_SIZE(image)` で確保された 8-bit 出力サイズを超えるヒープバッファオーバーフローである。 Android の分類は Framework / EoP / Critical、参照 ID は `A-463998243`。OSV では `GHSA-7wv6-48j4-hj3g` と対応し、詳細は「heap buffer overflow による remote code execution / remote escalation of privilege の可能性」とされている。 ## 対象情報 - CVE: `CVE-2025-65018` - Android bug/reference: `A-463998243` - Component: `Framework` - Type: `EoP` - Severity: `Critical` - Updated AOSP versions: `14, 15, 16, 16-qpr2` - Bulletin: https://source.android.com/docs/security/bulletin/2026/2026-06-01 - Android OSV: https://osv.dev/vulnerability/ASB-A-463998243 - Upstream advisory: https://github.com/pnggroup/libpng/security/advisories/GHSA-7wv6-48j4-hj3g ## パッチ解析 Android Bulletin の対象行には libpng の 6 コミットが並ぶ。うち `Bug: 463998243` として直接この CVE を直すのは次の 2 つ。 - `960064fdb5f41cc085ab81cd97443dd51736c4aa` - URL: https://android.googlesource.com/platform/external/libpng/+/960064fdb5f41cc085ab81cd97443dd51736c4aa - upstream 相当: `16b5e3823918840aae65c0a6da57c78a5a496a4d` - 内容: `png_image_finish_read()` の先頭付近で、IHDR が 16-bit なのに要求出力が 8-bit、またはその逆、という bit-depth mismatch を拒否する暫定修正。 - 問題: interlace でない 16-to-8 変換まで拒否し、simplified API の正当なユースケースを壊す。 - `2cafeca8cbf2fa636b658b7bef0aebe900447f40` - URL: https://android.googlesource.com/platform/external/libpng/+/2cafeca8cbf2fa636b658b7bef0aebe900447f40 - upstream 相当: `218612ddd6b17944e21eda56caf8b4bf7779d1ea` - 内容: 暫定修正を削除し、16-to-8 変換かつ interlace の場合のみ `do_local_scale` を立て、`png_image_read_direct_scaled()` で中間 row バッファに `png_read_row()` してからユーザーバッファへ `memcpy()` する。 Bulletin 同行の残り 4 コミットは同じ libpng 更新束に含まれる別脆弱性/別 bug の修正だった。 - `2113a245b832fe86bb2c61a99778d8e70149bd3b`: `Bug: 463995203`, `png_init_read_transformations`, CVE-2025-64720 側。 - `93147074acc1bebdda8316d9a891a5d4d122ca80`: `Bug: 463980379`, `png_do_quantize`。 - `3f148ea688b5dc631c51699947ce5dd3654a5eb0`: `Bug: 463980379`, `png_set_quantize` memory leak/refactor。 - `2ccc385b188d5f4856c1ea441db21a06eb420534`: `Bug: 463990846`, `png_write_image_8bit`。 ## 脆弱条件 攻撃に必要な条件は以下。 - アプリ/Framework 側が libpng simplified API、特に `png_image_begin_read_*()` と `png_image_finish_read()` を使う。 - 入力 PNG の IHDR が 16-bit color depth で、interlace method が Adam7。 - 呼び出し側が `image.format = PNG_FORMAT_RGBA` など、`PNG_FORMAT_FLAG_LINEAR` を含まない 8-bit 出力を要求する。 - 呼び出し側が documented pattern 通り `PNG_IMAGE_SIZE(image)` で出力バッファを確保し、そのバッファを `png_image_finish_read()` に渡す。 `png.h` では `PNG_FORMAT_FLAG_LINEAR` がない形式の component size は 1 byte、ある形式は 2 bytes と定義される。`PNG_IMAGE_SIZE(image)` は `PNG_IMAGE_PIXEL_COMPONENT_SIZE(image.format) * image.height * PNG_IMAGE_ROW_STRIDE(image)` で計算されるため、`PNG_FORMAT_RGBA` なら 8-bit RGBA、つまり 4 bytes/pixel 分しか確保されない。 一方、脆弱版 `pngread.c` は bit-depth 変更を検出すると 8-bit 出力側で `png_set_scale_16(png_ptr)` を設定するだけだった。そのまま通常の direct read path に入り、interlace の pass 処理中に `png_read_row(png_ptr, row, NULL)` の `row` がユーザー出力バッファを直接指す。interlace 合成では内部の `png_combine_row()` が変換前の IHDR 16-bit row として書くタイミングがあり、8-bit 出力サイズのバッファに 16-bit 幅のデータを書いてしまう。 例として 32x32, 16-bit RGB, Adam7 interlace PNG を 8-bit RGBA で読む場合: - 呼び出し側の想定出力: 32 * 32 * 4 bytes = 4096 bytes - interlace 合成時の変換前 row: 32 * 32 * 3 channels * 2 bytes = 6144 bytes - 差分: 2048 bytes の過剰書き込み 256x256 なら差分は 131072 bytes になり、画像サイズに比例して増える。 ## 根本原因 根本原因は simplified API の公開契約と interlace 内部処理の不整合である。 `PNG_IMAGE_SIZE(image)` は要求された出力形式を基準にバッファサイズを返す。通常これは `png_image_finish_read()` の出力サイズと一致するべきだが、16-bit interlaced PNG の 16-to-8 変換では、Adam7 pass の合成処理が bit-depth 変換完了前に走る。そこで `png_combine_row()` は IHDR の 16-bit depth を基準に destination row へ書くため、destination がすでに最終 8-bit 出力バッファである場合に境界外書き込みになる。 非 interlace PNG は同じ interlace 合成経路を通らないため、この特定の overflow は起きない。これが初期修正の「全 16-to-8 変換拒否」が過剰で、最終修正が interlace の場合だけ中間 row バッファを使う設計になった理由である。 ## 修正内容 最終修正 `2cafeca8...` は以下を追加した。 - `png_image_read_direct()` に `do_local_scale` を追加。 - bit-depth change で 8-bit 出力を選び、かつ `png_ptr->interlaced != 0` のとき `do_local_scale = 1`。 - `do_local_scale` のとき、`png_get_rowbytes(png_ptr, info_ptr)` で中間 row バッファを確保。 - 新規 `png_image_read_direct_scaled()` で各 Adam7 pass/row を中間バッファへ `png_read_row()` し、変換済みの `display->row_bytes` だけユーザー出力バッファへコピー。 - 初期修正の bit-depth mismatch 拒否ロジックを削除し、非 interlace の正当な 16-to-8 変換は維持。 重要な点は、危険な `png_read_row()` の destination をユーザーが `PNG_IMAGE_SIZE(image)` で確保した最終出力バッファから、libpng 内部が十分な row サイズで確保した中間バッファに切り替えたこと。これにより `png_combine_row()` が IHDR 16-bit 幅で一時的に書いても、ユーザー出力バッファは破壊されない。 ## 検証で見つかったこと - Android Bulletin では Framework 扱いだが、ソースレベルの修正は `external/libpng/pngread.c` で、AOSP Framework Java/Kotlin の権限チェック欠陥ではない。 - Bulletin の `CVE-2025-65018` 行に 6 コミットがあるため、単純に全リンクを同一 CVE の直接修正と見ると誤る。`Bug: 463998243` は `960064f...` と `2cafeca8...` の 2 つで、他は同じ libpng 更新束の別 bug。 - OSV の `ASB-A-463998243` は Android 14/15/16/16-qpr2/17-next それぞれの backport commit を列挙している。`osv-fixes-summary.txt` に保存した。 - upstream advisory では影響範囲が libpng `>= 1.6.0, < 1.6.51`、patched version が `1.6.51` とされている。 - `png_safe_execute()` は libpng の `png_error()`/longjmp を安全に扱うための枠であり、境界外 `memcpy`/row write そのものを事前に防ぐものではない。今回のように内部 row サイズ計算が契約とずれる場合は、safe wrapper だけでは防げない。 ## 保存した付帯ファイル - `2cafeca8_rearchitect.diff`: Android 最終修正 diff。 - `960064f_initial_fix.diff`: Android 初期修正 diff。 - `upstream_218612dd_final.patch`: upstream 最終修正 patch。 - `upstream_16b5e382_initial.patch`: upstream 初期修正 patch。 - `pngread_vulnerable_parent_2113a245.c`: 対象修正直前の `pngread.c`。 - `pngread_after_initial_960064f.c`: 初期修正後の `pngread.c`。 - `pngread_after_final_2cafeca8.c`: 最終修正後の `pngread.c`。 - `png_h_android16_r2.txt`: `PNG_FORMAT_*` / `PNG_IMAGE_SIZE` マクロ確認用。 - `osv-ASB-A-463998243.json`: Android OSV raw JSON。 - `osv-fixes-summary.txt`: OSV から抽出した aliases/details/branch fixes。 ## 判定 `ok`。公開ソースコード差分、修正前後の `pngread.c`、upstream advisory、Android OSV の対応を照合し、脆弱性が起きる入力条件、オーバーフローの理由、根本原因、修正方式を説明できる。