Severity by source
AV:L/AC:H/PR:N/UI:N/S:C/C:L/I:L/A:H
Crafted image must be decoded (AV:L) on a 32-bit target with overflow-triggering dimensions (AC:H); no auth (PR:N); attacker-controlled heap OOB write plausibly yields code execution within the process (S:U, C/I/A:H).
Primary rating from Vendor (https://github.com/tirr-c/jxl-oxide).
CVSS VectorVendor: https://github.com/tirr-c/jxl-oxide
Lifecycle Timeline
2Blast Radius
ecosystem impact- 16 cargo packages depend on jxl-grid (8 direct, 8 indirect)
Ecosystem-wide dependent count for version 0.6.2.
DescriptionCVE.org
Summary
On 32-bit platforms, decoding a crafted image may lead to out-of-bounds writes due to integer overflow in length calculation.
Details & PoC
The test listed below fail under miri with command cargo +nightly miri test --release -p jxl-grid
Or you can use Address Sanitizer, which ignores Rust-specific UB like aliasing but still flags out-of-bounds accesses:
RUSTFLAGS=-Zsanitizer=address cargo +nightly test -Zbuild-std -p jxl-grid --release --target x86_64-unknown-linux-gnu
The following tests should be appended to crates/jxl-grid/src/test/subgrids.rs:
mod miri_ub {
use super::*;
// `AlignedGrid::with_alloc_tracker` computes `width * height` unchecked. In release, overflow
// can create a tiny backing buffer for huge logical dimensions.
#[test]
fn aligned_grid_dimension_product_overflows() {
let width = usize::MAX / 2 + 1;
let mut grid = AlignedGrid::<u8>::with_alloc_tracker(width, 2, None).unwrap();
let mut subgrid = grid.as_subgrid_mut();
*subgrid.get_mut(0, 1) = 1;
std::hint::black_box(grid);
}
}This issue can be reachable through decoding a crafted image in two ways:
- Huge actual frame
A frame such as 65536 x 65536 passes the current frame area limit (2^32 <= 2^40) but overflows usize element count on 32-bit. Rendering then allocates too-small AlignedGrids in modular/VarDCT/filter paths and later writes through mutable subgrids.
- Huge canvas plus tiny cropped frame
This is the more practical “small payload, huge logical output” case. A bitstream-controlled frame crop can be tiny, but if the canvas/default requested region is huge, composition can allocate an output grid sized to the canvas/ROI at crates/jxl-render/src/blend.rs. That is bitstream frame cropping, not API crop. With a 32-bit target and a full requested image region whose area overflows, this can happen through ordinary render_frame().
Impact
On 32-bit platforms this can cause out-of-bounds writes with attacker-controlled data when decoding a crafted JPEG XL image. This could allow arbitrary code execution.
AnalysisAI
Heap out-of-bounds writes in jxl-oxide's jxl-grid crate allow attacker-controlled memory corruption on 32-bit platforms when decoding a crafted JPEG XL image, potentially leading to arbitrary code execution. An integer overflow in AlignedGrid's width×height length calculation produces an undersized backing buffer for huge logical dimensions, after which rendering writes through mutable subgrids beyond the allocation. Publicly available exploit code exists in the advisory (miri/ASan PoC tests); no active exploitation is reported (not in CISA KEV) and no EPSS score was provided.
Technical ContextAI
jxl-oxide is a pure-Rust JPEG XL image decoder; the flaw lives in its jxl-grid crate (pkg:rust/jxl-grid), which provides the AlignedGrid buffer abstraction used across modular, VarDCT, filter, and blend/composition rendering paths. The root cause is CWE-122 (heap-based buffer overflow) driven by an unchecked integer overflow: AlignedGrid::with_alloc_tracker computes width * height without overflow checking, so on 32-bit targets (usize = 32 bits) a product exceeding 2^32 wraps to a small value, allocating a tiny heap buffer while the logical grid reports huge dimensions. Two decode paths reach this: a genuinely huge frame (e.g. 65536×65536, which passes the 2^40 frame-area limit but overflows 32-bit usize element count), and the more practical 'small payload, huge logical output' case where a tiny bitstream-cropped frame is composited onto a huge canvas/requested region in jxl-render/src/blend.rs, allocating the output grid to the overflowing ROI area during ordinary render_frame().
RemediationAI
Upgrade jxl-grid (and the parent jxl-oxide) to the patched release identified in the vendor advisory; no exact fix version is included in the provided data, so determine the fixed version from RUSTSEC-2026-0151 (https://rustsec.org/advisories/RUSTSEC-2026-0151.html) and GHSA-5pmv-rx8r-wmv5 (https://github.com/tirr-c/jxl-oxide/security/advisories/GHSA-5pmv-rx8r-wmv5), then run cargo update -p jxl-grid and rebuild. As the most effective compensating control until patched, avoid running the decoder on 32-bit targets - building and deploying only 64-bit binaries removes reachability entirely, at the cost of dropping 32-bit platform support. Where 32-bit builds are unavoidable, decode only trusted images or enforce strict upstream limits on image/canvas/requested-region dimensions so that width×height cannot exceed 32-bit usize (reject frames and canvases whose area approaches 2^32), accepting that this rejects some legitimately large images; isolating the decoder in a sandboxed, low-privilege process further limits the blast radius of any OOB write.
Same weakness CWE-122 – Heap-based Buffer Overflow
View allShare
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-62513
GHSA-5pmv-rx8r-wmv5