## Ticket: CVE-2026-32738 — libheif integer underflow out-of-bounds read crash (DoS)

**Advisory**: GHSA-7f2h-cmpf-v9ww — https://github.com/strukturag/libheif/security/advisories/GHSA-7f2h-cmpf-v9ww
**CVE**: CVE-2026-32738 | **CWE-191** (Integer Underflow)
**Severity**: Medium — CVSS 3.1 base 6.5 (AV:N/AC:L/PR:N/UI:N)
**Package**: `libheif` (C++ library) | **Repository**: https://github.com/strukturag/libheif

### Impact

`libheif` parses HEIF/HEIC sequence files. The `Chunk` constructor computes the
last sample index as `m_last_sample = first_sample + samples_per_chunk - 1`. A
crafted file whose `stsc` (sample-to-chunk) box sets `samples_per_chunk = 0`
makes this evaluate to `0 + 0 - 1`, which **underflows** the unsigned 32-bit
counter to `UINT32_MAX`.

Subsequently accessing any sample indexes into an **empty `std::vector`** at
index `0`, dereferencing the null page — an out-of-bounds read that crashes the
process with `SIGSEGV`. Any application that decodes attacker-supplied HEIF
files is exposed to this **denial of service**.

### Affected / fixed versions

Affected: `libheif` `<= 1.21.2`.
Fixed: **`1.22.0`**.

Reproduce on a vulnerable build (**`1.21.2`**) and verify the fix on
**`1.22.0`**.

### Where to look

Build both versions from the repository:

```bash
git clone https://github.com/strukturag/libheif.git
cd libheif && git checkout v1.21.2   # vulnerable
# ... and v1.22.0 for the fixed build
```

The fix commit is **not pinned** — inspect the `1.21.2`→`1.22.0` history
(`git log v1.21.2..v1.22.0`) to locate the commit that hardens the `Chunk`
constructor / `stsc` parsing against a zero `samples_per_chunk` and the
resulting `m_last_sample` underflow.

### Reproduction approach

No service, database, or browser is needed — the crash is observable from a
libheif CLI tool.

1. Build the **vulnerable** `libheif 1.21.2` (CMake).
2. Craft a small HEIF sequence file whose `stsc` box specifies
   `samples_per_chunk = 0`.
3. Run a libheif tool such as `heif-info` or `heif-convert` on the crafted
   file.
4. Repeat with the **fixed** `libheif 1.22.0`.

Building with AddressSanitizer makes the out-of-bounds read explicit, but a
plain build already crashes on the vulnerable version.

### Expected result

| Build | `heif-info`/`heif-convert` on crafted file | Outcome |
|-------|--------------------------------------------|---------|
| `libheif 1.21.2` (vulnerable) | `m_last_sample` underflows to `UINT32_MAX`; sample access reads index 0 of an empty vector | **SIGSEGV** (signal 11), non-zero exit |
| `libheif 1.22.0` (fixed) | zero `samples_per_chunk` handled | controlled decode error or exit `0`, no crash |

- **Vulnerable indicator**: the tool crashes with `SIGSEGV` (signal 11) and a
  non-zero exit code on `1.21.2`.
- **Fixed indicator**: the tool handles the file cleanly on `1.22.0` — no
  crash.

### Expected artifacts

- `reproduction_steps.sh` — builds both libheif versions, generates the
  crafted HEIF file, and runs the tool against each build.
- `validation_verdict.json` — `verdict: confirmed`, with the vulnerable
  (crash) and fixed (clean) indicators captured.
- Logs capturing the tool output / exit codes for `1.21.2` and `1.22.0`.
