Security fixes from the Anvil audit (GHSA-57w8-h6pj-xx45)#411
Merged
Conversation
added 14 commits
June 24, 2026 11:59
Move the legal-header-size switch ahead of the read() that uses biSize, and reject lengths exceeding the temp buffer. Previously biSize<4 underflowed the read length and biSize>124 overran the 124-byte stack buffer, both attacker-controlled stack writes. Thanks to Ariel Koren (Anvil security research) for the audit that discovered this issue and informed the fix. Reported-by: Ariel Koren <ariel.koren@gmail.com> Advisory: GROK-0001 (CWE-787)
The RLE decoders walked the compressed buffer with *pixels_ptr++ with no upper bound, so a large declared height plus a small/zero biSizeImage and no end-of-bitmap marker ran the pointer off the allocation. Guard every read against pixels_end. Thanks to Ariel Koren (Anvil security research) for the audit that discovered this issue and informed the fix. Reported-by: Ariel Koren <ariel.koren@gmail.com> Advisory: GROK-0002 (CWE-125)
cmptparm[] and planes[] are hard-coded [3], but the fill loop and grk_image_new used cinfo.output_components, which is 4 for an Adobe CMYK/YCCK JPEG. The 4th component overran both stack arrays. Reject output_components > 3 right after the precision gate. Thanks to Ariel Koren (Anvil security research) for the audit that discovered this issue and informed the fix. Reported-by: Ariel Koren <ariel.koren@gmail.com> Advisory: GROK-0004 (CWE-787)
The per-row scratch buffer is sized from SamplesPerPixel, but the chunky de-interleave reads width*numcomps samples (numcomps derived from photometric + extrasamples). A TIFF declaring numcomps > tiSpp over-read the row buffer into the output. Reconcile the two for PLANARCONFIG_CONTIG before reading pixels. Thanks to Ariel Koren (Anvil security research) for the audit that discovered this issue and informed the fix. Reported-by: Ariel Koren <ariel.koren@gmail.com> Advisory: GROK-0005 (CWE-125)
Sample offset_ (from STCO) and samples_size_ (from STSZ) were used as raw offsets into the whole-file buffer with no bounds check, so a crafted STCO offset (e.g. 0xFFFFFFFF) read far past the input and could decode adjacent heap into the output. Bound offset+size (and the box-length probe) by the stream length in both readHeader and the per-sample decode path. Thanks to Ariel Koren (Anvil security research) for the audit that discovered this issue and informed the fix. Reported-by: Ariel Koren <ariel.koren@gmail.com> Advisory: GROK-0007 (CWE-125)
tts_decompact materialised one mj2_sample per declared sample with no cap on the per-entry samples_count_ (raw uint32), so a single STTS entry of 0xFFFFFFFF drove ~4.29B push_backs and wrapped num_samples_. Cap the running total at the file size (each sample needs >=1 byte of media) and fail the parse on overflow. Thanks to Ariel Koren (Anvil security research) for the audit that discovered this issue and informed the fix. Reported-by: Ariel Koren <ariel.koren@gmail.com> Advisory: GROK-0009 (CWE-770)
The box reader recurses into minf/dinf/dref even when no tkhd has been seen, so a dref->url/urn box with no preceding track header reached read_url/read_urn with current_track_ == nullptr and dereferenced it. Reject url/urn boxes outside a track. Thanks to Ariel Koren (Anvil security research) for the audit that discovered this issue and informed the fix. Reported-by: Ariel Koren <ariel.koren@gmail.com> Advisory: GROK-0013 (CWE-476)
read_url/read_urn unconditionally did headerSize -= 4 for the version+flags fullbox header. A box shorter than 4 bytes underflowed headerSize to a near-UINT32_MAX value, defeating the bounds checks on the following location reads and over-reading the heap. Reject boxes too short to hold the fullbox header. Thanks to Ariel Koren (Anvil security research) for the audit that discovered this issue and informed the fix. Reported-by: Ariel Koren <ariel.koren@gmail.com> Advisory: GROK-0003 (CWE-125)
Mct captured a non-owning Tile* once at construction. When the tile cache released a tile (release/releaseForSwath) it freed tile_ but left mct_->tile_ dangling, and reinitForReDecompress allocated a fresh tile without updating mct_. On re-decompress of an evicted MCT tile the inverse MCT pass then read/wrote the freed Tile. Re-point mct_ on recreate and null it on release. Thanks to Ariel Koren (Anvil security research) for the audit that discovered this issue and informed the fix. Reported-by: Ariel Koren <ariel.koren@gmail.com> Advisory: GROK-0006 (CWE-416)
The band-callback strip buffer was allocated for the first tile-row's reduced height, but the drain loop mutates comp->h to each subsequent row height without reallocating. With a YTOsiz not aligned to dy*2^reduce, an interior row is taller than the first and the composite copy wrote past the allocation. Size the buffer for the tallest reduced tile-row in the slated region. Thanks to Ariel Koren (Anvil security research) for the audit that discovered this issue and informed the fix. Reported-by: Ariel Koren <ariel.koren@gmail.com> Advisory: GROK-0010 (CWE-787)
read_asoc recursed once per nested asoc child with no depth limit, consuming only the 8-byte child header per level, so a few-MB header of nested asoc boxes exhausted the stack during header parse. Track depth and reject nesting beyond maxAsocDepth. Thanks to Ariel Koren (Anvil security research) for the audit that discovered this issue and informed the fix. Reported-by: Ariel Koren <ariel.koren@gmail.com> Advisory: GROK-0011 (CWE-674)
WaveletPoolData::alloc multiplied maxDim (the tile dimension) by a per-thread element size with no overflow check, and the caller ignored the result. A crafted SIZ tile dimension drove a multi-GB allocation (or a size_t wrap). Add the overflow guard the sibling dwt_scratch already has, and abort the inverse transform when the pool cannot be allocated instead of proceeding with null buffers. Thanks to Ariel Koren (Anvil security research) for the audit that discovered this issue and informed the fix. Reported-by: Ariel Koren <ariel.koren@gmail.com> Advisory: GROK-0014 (CWE-770)
The band-callback scratch image sized packed_row_bytes from the raw
(bit-packed) precision, but the PNM streaming packer always emits whole
8- or 16-bit samples. For a precision not in {8,16} this under-sized
the strip buffer and the packer wrote past it. Use the rounded byte
width for PXM output, matching GrkImage::initOutput.
Thanks to Ariel Koren (Anvil security research) for the audit that
discovered this issue and informed the fix.
Reported-by: Ariel Koren <ariel.koren@gmail.com>
Advisory: GROK-0008 (CWE-787)
The SIMD MagSgn forward reader does 16-byte vector loads that can read up to 16 bytes past the end of the code-block data, but the buffer was padded by only 8 bytes, so a small MagSgn segment (scup < 8) over-read the allocation. Pad by 16 bytes to cover the widest load. Thanks to Ariel Koren (Anvil security research) for the audit that discovered this issue and informed the fix. Reported-by: Ariel Koren <ariel.koren@gmail.com> Advisory: GROK-0012 (CWE-125)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the 14 issues Ariel Koren (Anvil) reported privately in GHSA-57w8-h6pj-xx45. One commit per finding; each commit body carries the GROK id, the CWE, and a
Reported-by:trailer so the mapping back to the advisory is self-contained.These touch the image-format readers (BMP/JPEG/TIFF), the MJ2 box + sample-table parser, the JP2 asoc parser, the tile-processor MCT lifecycle, the streaming strip composite, the inverse-DWT scratch pool, and the HTJ2K MagSgn SIMD path. None are in the core J2K/T1/T2/wavelet/MQ math.
Highs:
Mediums:
Lows:
Builds clean and the full test suite passes locally. Headed for 20.3.6.