PUBLIC MIRROR A read-only public view of Anvil. Only publicly-disclosed findings are shown; the Playbook, techniques, sessions and embargoed research are hidden.

← Findings

LIBHEIF-0002

high patched

Grid uint32 underflow → heap out-of-bounds read in decode_grid_tile

✓ Disclosure ready Real-world verified and packaged for disclosure (or already carries a CVE / advisory).
Crash SEGV (heap OOB READ) — unchecked std::vector::operator[] in decode_grid_tile at grid.cc:588 (NDEBUG)
Topmost entry point unknown — establish reachability
Verified through no real consumer named
Real-world impact 7.1 CVSS · high · CVE-2026-48029

Classification

Targetlibheif
ComponentGrid image item
Locationlibheif/image-items/grid.cc · ImageItem_Grid::decode_grid_tile:586-588
Vuln classoob-read
CVECVE-2026-48029
CVSS7.1 (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:H)
Discovered2026-05-03

Verification

Evidence ✓ Disclosure ready (real-world verified)
Harness fired✅ yes
Protocol1.0
Sanitizerasan
Crash typeSEGV (heap OOB READ) — unchecked std::vector::operator[] in decode_grid_tile at grid.cc:588 (NDEBUG)
ReproRelease-NDEBUG ASAN+UBSAN build of 1.21.2, decode_grid_overlay_runner on F2-clusterfuzz-irot270-16389b.bin → 'AddressSanitizer: SEGV ... READ' at grid.cc:588 (debug build: assert at grid.cc:586). v1.22.0: rc=0, no signal.

Disclosure

Reported toGitHub private security advisory — strukturag/libheif
Reported2026-05-03
Vendor ack2026-05-18
Embargo until
Public2026-05-19
Patched in1.22.0

PoC: project/blogpost/findings/F2-grid-oob-read/F2-clusterfuzz-irot270-16389b.bin (16,389-byte HEIF, irot=270, in /home/ariel/Projects/libheif)

Writeup

Summary

ImageItem_Grid::decode_grid_tile computes a tile index idx = ty * cols + tx and guards it only with a debug assert before m_grid_tile_ids[idx]. Two cooperating defects make idx attacker-influenced: a uint32 underflow in the inverse-rotation tile arithmetic (CWE-191) feeds an unchecked std::vector::operator[] (CWE-125). In release/NDEBUG builds — the way distros ship libheif — the assert is compiled out and the access is a real heap out-of-bounds read of 4 bytes at an attacker-influenced offset.

Reproduction

16,389-byte HEIF with a grid item carrying an irot=270 rotation property, decoded via heif_image_handle_decode_image_tile (or the full-grid path):

==ERROR: AddressSanitizer: SEGV on unknown address 0x610400000e00 (READ)
    #0 ImageItem_Grid::decode_grid_tile   grid.cc:588:26
    #1 ImageItem_Grid::decode_compressed_image grid.cc:224
    ...

(Debug build instead hits assert(idx < m_grid_tile_ids.size()) at grid.cc:586.) The release-NDEBUG re-run via the build-flavor matrix is what proved this is memory-safety, not a robustness assert.

Root cause

Two sites (the sibling-class audit localized both):

  • Sourceimage_item.cc:604+ (transform_requested_tile_position_to_original_tile_position): for irot 90°/270° with rows ≠ columns, the inverse-rotation subtractions use pre-rotation tiling.num_columns/num_rows against post-rotation caller coordinates, underflowing to ~UINT32_MAX.
  • Sinkgrid.cc:586-588: the idx bounds check is a debug-only assert; release builds index the vector unchecked.

Fixed upstream in v1.22.0: commit 518bd95f replaces the assert with a runtime bounds check (sink); commit e523ec0b validates (tile_x,tile_y) against post-rotation dims before any subtraction (source). Sibling overlay.cc:265 already parse-time count-checks; grid did not.

Verification evidence

Verified per the verification protocol v1.0: deterministic ASAN SEGV (READ) at grid.cc:588 on a release-NDEBUG 1.21.2 build; debug build aborts on the grid.cc:586 assert; re-tested against a fresh v1.22.0 release+debug build → both return cleanly (heif_error_Invalid_input / heif_suberror_Missing_grid_images). Reachable with no codec back-end installed.

Impact

Per the impact rubric: high (CWE-191 → CWE-125). Heap OOB read with an attacker-influenced offset, reachable from public decode APIs (heif_image_handle_decode_image_tile, heif_image_handle_get_grid_image_tile_id, and the full-grid heif_decode_image path) on a crafted HEIF/HEIC/AVIF before any codec runs. Deterministic SIGSEGV when the offset overshoots into unmapped memory; where it lands in mapped heap, the 4 bytes read are used as a heif_item_id lookup key (not a trivially-leaking primitive). Disclosed via GitHub private security advisory (2026-05-03), fixed in v1.22.0 (2026-05-19), CVE-2026-48029.

References