Coordinated disclosureCVE-2026-84451GHSA-hh47-fhqr-cj2rlibheif · C/C++6.5 Moderate

Proof/CVEs & advisories/CVE-2026-84451

CVE.08 — Incomplete fix

One branch was fixed. Its sibling was not.

An earlier libheif advisory replaced an overflow-prone range check with an overflow-safe one. The same check existed twice. Only one copy was rewritten, and the surviving addition-form check still wraps a 64-bit range to zero.

CVECVE-2026-84451
AdvisoryGHSA-hh47-fhqr-cj2r
Projectlibheif
EcosystemC / C++
Affected1.19.0 – 1.23.2
Fixed1.23.3
WeaknessCWE-125
Severity6.5 Moderate
Creditthe-vibe-dev (shared)
A bounds check written as offset + size > limit is not a bounds check when offset + size can wrap. Fixing one instance of that pattern does not fix the others.
Weakness
CWE-125
Trigger
advertised 4096 × 4096 tile grid
Control
the already-patched sibling branch
Outcome
out-of-bounds read · crash
Availability only.The published advisory characterises the impact as denial of service through a crash. It does not establish code execution or data disclosure, and neither is claimed here.

libheif decodes uncompressed (unci) image items. When an item carries no icef (item-compressed-frame) table, the decoder computes the byte range of each tile arithmetically from the advertised grid geometry rather than reading it from a table.

This case is a follow-up to an earlier advisory, GHSA-73p7-m7gg-w2jv. That fix corrected the range check on one code path. The equivalent check on the sibling no-icef path was left in its original addition form.

A range check must remain valid across the whole domain of its inputs. If the inputs are attacker-advertised 64-bit quantities, the check must not itself be able to overflow.

The geometry that drives the computation — tile width, tile height, and the number of tiles in each direction — comes from the file. Nothing constrains those values to a size that keeps the arithmetic in range before the check runs.

The two forms differ only in where the addition happens, and that difference is the entire vulnerability:

Addition form — wrapsthe surviving branch
if (offset + size > available)
    return error;      /* offset + size can wrap to a small value */
Subtraction form — cannot wrapthe branch fixed earlier
if (offset > available || size > available - offset)
    return error;      /* both comparisons stay in range */

The subtraction form was introduced by the earlier fix. The addition form survived on the sibling path, so the same class of input that the earlier advisory closed remained reachable through a different entry point.

The published advisory states the triggering geometry directly: a crafted HEIF file advertising a 4096 × 4096 uncompressed tile grid. For the final advertised tile, the computed end of the range wraps from 2^64 to zero.

What the check seesfinal advertised tile
advertised grid   : 4096 x 4096 tiles
computed range end: 2^64  ->  0   (wraps)
check result      : 0 > available  is false  ->  accepted

Because the wrapped value compares as small, the range is accepted as valid. The decoder then proceeds to copy the tile.

01 · Input

Crafted HEIF

An uncompressed item with no icef table and a 4096 × 4096 advertised tile grid.

02 · Route

No-icef path

Tile ranges are computed from geometry rather than read from a table.

03 · Wrap

Addition-form check

The final tile’s range end wraps past the 64-bit boundary to zero.

04 · Read

Out-of-bounds copy

The copy runs with an invalid source pointer and an absurd length.

Once the wrapped range passes validation, the advisory records that memcpy() is reached with an invalid source pointer and a length on the order of 1 TiB. The process faults almost immediately; the length matters only in that nothing bounded it.

CANDIDATE unci item, no icef table, 4096x4096 advertised tiles EXPECTED range rejected: "Data range out of existing range" OBSERVED range accepted after 2^64 wrap to zero 1.23.2 decode tile via public API: SIGSEGV (READ in memcpy) 1.23.3 decode tile via public API: STRUCTURED ERROR MARKER=LIBHEIF_UNCI_NOICEF_RANGE

The fault surfaces through the ordinary tile-decoding entry point in the public API, so any application that decodes untrusted HEIF tiles reaches it without doing anything unusual.

The root cause is not the arithmetic. It is that a security fix was applied to one instance of a duplicated check.

An incomplete fix leaves a vulnerability that is harder to find than the original, because the advisory for the original suggests the pattern has already been dealt with.

The useful control here was the already-patched branch: it shows precisely what the maintainer intended the check to be, which makes the surviving branch’s divergence unambiguous rather than a matter of interpretation.

The SecHive research loop

Read the fix, then look for its siblings.

A published advisory is a map of where a maintainer already knows the danger is. The highest-value follow-up question is rarely “is this fix correct?” — it is “how many other places did this exact pattern live?”

SK.01

Fix diffing

Read the prior advisory’s patch and extract the corrected check as a pattern rather than a location.

SK.02

Sibling search

Find every remaining site in the codebase still using the pre-fix form of that check.

SK.03

Reachability

Establish that the surviving site is reachable from the public API with a file an attacker controls.

SK.04

Skeptic gate

Hold the claim at out-of-bounds read and crash; decline to assert exploitability without evidence.

HUMAN

Promotion and disclosure

Report as an incomplete fix referencing the original advisory, and confirm the patched release.

libheif 1.23.3 brings the surviving branch into line with its sibling, so both range checks use the non-wrapping subtraction form. The crafted file then returns the structured Data range out of existing range error instead of faulting.

Moderate
Published advisory severity
6.5
CVSS

libheif is a decoding library, so reachability is set by the integrating application. Image-upload pipelines, thumbnailers, desktop viewers, and mail and messaging clients that render HEIF attachments all expose the tile-decoding path to files they did not author.

ScopeValueBasis
Affected1.19.0 through 1.23.2range as stated in the published advisory
First fixed1.23.3advisory-confirmed patched release
Prior advisoryGHSA-73p7-m7gg-w2jvthe incomplete fix this case follows up

Upgrade to libheif 1.23.3 or later.

For codec maintainers, the broader lesson is about fix propagation rather than this specific expression. When a range check is corrected, the patch should be treated as a pattern to sweep for, not a line to change. Regression coverage should include:

  1. geometries whose computed range wraps the 64-bit boundary;
  2. items with and without an icef table, exercising both branches;
  3. an assertion that both branches reject the same crafted input identically;
  4. neighbouring valid geometries that must continue to decode.
2026-09-01
GHSA-hh47-fhqr-cj2r published with CVE-2026-84451, affected range, severity and reporter credit.
2026-09-05
Public advisory re-checked; write-up prepared from published material only.
  1. GHSA-hh47-fhqr-cj2r — libheif advisory
  2. CVE-2026-84451 record
  3. GHSA-73p7-m7gg-w2jv — the earlier, incomplete fix

Credit: The published advisory credits three reporters — the-vibe-dev, hillalee and sonicnew. That shared attribution is preserved here rather than presented as sole credit. Research was SecHive-assisted through source mapping, fix-pattern extraction and evidence organization; final validation, disclosure coordination and publication review remained human.