libheif 1.18.0 to 1.23.2 heap overflow in the unci decoder (CVE-2026-120208); sharp 0.34.3 and later never compiled that decoder
TL;DR
libheif's advisory GHSA-x8r2-mggj-j6wr (2026-09-01, vendor-rated Critical 9.8) is a heap buffer overflow in the unci uncompressed-image decoder: a HEIF file that declares unequal bit depths for its Cb and Cr planes makes the mixed-interleave path write past the smaller plane; fixed in 1.23.3 the same day, and VulnCheck issued CVE-2026-120208 for it on 2026-10-31 at 7.7. The decoder only exists when libheif is built with WITH_UNCOMPRESSED_CODEC=ON, which libheif leaves off by default and which sharp-libvips' build script does not set in any of the tags read (v1.2.0 to v1.3.3, under sharp 0.34.3 to 0.35.4), so next/image, Astro and other sharp users on 0.34.3 or later were not exposed to this one even on sharp 0.35.4; sharp 0.34.2 and older were not checked; anyone who builds libheif themselves with the uncompressed codec, or runs a distribution package that does, needs 1.23.3 or later.
What happened
- 2026-09-01. libheif maintainer farindk publishes GHSA-x8r2-mggj-j6wr, "Heap buffer overflow in
uncimixed-interleave decoding with unequal chroma bit depths", Critical, CVSS 3.1 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H), CWE-787, affected >= 1.18.0 and <= 1.23.2, patched 1.23.3, credits alextwordfence and poppo25-Toss. The mixed-interleave YCbCr path "permits paired Cb and Cr components to have different bit depths"; each chroma plane is allocated by its own depth but both are written using the first chroma entry's byte width, so the smaller plane is written past its end. The reporter reproduced it with AddressSanitizer on the 1.23.2 release. The advisory states the bug is internal to libheif and requiresWITH_UNCOMPRESSED_CODEC=ON; it says file disclosure and code execution were demonstrated on one specific WordPress deployment and that this "does not show universal RCE". The page prints "No known CVE" as read on 2026-11-01 and names a related advisory, GHSA-2jg2-4ch7-h545, which it says is distinct. - 2026-09-01. Release v1.23.3, "security release", ABI- and API-compatible with 1.23.2; the notes rate one fix critical, list several heap overflows and out-of-bounds reads, and describe tighter checks in the uncompressed decoder and in pixel-plane allocation.
- 2026-10-31, 17:25 UTC. VulnCheck publishes CVE-2026-120208, "libheif 1.18.0 before 1.23.3 Heap Buffer Overflow via unci Mixed-Interleave Decoder", CVSS 4.0 7.7 (
AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N) and CVSS 3.1 7.5 (AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H), affectedstrukturag/libheif1.18.0 before 1.23.3, references the vendor advisory, fix commit 9d3cd91d, the v1.23.3 release and the source line inunc_decoder_mixed_interleave.ccat v1.23.2.
Two scores, two readings. The vendor's 9.8 is the worst case for a process that decodes attacker files with the codec compiled in; VulnCheck's 7.7 adds attack prerequisites and user interaction. Neither is a measurement of a given deployment, and the vendor's own page limits the demonstrated code execution to one deployment. This file's severity is high: real memory corruption in a parser that takes attacker bytes, gated by a non-default build flag.
What sharp and Next.js users actually run. sharp ships libheif inside its @img/sharp-libvips-* packages, and that project records the libheif version in versions.properties per release: sharp-libvips 1.2.0 and 1.2.1 carry libheif 1.20.1, 1.2.2 and 1.2.3 carry 1.20.2, 1.3.0 and 1.3.1 carry 1.23.0, 1.3.2 carries 1.23.1, 1.3.3 (npm 2026-08-26) carries 1.23.2, and 1.3.4 (npm 2026-09-27) carries 1.23.5. The sharp pins: 0.34.3 (2025-07-10) → 1.2.0, 0.34.4 → 1.2.3, 0.35.0 and 0.35.1 → 1.3.0, 0.35.2 → 1.3.1, 0.35.3 → 1.3.2, 0.35.4 (2026-08-26) → 1.3.3, 0.35.5 (2026-09-27) → 1.3.4. So every sharp release from 0.34.3 to 0.35.4 bundled a libheif version inside the affected range; sharp 0.34.0 to 0.34.2 pin sharp-libvips 1.1.0, whose tags carry no versions.properties, so their libheif version was not read this run. The build script, build/posix.sh, at sharp-libvips v1.2.0, v1.2.3 and v1.3.3 alike configures libheif with -DBUILD_SHARED_LIBS=FALSE -DBUILD_DOCUMENTATION=0 -DBUILD_TESTING=0 -DENABLE_PLUGIN_LOADING=0 -DWITH_EXAMPLES=0 -DWITH_LIBDE265=0 -DWITH_X265=0 and does not set WITH_UNCOMPRESSED_CODEC; libheif's CMakeLists.txt at v1.23.2 declares that option "(experimental)" with default OFF. The vulnerable decoder is therefore not compiled into sharp's libheif, and this bug does not reach next/image, Astro's image service or any other sharp consumer through the bundled binaries. Separately, Next.js 15.5.24 and 16.3.3 (2026-08-25) disabled AVIF optimization after the earlier libheif advisory GHSA-g89c-p67h-r497, filed in Next.js July and August 2026 releases, and next@16.4.3 pins sharp at ^0.35.5. Upgrading sharp to 0.35.5 still moves you to libheif 1.23.5 and is the right move for the other fixes in 1.23.3.
Why it is here: libheif is the HEIF and AVIF decoder under sharp, under next/image, and under the PHP and WordPress image stacks, and the last libheif critical did reach Next.js. This one does not reach the npm path, and a reader should be able to see why rather than take an aggregator's "libheif critical" at face value.
Am I affected?
- sharp, Next.js, Astro and other npm consumers: not through the bundled libheif, for the build-flag reason above. Still check the version and move to the current line:
npm ls sharp @img/sharp-libvips-linux-x64 @img/sharp-libvips-darwin-arm64 2>/dev/null
@img/sharp-libvips-* 1.3.4 means libheif 1.23.5; 1.2.0 to 1.3.3 means an affected libheif version with the vulnerable decoder not compiled; 1.1.x and older were not checked here.
- Python wheels that bundle libheif (pillow-heif and similar): not covered by this file. Whether those wheels enable the uncompressed codec was not read this run; check the wheel's bundled libheif version and build options before concluding either way.
- Self-built or distribution libheif (PHP imagick and gd through ImageMagick or libvips, WordPress hosts, GStreamer and desktop image viewers): check the installed version and whether it was built with the uncompressed codec:
heif-info --version 2>/dev/null; pkg-config --modversion libheif 2>/dev/null
A version from 1.18.0 to 1.23.2 is affected if the build enabled WITH_UNCOMPRESSED_CODEC; the advisory's one demonstrated code-execution case was a WordPress deployment. If you cannot tell how the library was built, treat it as affected and upgrade.
- Scanners keyed on CVE ids will start flagging libheif < 1.23.3 from 2026-10-31, two months after the fix; a flag on a sharp-bundled copy is a true version match and a false exposure for this id.
If you are affected
- Upgrade libheif to 1.23.3 or later (1.23.5 is what sharp 0.35.5 bundles), or rebuild without
WITH_UNCOMPRESSED_CODECif you do not need uncompressed HEIF. - If a service decoded untrusted HEIF with an affected build, follow if your web app was compromised and review the host, since the vendor describes code execution as demonstrated.
Prevention
- Keep image parsing off the host that holds secrets: run
next/image, thumbnailers and upload processors on the Edge runtime or in an isolated service. See supply-chain attack surface. - Do not pin
sharpbelow the current line; the bundled libheif moves only when sharp does.
Sources
- libheif — GHSA-x8r2-mggj-j6wr: Heap buffer overflow in unci mixed-interleave decoding with unequal chroma bit depths — fetched 2026-11-01: published 2026-09-01 by farindk, Critical 9.8 with vector, CWE-787, affected >= 1.18.0 <= 1.23.2, patched 1.23.3, the mechanism summary, the
WITH_UNCOMPRESSED_CODEC=ONcondition, the single-deployment RCE statement, credits, "No known CVE", the related-advisory note. - libheif — release v1.23.3 — fetched 2026-11-01: "security release", 01 Sep, ABI and API compatible with 1.23.2, one fix rated critical, hardening in the uncompressed decoder and plane allocation.
- CVE Services — CVE-2026-120208 — queried 2026-11-01: VulnCheck CNA, published 2026-10-31 17:25 UTC, title, description, affected range, both CVSS metrics, references including commit 9d3cd91d and the v1.23.2 source line.
- sharp-libvips — versions.properties at v1.2.0, v1.2.1, v1.2.2, v1.2.3, v1.3.0, v1.3.1, v1.3.2, v1.3.3 and v1.3.4 — fetched 2026-11-01:
VERSION_HEIF1.20.1, 1.20.1, 1.20.2, 1.20.2, 1.23.0, 1.23.0, 1.23.1, 1.23.2 and 1.23.5 respectively; the v1.1.0 and v1.1.1 tags return no such file. - sharp-libvips — build/posix.sh at v1.3.3, and the same file at v1.2.0 and v1.2.3 — fetched 2026-11-01: the libheif cmake invocation quoted above in each, with no
WITH_UNCOMPRESSED_CODECsetting. - libheif — CMakeLists.txt at v1.23.2 — fetched 2026-11-01:
option(WITH_UNCOMPRESSED_CODEC ... (experimental) OFF). - npm registry, queried 2026-11-01:
@img/sharp-libvips-linux-x64time(1.3.3 2026-08-26, 1.3.4 2026-09-27);sharptime(0.35.4 2026-08-26, 0.35.5 2026-09-27);sharp@0.34.0throughsharp@0.35.5optionalDependencies.@img/sharp-libvips-linux-x64(1.1.0, 1.1.0, 1.1.0, 1.2.0, 1.2.3, 1.3.0, 1.3.0, 1.3.1, 1.3.2, 1.3.3, 1.3.4) andsharptimefor 0.34.3 (2025-07-10) and 0.34.4 (2025-09-17);next@16.4.3optionalDependencies.sharp^0.35.5.