PUBLIC MIRROR A read-only public view of Anvil. Only publicly-disclosed findings are shown; the Playbook, techniques, sessions and embargoed research are hidden.

← Findings

GROK-0012

low confirmed

HTJ2K SIMD decoder: MagSgn frwd_read 16-byte vector load over-reads the 8-byte-padded code-block buffer

Static only Source / code-trace analysis only. Nothing has been executed.

⚠ Harness reproduced — not real-world verified. Reproduce through the public API, a real application, or a platform decoder before treating this as verified.

Crash heap-buffer-overflow (read, <=6 bytes)
Topmost entry point unknown — establish reachability
Verified through no real consumer named
Real-world impact 4 CVSS · low

Classification

TargetGrok
ComponentTier-1 / MQ / HT block coding
Locationsrc/lib/core/t1/part15/coding/ojph_block_decoder_avx2.cpp · frwd_read (avx2 line 620 / ssse3 line 617):avx2:620, ssse3:617
Vuln classoob-read
CVE
CVSS4 (CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:L)
Discovered2026-06-16

Verification

Evidence Static only (not real-world verified)
Harness fired❌ no
Protocol1.0
Sanitizernone
Crash typeheap-buffer-overflow (read, <=6 bytes)

Disclosure

Reported toGrok maintainers — support@grokcompression.com (private email; listed as a source-confirmed lead, not yet runtime-verified)
Reported2026-06-23
Vendor ack
Embargo until
Public2026-06-24
Patched in

Writeup

Summary

The SIMD HTJ2K block decoders (the default x86-64 dispatch: AVX2, else SSSE3) read the compressed MagSgn data with a 16-byte vector load _mm_loadu_si128((__m128i*)msp->data) (ojph_block_decoder_avx2.cpp:620, ojph_block_decoder_ssse3.cpp:617) before any size guard. Grok allocates the compressed code-block buffer with only 8 bytes of trailing pad (grk_cblk_dec_compressed_data_pad_ht = 8, CoderOJPH.cpp:215-225), but the SIMD reader’s own comment documents it “can go beyond the end of buffer by up to 16 bytes.” When the MagSgn segment is exhausted, msp->data freezes at base + 8 + (lcup - scup) and the 16-byte load reaches base + L + 24 - scup; the buffer ends at base + 16 + L, so the over-read is 8 - scup bytes — 1–6 bytes for the attacker-controlled scup ∈ {2..7} (validated only as scup >= 2).

The scalar decoder (ojph_block_decoder32.cpp) is NOT affected — it dereferences *(ui32*) only while size>3 and stops at base+8+(lcup-scup), in-bounds with the 8-byte pad. This is a SIMD-only regression caused by the pad (8) being smaller than the SIMD look-ahead (16).

Reproduction

Decode an HTJ2K (Part-15) J2K whose cleanup-pass scup (read from coded_data[lcup-1..lcup-2]) is < 8 and whose MagSgn segment is small/exhausted, on a default x86-64 build (AVX2/SSSE3). The over-read materializes in frwd_read. (Triggering a specific scup<8 is data-dependent; an HT fuzz campaign or a hand-tuned code-block is the reliable route — see nextSteps.)

Root cause

grk_cblk_dec_compressed_data_pad_ht = 8 < the 16-byte trailing slack the SIMD frwd_read assumes.

Verification evidence

Confirmed by source: over-read = 8 - scup bytes (max 6 at scup=2); present on the default AVX2 + SSSE3 dispatch; absent from the verified-safe scalar path. The decoder’s own comment documents the 16-byte look-ahead vs the 8-byte pad.

Runtime status (2026-06-16): not yet ASAN-reproduced. Triggering needs an HTJ2K code-block whose cleanup-pass scup decodes to < 8 and whose MagSgn segment is exhausted — a data-dependent property of the compressed block that a normal grk_compress-produced HT codestream does not generally satisfy. Producing it requires a hand-tuned HT code-block or (more practically) a coverage-guided HT fuzz campaign — the recommended route. Remains confirmed by source pending that.

Impact

Heap out-of-bounds read of up to ~6 bytes in the HTJ2K MagSgn decoder on the default x86-64 SIMD path, reachable from a crafted HT code-block. The over-read bytes are masked into the bit accumulator and influence decoded output (minor info-leak flavor) and can fault on a hardened allocator / page boundary. No OOB write; small bounded extent. Severity low (CVSS 4.0). Fix: raise grk_cblk_dec_compressed_data_pad_ht to 16 (and the matching alloc/zero in CoderOJPH.cpp) to match the SIMD reader’s documented look-ahead. NOTE: if lengths2 != 0 is ever passed (currently always 0, so SPP/MRP are dead), the same 16-byte load in the SPP sigprop reader over-reads a full 8 bytes — fix the pad regardless.

References