CVE-2026-0049: DNG MapTable / LocalImageResolver DoS
Android / Pixel 脆弱性情報
| プログラム | Android Security Rewards Program |
|---|---|
| アドバイザリ | Android Security Bulletin - April 2026 |
| 公開日 / 更新日 | 2026-04-06 / 2026-04-08 |
| パッチレベル | 2026-04-01 |
| コンポーネント | Framework |
| Type / Severity | DoS / Critical |
| Updated AOSP versions | 14, 15, 16, 16-qpr2 |
| References | A-456471290, additional AOSP change reference 2 |
フォルダ: 01-ok-CVE-2026-0049-DNG-MapTable-LocalImageResolver-DoS
具体的な脆弱性
LocalImageResolver 経由でDNG/TIFF系画像を ImageDecoder に到達させると、DNG SDK の dng_opcode_MapTable::ProcessArea() で Plane + Planes の 32-bit unsigned overflow が発生し、サニタイザによりabortするローカルDoS。
原因
DNG Opcode の外部入力 fPlane / fPlanes を信頼し、uint32_t 加算で終端Planeを計算していた。dng_area_spec::GetData() は fPlanes >= 1 などしか検証せず、fPlane + fPlanes のオーバーフローを拒否していなかった。加えて LocalImageResolver はサイズ制限だけで、DNGのような複雑なコーデックを拒否していなかった。
特定根拠
AOSP差分 external/dng_sdk commit 80a267ed、frameworks/base commit 78ec493、追加テスト dng_opcode_MapTable_ProcessArea.png の OpcodeList2 解析、reproduce_plane_overflow.cpp のサニタイザ付き最小再現から特定。
- テストファイルは .png 名だが実体はTIFF/DNG。
- Opcode[2] MapTable の plane=0x0000ffff、planes=0xffff0001 により旧式の終端計算が 0x100000000 へ桁あふれする。
cve.md を表示
# CVE-2026-0049 ## Summary - Program target: Android Security Rewards Program - Advisory: Android Security Bulletin - April 2026 - Published: 2026-04-06 - Updated: 2026-04-08 - Security patch level: 2026-04-01 - Component: Framework - Type: DoS - Severity: Critical - Updated AOSP versions: 14, 15, 16, 16-qpr2 - References: A-456471290, additional AOSP change reference 2 ## Advisory Information The Android April 2026 bulletin describes this Framework vulnerability as a local denial of service issue requiring no additional execution privileges and no user interaction. The bulletin identifies it as the most severe issue for the month. ## Bounty Scope Decision Included. This is an Android Framework vulnerability documented in the Android Security Bulletin with AOSP version coverage. It is not listed as Linux kernel, external OSS, or a third-party vendor component. ## Source - https://source.android.com/docs/security/bulletin/2026/2026-04-01
validated.md を表示
# CVE-2026-0049 検証結果
## 判定
OK。ソースコード差分、追加テストファイル、DNG OpcodeListの実値、unsigned overflow sanitizer付きの最小再現で、脆弱性の条件と修正意図を説明できた。
## 概要
CVE-2026-0049 は、Android Framework の `LocalImageResolver` 経由でDNG/TIFF系画像を `ImageDecoder` に渡せることと、DNG SDK側の `dng_opcode_MapTable::ProcessArea()` にあるPlane範囲計算の32-bit unsigned overflowが組み合わさったローカルDoSと判断した。
攻撃者が細工したDNGを、通知のMessagingStyle/ConversationStyle画像など `LocalImageResolver` が処理する画像として到達させると、DNGデコード中に `MapTable` opcodeが処理される。問題の入力では `Plane + Planes` が `uint32_t` の範囲を超える。Androidのサニタイザ設定ではこのunsigned overflowがruntime errorとして扱われ、デコード処理側プロセスをabortさせる。
## 参照したAOSP修正
- Android Security Bulletin: https://source.android.com/docs/security/bulletin/2026/2026-04-01
- `external/dng_sdk`: `80a267ed1ac714acff455e85ae28c1732777d5b6`
- Commit message: `Handle underflows in dng_opcode_MapTable`
- 対象: `source/dng_misc_opcodes.cpp`
- `frameworks/base`: `78ec493d6192240da0d0d37be93c6921eff403e7`
- Commit message: `Add mimetype filter to LocalImageResolver`
- 対象: `core/java/com/android/internal/widget/LocalImageResolver.java`
- 追加テスト: `core/tests/coretests/res/raw/dng_opcode_MapTable_ProcessArea.png`
## パッチ差分から分かったこと
### dng_sdk側
修正前の `dng_opcode_MapTable::ProcessArea()` は以下のように、開始PlaneとPlane数を加算して終端Planeを作っていた。
```cpp
for (uint32 plane = fAreaSpec.Plane ();
plane < fAreaSpec.Plane () + fAreaSpec.Planes () &&
plane < buffer.Planes ();
plane++)
```
修正後は、加算で終端を作らず、`plane - planeStart < planeCount` で範囲内かどうかを判定する形に変わった。
```cpp
const uint32 planeStart = fAreaSpec.Plane ();
const uint32 planeCount = fAreaSpec.Planes ();
const uint32 bufferPlanes = buffer.Planes ();
for (uint32 plane = planeStart;
plane < bufferPlanes &&
plane - planeStart < planeCount;
++plane)
```
根本的な差は `planeStart + planeCount` を評価しなくなったこと。DNGの `dng_area_spec::GetData()` は `fPlanes < 1` と `rowPitch/colPitch < 1` しか拒否しておらず、`fPlane + fPlanes` が `uint32_t` でオーバーフローしないことを保証していなかった。
### Framework側
修正前の `LocalImageResolver.onHeaderDecoded()` は画像サイズの上限だけを見ており、DNG/TIFF系のような複雑なコーデックも `ImageDecoder` に渡せた。
修正後は `ImageDecoder.ImageInfo.getMimeType()` を確認し、以下のMIMEだけを許可する。
- `image/png`
- `image/jpeg`
- `image/webp`
- `image/gif`
- `image/bmp`
- `image/x-ico`
- `image/vnd.wap.wbmp`
- `image/heif`
- `image/heic`
- `image/avif`
DNGは許可リストに含まれないため、`LocalImageResolver` の時点で `RuntimeException` になり、外側で `IOException` として扱われる。追加テストも `dng_opcode_MapTable_ProcessArea.png` に対して `IOException` を期待している。
## 追加テストファイルの解析
`frameworks/base` の修正で追加された `dng_opcode_MapTable_ProcessArea.png` は、拡張子は `.png` だが実体はPNGではない。
ローカル解析結果:
```text
file: TIFF image data, little-endian, direntries=52, height=144, width=256
sha256: cb576e523bcc7f476d8227b534a6c4e2ed31dc6533561c5bebe961955971d581
size: 87460 bytes
```
`tiffdump` ではIFD0に異常な値も見える。
```text
ImageWidth = 256
ImageLength = 3892314256
SubIFD = 12580
```
SubIFD `12580` のタグ `51009`、つまりDNG `OpcodeList2` を復元すると、3個のopcodeが入っていた。問題の3個目は `MapTable`。
```text
Opcode[2] id=7 MapTable
area.t=195
area.l=-1979711488
area.b=338
area.r=600
plane=65535 0x0000ffff
planes=4294901761 0xffff0001
rowPitch=13
colPitch=241
table_count=16
```
ここで旧コードの終端計算は次の通り。
```text
0x0000ffff + 0xffff0001 = 0x100000000
```
`uint32_t` では `0x00000000` に桁あふれする。
## 最小再現
`analysis/reproduce_plane_overflow.cpp` に、追加テストDNGから復元した `plane` と `planes` を使った最小再現を書いた。
実行コマンド:
```sh
clang++ -std=c++17 -fsanitize=unsigned-integer-overflow \
-fno-sanitize-recover=unsigned-integer-overflow \
analysis/reproduce_plane_overflow.cpp \
-o analysis/reproduce_plane_overflow
./analysis/reproduce_plane_overflow
```
結果:
```text
runtime error: unsigned integer overflow: 65535 + 4294901761 cannot be represented in type 'uint32_t'
exit_status=134
```
これはAndroid実機でのクラッシュログではないが、パッチが消した算術式そのものに対して、サニタイザがabort対象のruntime errorを出すことを確認できる。
## 脆弱性の根本原因
根本原因は、DNG Opcodeの外部入力値 `fPlane` と `fPlanes` を信頼したまま、`uint32_t` 加算で終端Planeを計算していたこと。
`dng_area_spec::GetData()` は `fPlanes >= 1` だけを検証し、`fPlane + fPlanes` のオーバーフローを検証していない。そのため、DNG内の `MapTable` opcodeに `plane=0x0000ffff`、`planes=0xffff0001` のような値を入れられると、`ProcessArea()` のループ条件評価でunsigned overflowが発生する。
Framework側の根本的な問題は、通知・会話画像向けの軽量な画像解決クラスである `LocalImageResolver` が、実際にはDNGのような複雑で攻撃面の大きいコーデックまで許可していたこと。サイズ制限はあったが、コーデック種別の制限がなかった。
## どういう脆弱性だったか
ローカルアプリなどが細工した画像URIまたはリソースを `LocalImageResolver` の処理経路へ渡す。`LocalImageResolver` はパッチ前、DNG MIMEを拒否しないため `ImageDecoder` がDNGデコードへ進む。DNG内の `OpcodeList2` に含まれる `MapTable` opcodeで `Plane + Planes` がoverflowし、unsigned integer overflow sanitizerによりabortする。影響はローカルDoS。
アドバイザリの「no additional execution privileges needed」「User interaction is not needed」は、攻撃者が特権なしで通知などのローカル処理経路に画像を渡し、ユーザー操作なしにシステムUI/通知表示側の画像解決を誘発できる、という形と整合する。
## 面白い点・調査メモ
- CVEはFrameworkコンポーネントとして掲載されているが、修正は `frameworks/base` だけでなく `external/dng_sdk` にも入っている。
- `frameworks/base` のテストファイル名は `.png` だが、実体はTIFF/DNG。拡張子や表面的な名前ではなく、`ImageDecoder` が見たMIME/ヘッダで処理される。
- `LocalImageResolver` の修正はDNG SDKのバグそのものを直すものではなく、通知画像の攻撃面からDNGなどを外す防御層。
- `dng_sdk` の修正は `MapTable` だけに入っている。同じような `fAreaSpec.Plane() + fAreaSpec.Planes()` 形のループは `MapPolynomial`、`DeltaPerRow`、`DeltaPerColumn`、`ScalePerRow`、`ScalePerColumn` にも残っているように見える。ただし今回の追加テストとCVE対象は `MapTable`。
- `ScaledOverlap()` には `DNG_ATTRIB_NO_SANITIZE("unsigned-integer-overflow")` が追加されている。こちらはDNG SDK内で意図的または既存許容されていたunsigned演算をサニタイザ対象から外す変更で、CVEの中心は `MapTable::ProcessArea()` のPlane範囲計算修正と見ている。
## 付帯ファイル
- `analysis/80a267ed-dng_sdk.patch`: DNG SDK側のAOSP差分
- `analysis/78ec493-frameworks-base.patch`: Framework側のAOSP差分
- `analysis/dng_misc_opcodes_before.cpp`: DNG SDK修正前ソース
- `analysis/dng_misc_opcodes_after.cpp`: DNG SDK修正後ソース
- `analysis/LocalImageResolver_before.java`: Framework修正前ソース
- `analysis/LocalImageResolver_after.java`: Framework修正後ソース
- `analysis/dng_opcode_MapTable_ProcessArea.png`: AOSPテストから取得したDNGサンプル
- `analysis/tiffdump.txt`: IFD0のTIFF解析結果
- `analysis/tiffdump-subifd-12580.txt`: SubIFD解析結果
- `analysis/opcodelist2_decode.txt`: OpcodeList2の復元結果
- `analysis/reproduce_plane_overflow.cpp`: 算術overflowの最小再現
- `analysis/reproduce_plane_overflow.out`: サニタイザ付き実行結果