LIBHEIF-0001
medium verifiedGrid NULL-pointer dereference in decode_grid_tile on missing tile reference
heif_image_handle_decode_image_tilepublic API libheif public per-tile C API — heif_image_handle_decode_image_tile (no codec back-end) Reproduction command (ubsan)
cmake ASan/UBSan Debug libheif @ 78638f4f, clang-20 (reuse project/builds/unc_asan)
./decode_tile poc-grid-missing-tile-303b.heic # public per-tile API heif_image_handle_decode_image_tile
# expect: runtime error: member call on null pointer of type 'ImageItem' at grid.cc:590 (UBSan); with -fno-sanitize-recover this is a hard crash / DoS Classification
| Target | libheif |
|---|---|
| Component | Grid image item |
| Location | libheif/image-items/grid.cc · ImageItem_Grid::decode_grid_tile:590 |
| Entry point | heif_image_handle_decode_image_tile public API heif_image_handle_decode_image_tile → ImageItem_Grid::decode_grid_tile (grid.cc:588-590) → get_context()->get_image(tile_id,true) returns empty shared_ptr → tile_item->get_item_error() on NULLThe per-tile public API is the reachable consumer on 78638f4f. The whole-image path heif_decode_image does NOT reach the null deref: it runs a grid-assembly pre-check that returns "Missing grid images: Tile image ID=… is not a proper image" BEFORE calling decode_grid_tile, so the full-image decode fails gracefully. The per-tile API decodes a single tile directly and bypasses that pre-check, hitting the unchecked null. Fixed upstream in v1.22.0 (e1b97646) by adding the null check the sibling derived-image classes already had. |
| Vuln class | dos |
| CVE | — |
| CVSS | 6.5 (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H) |
| Discovered | 2026-05-03 |
Verification
| Evidence | ✓ Disclosure ready (real-world verified) |
|---|---|
| Verified through | libheif public per-tile C API — heif_image_handle_decode_image_tile (no codec back-end) |
| Harness fired | ✅ yes |
| Protocol | 2.0 |
| Sanitizer | ubsan |
| Crash type | null-pointer member call of type 'ImageItem' at grid.cc:590 (UBSan); SEGV / DoS |
| Repro | RE-VERIFIED 2026-06-22 on a freshly-rebuilt pristine 78638f4f ASan/UBSan lib. decode_tile.c opens poc-grid-missing-tile-303b.heic with the public C API (heif_context_read_from_file -> heif_context_get_primary_image_handle) and calls heif_image_handle_decode_image_tile(handle,…,tx=0,ty=0): UBSan reports "runtime error: member call on null pointer of type 'ImageItem'" at image-items/grid.cc:590:31 -> hard crash with -fno-sanitize-recover (DoS). Note the whole-image heif_decode_image path does NOT reach it on this build (grid-assembly pre-check returns "Missing grid images" first); the per-tile public API is the reachable consumer. Originally found 2026-05-03 via the decode_grid_overlay fuzz runner; disclosed (GHSA, 2026-05-03) and fixed in v1.22.0 (e1b97646) -> evidence disclosure_ready. |
Reproduce / self-verify
# build
cmake ASan/UBSan Debug libheif @ 78638f4f, clang-20 (reuse project/builds/unc_asan)
# run
./decode_tile poc-grid-missing-tile-303b.heic # public per-tile API heif_image_handle_decode_image_tile
# expect (ubsan):
runtime error: member call on null pointer of type 'ImageItem' at grid.cc:590 (UBSan); with -fno-sanitize-recover this is a hard crash / DoS cost: low (303-byte file)
Disclosure
| Reported to | GitHub private security advisory — strukturag/libheif |
|---|---|
| Reported | 2026-05-03 |
| Vendor ack | 2026-05-18 |
| Embargo until | — |
| Public | 2026-05-19 |
| Patched in | 1.22.0 |
PoC: pocs/libheif/LIBHEIF-0001/poc-grid-missing-tile-303b.heic (303-byte HEIF) + decode_tile.c (public per-tile API harness), ubsan-pertile-publicapi.txt
Writeup
Summary
ImageItem_Grid::decode_grid_tile resolves each grid tile id via
get_context()->get_image(tile_id, true) and then calls
tile_item->get_item_error() without a null check (grid.cc:590).
HeifContext::get_image(id, true) returns an empty shared_ptr for any id that
doesn’t resolve to an image item, so a grid whose dimg references non-existent
(or non-image) ids dereferences NULL and SIGSEGVs — a deterministic DoS on parse.
Reproduction
303-byte HEIF: a grid item declaring 2×2 tiles whose iref dimg lists four
item ids that don’t exist. The parse-time dimg.size() == rows×columns check
passes (4 == 4); each id is resolved at decode time and none exists.
libheif/image-items/grid.cc:590:31: runtime error: member call on null pointer
==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000
SUMMARY: AddressSanitizer: SEGV grid.cc:590:31 in ImageItem_Grid::decode_grid_tile
Found within ~30 s by a targeted structure-aware seed
(gen_07_grid_dimg_to_missing), reachable from heif_decode_image and
heif_image_handle_decode_image_tile.
Root cause
Missing null check on the resolved tile item. The sibling-class audit is
decisive: overlay.cc:339, iden.cc:87, and iden.cc:109 all perform exactly
this null check; grid.cc:589-590 is the one derived-image class that omits it.
Fixed upstream in v1.22.0 (commit e1b97646) by adding the same null check
the siblings already had.
Verification evidence
Verified per the verification protocol
v1.0: deterministic SEGV (null deref) at grid.cc:590 on 1.21.2 (release and
debug); re-tested against a fresh v1.22.0 build → rc=0, no signal. Reachable with
no codec back-end installed. Fixed upstream but no CVE assigned for this
finding, so it is recorded as verified (not patched).
Impact
Per the impact rubric: medium (CWE-476 → DoS). Deterministic
process-terminating SIGSEGV on any consumer that opens a crafted HEIF/HEIC/AVIF
with a grid item, before any codec decode runs — image viewers, thumbnailers,
server-side pipelines (ImageMagick/libvips), Android apps, browsers with a libheif
backend. The faulting address is 0x0 + a small vtable offset, so it is a clean
crash, not a controlled write. Disclosed alongside [[LIBHEIF-0002]] on 2026-05-03;
fixed in v1.22.0.