Summary
AvifDecoder::read_image's alpha-plane compositing loop zips the main picture's output buffer against the independently-decoded alpha picture's plane data, with no check that the two pictures actually have matching dimensions — Iterator::zip's silent-truncation-to-the-shorter-iterator semantics mean a dimension mismatch produces incomplete/incorrect alpha compositing rather than an error.
Details
Confirmed by direct source reading against image 0.25.10, src/codecs/avif/decoder.rs:
if let Some(picture) = self.alpha_picture {
if picture.pixel_layout() != PixelLayout::I400 {
return Err(...); // checks chroma layout only
}
let stride = picture.stride(PlanarImageComponent::Y) as usize;
let plane = picture.plane(PlanarImageComponent::Y);
for (buf, slice) in Iterator::zip(
buf.chunks_exact_mut(width as usize * 4), // sized by the MAIN picture's width
plane.as_ref().chunks_exact(stride), // sized by the ALPHA picture's own stride
) {
...
}
}
The primary picture and the alpha picture are decoded via two entirely separate dav1d::Decoder instances (AvifDecoder::new), each from its own coded data (ctx.primary_item_coded_data() / ctx.alpha_item_coded_data()). There is no check anywhere that alpha_picture's width/height match the primary picture's width/height before this loop. Iterator::zip silently stops at the shorter of its two input iterators, so a mismatch doesn't error — it just applies alpha values to only part of the image (or leaves remaining rows with whatever default/prior alpha was in the buffer).
Impact
A crafted AVIF file whose alpha auxiliary image has different dimensions than its primary image produces silently incorrect (partially-composited or entirely default-alpha) output rather than a decode error — a data-integrity bug, not a crash, in a format specifically used for images requiring correct transparency (UI assets, compositing pipelines).
Suggested direction
Not submitting a PR — the fix is a straightforward dimension-equality check (alpha_picture.width()/height() == picture.width()/height()) before the compositing loop, returning a decode error on mismatch, but flagging for maintainer confirmation since I haven't constructed an actual crafted-AVIF PoC to confirm dav1d doesn't already reject mismatched auxiliary-image dimensions somewhere in its own validation (in which case this code is unreachable dead-code-for-safety rather than a live gap) — this is a source-level reachability argument, not an empirically-triggered one.
Discovered by the rust-in-peace security pipeline.
Summary
AvifDecoder::read_image's alpha-plane compositing loopzips the main picture's output buffer against the independently-decoded alpha picture's plane data, with no check that the two pictures actually have matching dimensions —Iterator::zip's silent-truncation-to-the-shorter-iterator semantics mean a dimension mismatch produces incomplete/incorrect alpha compositing rather than an error.Details
Confirmed by direct source reading against
image0.25.10,src/codecs/avif/decoder.rs:The primary picture and the alpha picture are decoded via two entirely separate
dav1d::Decoderinstances (AvifDecoder::new), each from its own coded data (ctx.primary_item_coded_data()/ctx.alpha_item_coded_data()). There is no check anywhere thatalpha_picture's width/height match the primarypicture's width/height before this loop.Iterator::zipsilently stops at the shorter of its two input iterators, so a mismatch doesn't error — it just applies alpha values to only part of the image (or leaves remaining rows with whatever default/prior alpha was in the buffer).Impact
A crafted AVIF file whose alpha auxiliary image has different dimensions than its primary image produces silently incorrect (partially-composited or entirely default-alpha) output rather than a decode error — a data-integrity bug, not a crash, in a format specifically used for images requiring correct transparency (UI assets, compositing pipelines).
Suggested direction
Not submitting a PR — the fix is a straightforward dimension-equality check (
alpha_picture.width()/height() == picture.width()/height()) before the compositing loop, returning a decode error on mismatch, but flagging for maintainer confirmation since I haven't constructed an actual crafted-AVIF PoC to confirm dav1d doesn't already reject mismatched auxiliary-image dimensions somewhere in its own validation (in which case this code is unreachable dead-code-for-safety rather than a live gap) — this is a source-level reachability argument, not an empirically-triggered one.Discovered by the rust-in-peace security pipeline.