CVE-2026-4463
Overview
Background
- `cpi->orig_source`
- a libaom AV1 encoder frame buffer (
YV12_BUFFER_CONFIG) that keeps a copy of the unfiltered source frame so PSNR can be computed against the true input when real-time temporal filtering modifiescpi->source. - `use_rtc_tf`
- the real-time speed feature
cpi->sf.rt_sf.use_rtc_tfthat enables temporal filtering of the source in RTC encoding; together withis_psnr_calc_enabled(cpi)it gates this code. - `aom_alloc_frame_buffer` / `aom_yv12_copy_y`
- the libaom routines that (re)allocate a frame buffer for given dimensions, subsampling and bit depth, and copy the luma plane of one frame buffer into another using the source frame’s geometry.
Root Cause Analysis
In av1/encoder/encoder.c, before applying RTC temporal filtering the encoder copies cpi->source into cpi->orig_source with aom_yv12_copy_y(cpi->source, &cpi->orig_source, 1). The old code decided whether to (re)allocate cpi->orig_source by comparing cpi->rc.prev_coded_width/prev_coded_height with the configured cpi->oxcf.frm_dim_cfg.width/height, and allocated it using those configured dimensions plus seq_params->subsampling_x/y and seq_params->use_highbitdepth. None of these values is guaranteed to describe the frame that is actually copied: if cpi->source had larger crop dimensions, different subsampling, or a high-bit-depth layout while cpi->orig_source was sized differently (or was not reallocated because the stale prev_coded_* comparison did not trigger), the copy writes beyond the cpi->orig_source allocation, matching the advisory’s heap buffer overflow.
The fix keys the reallocation check directly on cpi->source (y_crop_width, y_crop_height, subsampling_x, subsampling_y, flags) and allocates with exactly those parameters, deriving use_highbitdepth from YV12_FLAG_HIGHBITDEPTH in cpi->source->flags, so the destination always matches the copy source. The enclosing function is not named in the hunk header.
cpi->orig_source from cpi->source itself.Attack Path
- AV1 encoding in RTC mode
A page causes Chrome’s WebRTC stack to encode video with the libaom AV1 encoder in real-time mode where
use_rtc_tfis active and PSNR calculation is enabled (whether Chrome enables PSNR calculation by default is not shown in the diff). - Source/config mismatch
The frame reaching the encoder (
cpi->source) differs in crop size, subsampling, or bit-depth flags from whatcpi->orig_sourcewas allocated for, e.g. after a resolution change where the staleprev_coded_*vsfrm_dim_cfgcomparison does not force reallocation (specific trigger inferred). - Overflowing copy
aom_yv12_copy_ycopies the luma plane usingcpi->source’s geometry into the undersizedcpi->orig_source, writing past its heap allocation. - Heap corruption
Adjacent heap memory in the encoding process is overwritten; any controlled corruption or further primitive is
(inferred).
Impact Assessment
use_rtc_tf and PSNR calculation enabled, plus a source-frame/configuration mismatch; the diff does not show whether these are on by default for web content, so default reachability is not established here. Code execution or sandbox escape is not shown and would be (inferred); the advisory does not report in-the-wild exploitation.Files Changed
av1/encoder/encoder.c
Audit Directions
- Cached scratch frame buffersAudit other libaom/libvpx encoder buffers (scaled sources, lookahead copies, denoiser buffers) whose realloc condition uses
oxcfdimensions orrc.prev_coded_*rather than the geometry of the frame actually copied in. - Bit-depth and subsampling driftCheck allocations that take
use_highbitdepthor subsampling fromseq_paramswhile the copied frame’sflags/subsampling_*may differ, since a high-bit-depth source doubles bytes per sample. - Dynamic resize paths in RTCExercise mid-stream resolution changes and spatial-layer switches in WebRTC encoder integrations, verifying every per-frame copy re-checks destination capacity.
Patch
From 34f25197c6e9efef129992bb763aefa6036119fd Mon Sep 17 00:00:00 2001
From: Wan-Teh Chang <wtc@google.com>
Date: Thu, 12 Mar 2026 17:58:23 -0700
Subject: [PATCH] Allocate original source buffer for psnr correctly
Correct the condition for allocating the cpi->orig_source buffer.
The use_rtc_tf speed feature and the cpi->orig_source buffer were added
in https://aomedia-review.git.corp.google.com/c/aom/+/153083.
The condition for allocating the cpi->orig_source buffer was modified in
https://aomedia-review.git.corp.google.com/c/aom/+/186721. The
cpi->rc.prev_coded_width and cpi->rc.prev_coded_height variables used in
the new condition were added in
https://aomedia-review.git.corp.google.com/c/aom/+/167241.
Bug: 491358681
Change-Id: Ibfc88491bc2e293f56394dc201fccab7786de02b
---
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 6182f1d..b1c02e4 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -3157,20 +3157,24 @@
// This is for rtc temporal filtering case.
if (is_psnr_calc_enabled(cpi) && cpi->sf.rt_sf.use_rtc_tf) {
- const SequenceHeader *seq_params = cm->seq_params;
-
if (cpi->orig_source.buffer_alloc_sz == 0 ||
- cpi->rc.prev_coded_width != cpi->oxcf.frm_dim_cfg.width ||
- cpi->rc.prev_coded_height != cpi->oxcf.frm_dim_cfg.height) {
- // Allocate a source buffer to store the true source for psnr calculation.
- if (aom_alloc_frame_buffer(
- &cpi->orig_source, cpi->oxcf.frm_dim_cfg.width,
- cpi->oxcf.frm_dim_cfg.height, seq_params->subsampling_x,
- seq_params->subsampling_y, seq_params->use_highbitdepth,
- cpi->oxcf.border_in_pixels, cm->features.byte_alignment, false,
- 0))
+ cpi->orig_source.y_crop_width != cpi->source->y_crop_width ||
+ cpi->orig_source.y_crop_height != cpi->source->y_crop_height ||
+ cpi->orig_source.subsampling_x != cpi->source->subsampling_x ||
+ cpi->orig_source.subsampling_y != cpi->source->subsampling_y ||
+ cpi->orig_source.flags != cpi->source->flags) {
+ // Allocate a source buffer to store the original source for psnr
+ // calculation.
+ const int use_highbitdepth =
+ (cpi->source->flags & YV12_FLAG_HIGHBITDEPTH) != 0;
+ if (aom_alloc_frame_buffer(&cpi->orig_source, cpi->source->y_crop_width,
+ cpi->source->y_crop_height,
+ cpi->source->subsampling_x,
+ cpi->source->subsampling_y, use_highbitdepth,
+ cpi->oxcf.border_in_pixels,
+ cm->features.byte_alignment, false, 0))
aom_internal_error(cm->error, AOM_CODEC_MEM_ERROR,
- "Failed to allocate scaled buffer");
+ "Failed to allocate cpi->orig_source buffer");
}
aom_yv12_copy_y(cpi->source, &cpi->orig_source, 1);
Original Bug Report
Heap buffer overflow write in libaom AV1 SVC encoding via WebRTC
Heap buffer overflow write in libaom AV1 SVC encoding via WebRTC
Summary
A heap buffer overflow write exists in libaom’s AV1 encoder when encoding video with spatial Scalable Video Coding (SVC) through WebRTC. The chroma_check function in var_based_part.c uses the LAST_FRAME reference’s scale factors when computing buffer offsets for GOLDEN_FRAME and ALTREF_FRAME, which have different resolutions in spatial SVC configurations. This causes setup_pred_plane to calculate an out-of-bounds buffer pointer, and subsequent frame copy operations write past the end of the allocated YV12 frame buffer. A web page can trigger this by establishing a WebRTC peer connection with AV1 and the L2T1 scalability mode. The vulnerability affects all platforms where WebRTC AV1 SVC encoding is supported (Windows, Linux, macOS, ChromeOS, Android).
Bisect
Introducing Commit (GOLDEN path): c6a503f
- Date: August 18, 2022
- Author: Marco Paniconi (marpan@google.com)
- Review: https://aomedia-review.googlesource.com/c/aom/+/161261
- Subject: “rtc: Fix to color artifacts under high motion”
Extended to ALTREF path: 555b3aa
- Date: March 6, 2023
- Author: Marco Paniconi (marpan@google.com)
- Review: https://aomedia-review.googlesource.com/c/aom/+/171601
- Subject: “rtc: Color sensitivity for altref in nonrd”
Both commits are in the upstream aom.git repository. The original setup_pred_plane with LAST_FRAME’s scale factors was introduced in commit 309d0af (July 27, 2022) for the LAST_FRAME path, where using LAST’s scale factors is correct. When the GOLDEN and ALTREF paths were added in the subsequent commits, they reused the same sf variable without obtaining the appropriate scale factors for those reference frames.
Root Cause
The chroma_check function in var_based_part.c computes UV chroma SAD values for three reference frames (LAST, GOLDEN, ALTREF) to determine color sensitivity. It retrieves the scale factors once, for LAST_FRAME only:
// av1/encoder/var_based_part.c:1026-1029
// https://aomedia.googlesource.com/aom/+/39606bf4bae/av1/encoder/var_based_part.c#1026
const YV12_BUFFER_CONFIG *yv12_g = get_ref_frame_yv12_buf(cm, GOLDEN_FRAME);
const YV12_BUFFER_CONFIG *yv12_alt = get_ref_frame_yv12_buf(cm, ALTREF_FRAME);
const struct scale_factors *const sf =
get_ref_scale_factors_const(cm, LAST_FRAME);
This sf is then passed to setup_pred_plane for all three reference frames. For LAST_FRAME, this is correct. For GOLDEN_FRAME and ALTREF_FRAME, it is wrong:
// av1/encoder/var_based_part.c:1062-1069
// https://aomedia.googlesource.com/aom/+/39606bf4bae/av1/encoder/var_based_part.c#1062
if (y_sad_g != UINT_MAX) {
uint8_t *src = (plane == 1) ? yv12_g->u_buffer : yv12_g->v_buffer;
setup_pred_plane(&dst, xd->mi[0]->bsize, src, yv12_g->uv_crop_width,
yv12_g->uv_crop_height, yv12_g->uv_stride, xd->mi_row,
xd->mi_col, sf, xd->plane[plane].subsampling_x,
xd->plane[plane].subsampling_y);
uv_sad_g = cpi->ppi->fn_ptr[bs].sdf(p->src.buf, p->src.stride,
dst.buf, dst.stride);
}
The setup_pred_plane function computes the buffer pointer as dst->buf = src + scaled_buffer_offset(x, y, stride, scale), where x and y are derived from the current macroblock’s mi_row and mi_col:
// av1/common/reconinter.h:386-404
// https://aomedia.googlesource.com/aom/+/39606bf4bae/av1/common/reconinter.h#386
static inline void setup_pred_plane(struct buf_2d *dst, BLOCK_SIZE bsize,
uint8_t *src, int width, int height,
int stride, int mi_row, int mi_col,
const struct scale_factors *scale,
int subsampling_x, int subsampling_y) {
const int x = (MI_SIZE * mi_col) >> subsampling_x;
const int y = (MI_SIZE * mi_row) >> subsampling_y;
dst->buf = src + scaled_buffer_offset(x, y, stride, scale);
}
In AV1 spatial SVC, the enhancement layer (e.g., 1280x720) references a lower-resolution base layer (e.g., 640x360) as its GOLDEN frame. LAST_FRAME, being from the same spatial layer, has identity scale factors (no scaling). When these identity scale factors are applied to mi_row/mi_col coordinates from the 1280x720 layer while indexing into the 640x360 GOLDEN buffer, the resulting offset exceeds the smaller buffer’s allocation. For a 1280x720 frame with 4:2:0 chroma subsampling, the UV plane coordinates can reach up to y=358, but the GOLDEN frame’s UV buffer at 640x360 only accommodates rows up to y=179, producing an overflow of approximately 114 rows worth of data.
The ASAN report shows the overflow manifests as a write of 640 bytes immediately past the end of a 688,935-byte allocation in aom_yv12_copy_v_c, which copies the V chroma plane row by row using memcpy. The overflow occurs because the source or destination frame buffer was allocated for the smaller spatial layer dimensions, while the copy loop iterates over the larger layer’s height.
Reproduce
Configure out/asan-release/args.gn:
is_asan = true
is_debug = false
dcheck_always_on = false
target_cpu = "x64"
is_component_build = true
Then build and run:
autoninja -C out/asan-release chrome
ASAN_OPTIONS=detect_odr_violation=0 out/asan-release/chrome --no-sandbox poc.html
The PoC opens a WebRTC peer connection with AV1 SVC (L2T1 scalability mode) encoding a 1280x720 canvas stream. The heap-buffer-overflow fires within seconds of encoding.
The ASAN report:
=================================================================
==43000==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x1409738e9b27 at pc 0x7ff8de4eb48c bp 0x00e97d7fc8e0 sp 0x00e97d7fc928
WRITE of size 640 at 0x1409738e9b27 thread T5
#0 0x7ff8de4eb48b (clang_rt.asan_dynamic-x86_64.dll+0x18004b48b)
#1 0x7ff8b03c7b9a in aom_yv12_copy_v_c yv12extend.c:313:5
#2 0x7ff8b07b14f1 in encode_with_recode_loop_and_filter encoder.c:3706:9
#3 0x7ff8b07a1f41 in av1_encode encoder.c:4611:9
#4 0x7ff8b0eecde7 in av1_encode_strategy encode_strategy.c:1713:7
#5 0x7ff8b07a8765 in av1_get_compressed_data encoder.c:5360:22
#6 0x7ff8b03a627f in encoder_encode av1_cx_iface.c:3620:20
#7 0x7ff8b03c3dc8 in aom_codec_encode aom_encoder.c:191:11
#8 0x7ff8afda2f26 in LibaomAv1Encoder::DoEncode libaom_av1_encoder.cc:1101:7
#9 0x7ff8afd9af17 in LibaomAv1Encoder::Encode libaom_av1_encoder.cc:993:16
#10 0x7ff8ae3d79d3 in SimulcastEncoderAdapter::Encode simulcast_encoder_adapter.cc:672:23
#11 0x7ff84aee87fe in StatsCollectingEncoder::Encode stats_collecting_encoder.cc:123:20
#12 0x7ff8aef38013 in VideoStreamEncoder::EncodeVideoFrame video_stream_encoder.cc:2226:43
0x1409738e9b27 is located 0 bytes after 688935-byte region [0x140973841800,0x1409738e9b27)
allocated by thread T5 here:
#0 0x7ff8de4ec8df (clang_rt.asan_dynamic-x86_64.dll+0x18004c8df)
#1 0x7ff8b07b7dee in aom_memalign aom_mem.c:59:22
#2 0x7ff8b086bc5a in aom_realloc_frame_buffer yv12config.c:259:12
#3 0x7ff8b086c2f8 in aom_alloc_frame_buffer yv12config.c:274:12
#4 0x7ff8b07b143e in encode_with_recode_loop_and_filter encoder.c:3706:9
SUMMARY: AddressSanitizer: heap-buffer-overflow yv12extend.c:313:5 in aom_yv12_copy_v_c
References
chroma_check(var_based_part.c:987) — buggy function, uses LAST’ssffor GOLDEN/ALTREFsetup_pred_plane(reconinter.h:386) — computesdst->bufviascaled_buffer_offsetaom_yv12_copy_v_c(yv12extend.c:293) — crash site, memcpy overflowLibaomAv1Encoder::DoEncode(libaom_av1_encoder.cc) — WebRTC entry point into libaom
Credit
Please use c6eed09fc8b174b0f3eebedcceb1e792 as the credit for this vulnerability. Thank you.
- https://aomedia-review.googlesource.com/c/aom/+/161261
- https://aomedia-review.googlesource.com/c/aom/+/171601
- https://aomedia.googlesource.com/aom/+/309d0affc51a621ffafb76cf6fdc8e917c345e8e
- https://aomedia.googlesource.com/aom/+/39606bf4bae/aom_scale/generic/yv12extend.c#293
- https://aomedia.googlesource.com/aom/+/39606bf4bae/av1/common/reconinter.h#386
- https://aomedia.googlesource.com/aom/+/39606bf4bae/av1/encoder/var_based_part.c#1026
- https://aomedia.googlesource.com/aom/+/39606bf4bae/av1/encoder/var_based_part.c#1062
- https://aomedia.googlesource.com/aom/+/39606bf4bae/av1/encoder/var_based_part.c#987
- https://aomedia.googlesource.com/aom/+/555b3aae440dc1a23d11723c556d95b172cb259d
- https://aomedia.googlesource.com/aom/+/c6a503f4b15da2ecef856f917d08473ea788ae00
- https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/modules/video_coding/codecs/av1/libaom_av1_encoder.cc;l=1101