Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactOut of bounds read in Codecs
DescriptionOut of bounds read in Codecs
ComponentCodecs
Bug ClassOOB
Tracker497926602
Fix commit7466a44ac808 (crabbyavif) +3/-3
CISA KEVNot listed
CreditedGoogle
Disclosed2026-05-05

Files Changed

  • DEPS
  • third_party/crabbyavif/README.chromium
  • third_party/crabbyavif/src
From 63bbdf60d7c60aab16192319671abac847c0b509 Mon Sep 17 00:00:00 2001
From: Vignesh Venkat <[email protected]>
Date: Thu, 02 Apr 2026 17:28:03 -0700
Subject: [PATCH] Roll src/third_party/crabbyavif/src/ c05daf3e2..7466a44ac (39 commits)

https://chromium.googlesource.com/external/github.com/webmproject/CrabbyAvif.git/+log/c05daf3e2e6d..7466a44ac808

$ git log c05daf3e2..7466a44ac --date=short --no-merges --format='%ad %ae %s'
2026-03-31 vigneshv tile: Check for yuv_format compatibility
2026-03-31 vigneshv dav1d/avm: Use correct plane height for Pixels
2026-03-27 yguyon Collapse conditional blocks (#788)
2026-03-26 vigneshv libyuv: Use default constants on Android
2026-03-26 vigneshv cli: Add alt_clli arg
2026-03-26 vigneshv cli: Allow specifying alpha/gainmap quality
2026-03-26 vigneshv cli: Add support for extracting gainmap
2026-03-26 vigneshv jpeg: Support images in the old apple format
2026-03-26 yguyon Replace Pixels::try_clone() with match blocks (#782)
2026-03-25 vigneshv jpeg: Add tests for jpeg gainmap parsing
2026-03-25 vigneshv jpeg: Support gainmap conversion
2026-03-25 vigneshv tests: Normalize test file names
2026-03-24 vigneshv conformance: Remove avifdec comparison
2026-03-24 vigneshv jpeg: Use zune jpeg crate directly
2026-03-24 yguyon Inline Decoder::apply_sample_transform() (#775)
2026-03-24 yguyon Move row_bytes assignment closer to planes (#774)
2026-03-20 yguyon Rename Image::clone_properties() to shallow_clone() (#773)
2026-03-18 vigneshv cli: Add placeholder functions for gainmap
2026-03-18 vigneshv jpeg: Support additional metadata
2026-03-18 vigneshv Revert: "Add libjpeg-sys for JPG support"
2026-03-17 vigneshv image: Refactor xmp fix into a function
2026-03-16 vigneshv Add libjpeg-sys for JPG support
2026-03-16 vigneshv cli: Override irot/imir only if explicitly specified
2026-03-12 vigneshv png: Rename libpng callbacks with a crabbyavif_ prefix
2026-03-11 vigneshv png: Use libpng instead of the png crate
2026-03-11 vigneshv Remove libgav1 support from crabbyavif.
2026-03-09 vigneshv tests: Use PngReader instead of png crate
2026-03-06 vigneshv zlib: Build zlib in a separate directory
2026-03-04 yguyon Rename Image::shallow_clone() to clone_properties (#758)
2026-03-03 vigneshv Add libpng-sys for PNG support
2026-02-25 yguyon Fix AV1 encoder instead of decoder in README.md (#760)
2026-02-24 yguyon Remove Image::steal_or_copy_planes() (#759)
2026-02-23 yguyon Move copy_properties_from from image to dec mod (#755)
2026-02-20 vigneshv cli: Add flag for enabling sample transform
2026-02-20 yguyon Move copy_from_tile() from image to tile (#756)
2026-02-20 yguyon Remove unneccessary temp var final_recipe (#754)
2026-02-20 yguyon Remove unused Image::image_owns_planes (#753)
2026-02-19 vigneshv cli: Support cropped output
2026-02-19 qwf crabbyavif: make avifFree and avifAlloc call libc

Created with:
  roll-dep src/third_party/crabbyavif/src

Bug: 497926602
Change-Id: I44f9450e77cae652ea255b64b67a8e844a4bc870
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7720378
Commit-Queue: James Zern <[email protected]>
Auto-Submit: Vignesh Venkat <[email protected]>
Reviewed-by: James Zern <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1609584}
---

diff --git a/DEPS b/DEPS
index fa5c073..f6da8bc 100644
--- a/DEPS
+++ b/DEPS
@@ -459,7 +459,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling crabbyavif
   # and whatever else without interference from each other.
-  'crabbyavif_revision': 'c05daf3e2e6d83f2a359ab97094ce042944020a9',
+  'crabbyavif_revision': '7466a44ac80893803d4a7168b98dc6cd02d1fe2d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Speedometer main
   # and whatever else without interference from each other.
diff --git a/third_party/crabbyavif/README.chromium b/third_party/crabbyavif/README.chromium
index 62323219..39c53066 100644
--- a/third_party/crabbyavif/README.chromium
+++ b/third_party/crabbyavif/README.chromium
@@ -2,7 +2,7 @@
 Short Name: crabbyavif
 URL: https://github.com/webmproject/CrabbyAvif
 Version: N/A
-Revision: c05daf3e2e6d83f2a359ab97094ce042944020a9
+Revision: 7466a44ac80893803d4a7168b98dc6cd02d1fe2d
 Update Mechanism: Manual
 License: Apache-2.0
 License File: LICENSE
diff --git a/third_party/crabbyavif/src b/third_party/crabbyavif/src
index c05daf3..7466a44 160000
--- a/third_party/crabbyavif/src
+++ b/third_party/crabbyavif/src
@@ -1 +1 @@
-Subproject commit c05daf3e2e6d83f2a359ab97094ce042944020a9
+Subproject commit 7466a44ac80893803d4a7168b98dc6cd02d1fe2d
Loading diff…

Original Bug Report

reported by [email protected]

Heap OOB Read in crabbyavif via incremental decode state loss and oversized slice

Project Fortify, an experimental security project, has identified the following potential security issue.

Overview: A potential heap out-of-bounds read exists in the crabbyavif library during incremental grid image decoding. A state tracking flaw allows mixing incompatible YUV formats in a grid, which interacts with an oversized buffer slice in the dav1d wrapper to bypass Rust bounds checks. This can lead to renderer heap memory being leaked into the decoded image, which an attacker could read via a <canvas> element.

Affected files:

  • third_party/crabbyavif/src/src/decoder/mod.rs
  • third_party/crabbyavif/src/src/codecs/dav1d.rs
  • third_party/crabbyavif/src/src/image.rs

Estimated timestamp from git blame: 2025-04-11

Summary

A potential heap out-of-bounds (OOB) read vulnerability exists in the crabbyavif library when processing grid images during incremental decoding. Two separate logic flaws combine to create this issue: a validation bypass caused by state loss when resuming an incremental decode, and an oversized PointerSlice created in the dav1d codec wrapper. Together, these allow an attacker to leak adjacent heap memory from the renderer process into the decoded image pixels.

Technical Details

1. Validation Bypass in GridImageHelper

In third_party/crabbyavif/src/src/decoder/mod.rs, the decode_grid() function handles grid assembly. During incremental decoding (which is enabled for still images in Blink), if the network stalls, the function yields. When more data arrives, decode_grid() is called again.

On each call, it constructs a fresh GridImageHelper instance:

// decoder/mod.rs:1894
let mut grid_image_helper = GridImageHelper {
    // ...
    first_cell_image: None,
    // ...
};

Because first_cell_image is initialized to None on every resume, copy_from_cell_image() fails to perform consistency checks on subsequent tiles if the decode was interrupted:

// decoder/mod.rs:425
} else if self.first_cell_image.is_some()
    && !cell_image.has_same_properties_and_cicp(self.first_cell_image.unwrap_ref())
{
    return AvifError::invalid_image_grid("grid image contains mismatched tiles");
}

This bypass allows an attacker to mix tiles with different subsampling formats (e.g., a YUV 4:4:4 tile followed by a YUV 4:2:0 tile).

2. Oversized PointerSlice in dav1d Wrapper

In third_party/crabbyavif/src/src/codecs/dav1d.rs, the wrapper incorrectly uses the full image.height (the luma height) when creating PointerSlice wrappers for all planes:

// codecs/dav1d.rs:255
image.planes[plane] = Some(Pixels::from_raw_pointer(
    dav1d_picture.data[plane] as *mut u8,
    image.depth as u32,
    image.height, // ERROR: Uses luma height for chroma planes
    image.row_bytes[plane],
)?);

For YUV 4:2:0 formats, chroma buffers are half the luma height. Passing the full height creates a slice that overestimates the allocation size, defeating Rust’s runtime bounds checks.

3. Triggering the OOB Read

In third_party/crabbyavif/src/src/image.rs, copy_from_tile merges a tile into the grid. If a 1-row grid is initialized as YUV 4:4:4 (from the first tile), but a subsequent tile is YUV 4:2:0, the code calculates the copy height based on the destination grid’s height:

// image.rs:494
let src_height_to_copy = if row_index == grid.rows - 1 {
    // ...
    checked_sub!(u32_from_usize(self.height(plane))?, height_so_far)?
} 

This evaluates to the full luma height. The decoder then copies luma_height rows from the source tile’s smaller chroma buffer. Because the PointerSlice from step 2 is oversized, the Rust bounds check succeeds, and crabbyavif silently reads past the end of the dav1d allocation, copying adjacent renderer heap memory into the image.

Suggested Attacker Steps (Theoretical)

Note: These are potential steps as our tooling does not currently run live code.

  1. Craft an AVIF image with a 1-row, multi-column grid.
  2. Encode Tile 0 as a YUV 4:4:4 keyframe.
  3. Encode Tile 1 as a YUV 4:2:0 keyframe.
  4. Serve the file over HTTP with chunked encoding. Send Tile 0 data, then stall the connection to trigger an incremental decode pause in Chrome.
  5. Send Tile 1 data. The decoder resumes, loses first_cell_image state, bypasses validation, and OOB-reads the Tile 1 chroma buffer.
  6. Render the resulting image to an offscreen <canvas> and use getImageData() to extract the leaked heap memory.

Suggested Fix

  1. Persist Grid State: Move first_cell_image out of the temporary GridImageHelper and store it in a persistent structure (e.g., Decoder or TileInfo) so format validation works across incremental decode boundaries.
  2. Correct Slice Bounds: In dav1d.rs, use the appropriate plane height when creating the slice: image.height(plane.into()) instead of image.height.

Evaluated with Chrome root at commit: a9cbf6e8b275fe4147435aa905f3b7f5a656f5f0


Results from so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.

View on issue tracker