v1.23.7 is a security and bugfix release. It is ABI- and API-compatible with v1.23.6 and is a drop-in replacement.
Fourteen of the fixed issues are rated high, so all users are advised to upgrade.
Security fixes
(CVE numbers will be added when assigned.)
- CVE-2026-XXXXX (GHSA-xwcv-cp39-m2w4) Out-of-bounds read in the OpenH264 decoder plugin. An AVC SPS that libheif's coded-size check could not parse was skipped and still passed to OpenH264, which accepts cropping offsets whose sum wraps around and returns plane pointers far outside of its frame buffer. An SPS that cannot be parsed is now an error, also in subset SPS NAL units. The VVC SPS check had the same pattern and fails closed as well. Introduced with the coded-size check of v1.23.5. (high)
- CVE-2026-XXXXX (GHSA-jx54-6rm6-2cgj) The AV1 coded-size check of v1.23.5 could be bypassed with libaom through an OBU without a size field. libheif stopped scanning at that OBU, while libaom continued and decoded a later, larger sequence header and frame. The last OBU of a sample is now given an explicit size before the data is scanned and decoded, so that both see the same framing. (high)
- CVE-2026-XXXXX (GHSA-fpr5-xpfr-vcq3) The AV1 coded-size check of v1.23.5 could be bypassed with libaom.
num_ticks_per_picture_minus_1was read with a UVLC reader that stops after 20 leading zero bits, although AV1 allows 31, and the error was ignored. The sequence header was then skipped, or scanned from the middle of the code where it could yield a small decoy size, and libaom allocated the real, much larger frame. The AV1 UVLC reader now follows the AV1 specification, and a sequence header that cannot be scanned completely is rejected. (high) - CVE-2026-XXXXX (GHSA-35mc-3887-rr37) Heap use-after-free when grid tiles are decoded in parallel. Several derived tiles (
iovl,idenorgrid) can share one coded input image. Their bit-depth queries read the decoder data of that input while another tile thread replaced it during decoding. A crafted file could crash the application or corrupt the heap with default options. The bit-depth queries now lock the decode mutex of the queried item. Only v1.23.6 is affected. (high) - CVE-2026-XXXXX (GHSA-hpg4-g2gc-pq42) Heap buffer overflow when padding an image to a size that is larger in one dimension and smaller in the other.
heif_image_extend_padding_to_size()could be reached with such a size by re-encoding a sequence whose frames have different sizes with the x265 or x264 sequence encoder. Padding now refuses a size below the image or any of its planes, checks all planes before changing them, and the sequence encoders refuse a frame whose size differs from the first frame. (high) - CVE-2026-XXXXX (GHSA-9rpf-2x22-78m9)
heif_context_encode_thumbnail()returned success without setting the output handle when the image already fits into the bounding box. Callers that released the handle, among themheif-enc -tand the C++ wrapper, freed an uninitialized or stale pointer (use-after-free, double free). The handle is now set to NULL in that case, as the API documentation says. (high) - CVE-2026-XXXXX (GHSA-48p4-gj45-w9v6) CPU exhaustion through derived images that decode a large image again and again and use only a few pixels of it, for example a grid of 1x1 tiles that are each a
clapcrop of an 8192x8192 overlay. A 2.6 kB file took 26 seconds to decode. Each decode now has a pixel budget of twice the size of the distinct images it uses (at least 2048x2048, at mostmax_image_size_pixels), single-tile decodes get their share of it, an exceeded security limit in a grid tile is an error also without strict decoding, and grid tiles outside of the image are not decoded. (high) - CVE-2026-XXXXX (GHSA-2g6j-vhr6-6wrj) CPU exhaustion through many coded pictures in one image item or sequence sample. The decoder decoded all of them, although only one image is returned: a 16 kB file with 1000 hidden HEVC pictures of 4096x4096 took 14 seconds. More than eight coded pictures per decode are now refused. HEVC, AVC and VVC count picture starts, AV1 counts frame OBUs, and AVC data with embedded start codes is refused. (high)
- CVE-2026-XXXXX (GHSA-7qm8-hj35-269r) CPU exhaustion through repeated transformations. An item could carry many associations of the same
irot,imirorclapproperty, andidenchains multiplied them: a 28 kB file took 73 seconds to decode. An item may now have at most one association of each of these properties, as HEIF requires, and at most 32 transformations are applied along a decode path. (high) - CVE-2026-XXXXX (GHSA-hhg3-fxg7-3gxw) CPU exhaustion while opening a file whose images have many
colrassociations. Reading the file regenerated and compared a colour profile property for every association. Reading and encoding now store colour profiles separately, so that opening a file does not create properties. (high) - CVE-2026-XXXXX (GHSA-73j7-gvm9-x8q6) CPU exhaustion in sequence decoding through samples that produce no output, for example when an edit list repeats a sample that the decoder does not display. One call of
heif_track_decode_next_image()could run for minutes, also withignore_sequence_editlist. The number of samples that are decoded without returning frames is now limited per codec, andignore_sequence_editliststops after the original samples. (high) - CVE-2026-XXXXX (GHSA-74mj-m37q-h3q9) Quadratic CPU time when opening a file with a
moovbox. Thestscentry of every chunk was looked up by a linear scan, so a file with many chunks andstscentries took seconds to minutes to open, also when the application only reads the still image. The lookup is now a binary search, and anstsctable whosefirst_chunkvalues decrease is refused. (high) - CVE-2026-XXXXX (GHSA-rcqc-842v-m26h) Deadlock when decoding a grid image with parallel tile decoding and non-strict decoding. A tile that was skipped because it is not valid left an empty entry in the job queue, which was dispatched as a decode of item ID 0 and could deadlock with the other tile threads. Skipped tiles no longer create decode jobs. (high)
- CVE-2026-XXXXX (GHSA-pfcm-ffqg-84hx) CPU exhaustion through cyclic Exif IFD pointers in the
heifiohelper ofheif-dec,heif-thumbnailerandheif-enc. The tag search followed IFD pointers in loops. The traversal is now bounded by the size of the Exif data. Applications that use only the library, and the gdk-pixbuf loader, are not affected. (high) - CVE-2026-XXXXX (GHSA-gff5-3vgr-p8h2) The gdk-pixbuf loader ignored the error of
heif_image_scale_image()and used an uninitialized image pointer, which gdk-pixbuf later released. A small valid file made the scaling fail when an application loaded it with a fixed width or height and kept the aspect ratio (a 1x65536 AVIF loaded at a width of 3840 requests a 2.9 TB image), which crashed the application. The loader now checks the result and refuses output sizes abovemax_image_size_pixels. (medium) - (GHSA-r9cg-w542-g7hj)
heif-dec -Sandheif-infoprinted and released an uninitialized URI pointer when aurimsample entry has nouribox. Depending on the build, they crashed, or read and freed the URI of a previous track a second time. The library is not affected. (low)
All of these issues were found by Anthropic's OSS Scanner. Thanks also to @iPlayForSG and @SounLabs, who reported GHSA-74mj-m37q-h3q9 independently.
Hardening
heif_image_scale_image()andheif_track_get_urim_sample_entry_uri_of_first_cluster()set their output pointer to NULL on error, so that a caller that misses the error check does not use an uninitialized pointer- A JPEG stream whose SOF marker cannot be parsed is refused instead of being passed to the decoder without a size check
- The libde265 plugin refuses NAL units of 2 GB or more. libde265 takes the size as
int, and versions 1.1.0 to 1.1.2 did not reject the negative value - Reading sample auxiliary information with a constant size from
saizis bounded by the sample count of thesaizbox, as it already was for variable sizes - The chroma bit depth query of a grid image whose first tile is missing returns -1 instead of dereferencing a null pointer
- heif-enc: the WebVMT parser was rewritten without
std::regex, whose recursion overflowed the stack for long sync data (about 30,000 characters). Timestamps are checked as the WebVMT specification defines them (minutes and seconds up to 59, three fraction digits, hours that fit the 32-bit millisecond counter), invalid hex and base64 data and payloads that are not JSON are reported, and a missing file or a file without WEBVMT signature is an error (#1941) - heif-enc: the PNG loader read past the end of the row for 16-bit grayscale images with alpha
Bug fixes
- Image sequences:
- The colour metadata of the sample entry (
colr,clli,mdcv,amve,ndwt,pasp) is attached to the decoded frames, and an NCLX profile in the sample entry takes precedence over the bitstream, with the same full-range correction and mismatch warnings as for still images (#1938, @dlemstra) - A decoder is drained before the decoding switches to a chunk with another sample description, and at the end of the input the last active decoder is flushed. Frames at the end of a chunk were lost before (#1939, @dlemstra)
- Repeated playback through an edit list works with all decoders, and the FFmpeg plugin recognizes the end of the stream
- The
data_reference_indexof theurimsample entry in URI metadata tracks was written uninitialized; it is now 1 - The
stsctable that libheif writes for a track with several chunks numbers its entries instead of letting all of them start at chunk 1
- The colour metadata of the sample entry (
- libde265: images with NAL units larger than 16 MiB could not be decoded, not even with the security limits disabled. The NAL size limit is now taken from
max_memory_block_size, and an error of libde265 for a NAL unit is reported (#1936) - SVT-AV1: odd image sizes are no longer padded with SVT-AV1 3.0.0 and later, so that no
clapcrop is written (Firefox does not display AVIF images withclap) (#1945). 10-bit images with alpha could not be encoded - heif-enc: the transparency of RGB and grayscale PNG images with a
tRNSchunk was dropped (#1947), and 16-bit grayscale PNG images with alpha were encoded wrong - heif-enc: the WebVMT parser started cues at the wrong time and dropped cues in files with CRLF line endings and sync commands that span two lines
- C++ API:
ImageHandlegets functions for the nominal diffuse white luminance (@therahedwig) - Rotations by 90 and 270 degrees are three to five times faster: the planes are rotated in cache-sized blocks, and plane strides that are a multiple of 512 bytes get one cache line of padding (#1934, @m6w6)
Behavior changes
- Decoding refuses with
heif_suberror_Security_limit_exceeded:- a decode that would produce more than twice the pixels of the distinct images it uses (at least 2048x2048 are always allowed)
- more than eight coded pictures in one image item or sequence sample
- more than 32 transformations along a decode path
- a sequence decoder that consumes too many samples without returning frames (32 for video codecs, 64 for VVC, 1 for still-image codecs)
- An item with more than one association of
irot,imirorclapis refused - An exceeded security limit in a grid tile is an error also without strict decoding. The tile was left blank before
- An AVC or VVC SPS, an AV1 sequence header or a JPEG SOF marker that libheif cannot parse is an error
- An
stsctable with decreasingfirst_chunkvalues is refused heif_image_extend_padding_to_size()returns an error when a requested dimension is smaller than the image- The x265 and x264 sequence encoders refuse a frame whose size differs from the first frame
heif_context_encode_thumbnail()returns a NULL handle when no thumbnail is needed- Metadata items (Exif, XMP, other
mimeitems) are decompressed when they are first read instead of while the file is opened. A file with a compressed metadata item that cannot be decompressed now opens: the item is listed,heif_image_handle_get_metadata()returns the error andheif_image_handle_get_metadata_size()returns 0. Before, a corrupt Exif ormimeitem made the whole file fail to open, and an item with an unsupported content encoding was left out of the list - The gdk-pixbuf loader refuses output sizes above
max_image_size_pixels - The vvdec plugin parses at most 8 pictures ahead