← Chrome — 2026-W31

a1a8e5fbb4611b99cab2bed23097040907027d84  h264_decoder: Prevent signed integer overflow in frame_num_offset

severity high class IntegerOverflow CVSS 6.5 signal score 11.89 media/ AI review CONFIRMED
Structural signals: checked_int (+5.39), msg_strong (+3.0), msg_medium (+2.0), area:media/ (+1.5)

The bug at a glance

A signed-integer overflow on attacker-controlled H.264 bitstream fields (frame_num_offset / abs_frame_num / pic_order_cnt) in the GPU-process H264Decoder; signed overflow is C++ undefined behavior and the subject/Fixed tag confirm it is a real safety fix, but the primary observable impact is corrupted POC arithmetic / decode failure rather than a direct memory-corruption primitive.

An attacker who can get a victim to decode a crafted H.264 stream (web video, WebCodecs, media file) reaches CalculatePicOrderCounts with hostile frame_num/offset values; the defender cares because this runs in the GPU process and feeds derived indices/ordering used downstream in reference-picture management.

Root cause

In H264Decoder::CalculatePicOrderCounts, frame_num_offset was computed as prev_frame_num_offset_ + max_frame_num_ and abs_frame_num as frame_num_offset + frame_num using plain int arithmetic. With attacker-influenced bitstream values (and a large seeded prev_frame_num_offset_, as the test’s 1195704462 shows) these additions and the later 2*(offset+frame_num) doubling can overflow a signed int, which is undefined behavior and yields corrupted picture-order-count values. The patch routes frame_num_offset, abs_frame_num, and temp_pic_order_cnt through base::CheckedNumeric, validates each with IsValid(), and returns false on overflow instead of proceeding with a poisoned value.

Key code

CheckedNumeric guards replace raw int POC arithmetic

base::CheckedNumeric<int> frame_num_offset = prev_frame_num_offset_;
...
if (!frame_num_offset.IsValid()) {
  DVLOG(1) << "Invalid frame_num_offset.";
  return false;
}
pic->frame_num_offset = frame_num_offset.ValueOrDie();

Patch walkthrough

  • media/gpu/h264_decoder.cc — Both pic_order_cnt_type code paths now accumulate frame_num_offset, abs_frame_num, and temp_pic_order_cnt as base::CheckedNumeric with explicit IsValid() checks that bail out (return false) on overflow, preventing undefined signed-overflow and propagation of corrupted order counts; safe_math.h is newly included.
  • media/gpu/h264_decoder_unittest.cc — Adds OverflowPicOrderCntDoesNotCrash, which seeds frame_num_offset = 1195704462 on the created picture and asserts the decoder rejects the overflowing arithmetic gracefully instead of crashing, documenting the overflow condition and the intended graceful-failure behavior.

Background

H264Decoder::CalculatePicOrderCounts — Computes a picture’s ordering counts (POC) from SPS fields and prior-frame state while parsing an H.264 slice header.

frame_num_offset / abs_frame_num — Intermediate values derived from bitstream frame_num and max_frame_num used to order reference pictures; attacker influence on these drives the overflow.

base::CheckedNumeric — A base arithmetic wrapper that tracks overflow and makes IsValid()/ValueOrDie() explicit, turning UB into a detectable error.

Vulnerability window

  1. Reachability — A crafted H.264 stream decoded by the GPU-accelerated path reaches CalculatePicOrderCounts during slice-header processing.
  2. Trigger — Hostile frame_num/offset combined with accumulated prev_frame_num_offset_ makes the int additions (or the 2* doubling) exceed INT_MAX.
  3. Primitive — Signed-integer overflow is undefined behavior; pre-patch it produced wrapped/corrupted picture-order-count values (inferred: these feed later reference-picture ordering logic).
  4. Escalation — Any memory-safety consequence beyond corrupted ordering/decode failure is unshown and not demonstrated by the diff (inferred, unconfirmed).

Proof of concept

Conceptually: construct an H.264 bitstream whose SPS uses pic_order_cnt_type 1 or 2 and whose slice sequence drives prev_frame_num_offset_ + max_frame_num_ (and abs_frame_num) near INT_MAX so the next addition overflows; feed it to the accelerated decoder and observe the pre-patch UB versus the post-patch graceful return false. No exploit code provided.

Exploitation

  1. Delivery — Attacker supplies a crafted H.264 stream via a web page, WebCodecs, or a media file the victim plays.
  2. Trigger — Stream forces the frame_num_offset / abs_frame_num / pic_order_cnt arithmetic to overflow a signed int in the GPU process.
  3. Impact — Pre-patch undefined behavior / corrupted order counts; downstream memory-safety impact is not established by the diff (inferred).

Detection & hunting

For defenders and detection engineers:

  • weggli/grep for int POC arithmetic: ‘frame_num_offset’, ‘abs_frame_num’, ‘pic_order_cnt’ with raw int + and * in media/gpu and media/parsers — Flag bitstream-derived additive/multiplicative arithmetic on plain int that lacks CheckedNumeric or range validation.
  • DVLOG lines ‘Invalid frame_num_offset.’ / ‘Invalid abs_frame_num.’ — In debug builds, these indicate the overflow guard firing on a stream and can corroborate fuzzing hits.

Audit directions

  • Other arithmetic in media/gpu/h264_decoder.cc and sibling decoders (h265/vp9/av1) handling frame_num, POC, and reference-list indices — Look for remaining raw-int computations on bitstream-controlled fields that could overflow and should be CheckedNumeric-wrapped.

Before / after

Loading diff…