Low watch CVSS 8.8 chrome Integer Overflow 📄 Reporter bug report 🔧 Commit mapped

Overview

Low
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInteger overflow in Media
DescriptionInteger overflow in Media
ComponentMedia
Sub-componentMedia
Bug ClassInteger Overflow
Action tierwatch — ub-class only — no demonstrated corruption path
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker485203821
Fix commit7cf2bae3b068 (chromium/src) +82/-23
CWECWE-472
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedMohammed Yasar B & Ameen Basha M K
Reported2026-02-17 (per vendor advisory)
Fix landed2026-02-18 (public git)
Disclosed2026-04-07
Patch gap48 days — fix public in source before the advisory

Background

`base::CheckedNumeric<int>`
an integer wrapper that tracks overflow during arithmetic and exposes IsValid(), ValueOrDie(), and AssignIfValid() so overflowing results can be detected and rejected.
H.265 `ParseSliceHeader`
the HEVC slice-header parser in h265_parser.cc that reads and combines bitstream fields such as delta_poc_msb_cycle_lt[], slice_qp_delta, num_entry_point_offsets, and offset_len_minus1.
`ParseSEI`
the SEI parser (present in both h264_parser.cc and h265_parser.cc) that accumulates type and payload_size from runs of 0xff bytes plus a trailing byte, then derives num_bits_remain = payload_size * 8.
`SKIP_BITS_OR_RETURN` / `IN_RANGE_OR_RETURN`
macros that advance the bit reader by a computed count, or enforce an inclusive range; both are unsafe if handed an expression that already overflowed.

Root Cause Analysis

Multiple arithmetic combinations of attacker-controlled bitstream values were carried out in plain int in the H.264/H.265 parsers, so a crafted stream could induce signed-integer overflow (undefined behavior) before validation. In H.265 ParseSliceHeader, Equation 7-52’s delta_poc_msb_cycle_lt[i] = delta_poc_msb_cycle_lt[i] + delta_poc_msb_cycle_lt[i-1] could overflow; the num_entry_point_offsets upper bound (num_tile_columns_minus1 + 1) * pic_height_in_ctbs_y - 1 could overflow; and the entry-point skip count num_entry_point_offsets * (offset_len_minus1 + 1) passed to SKIP_BITS_OR_RETURN could overflow. The pre-patch slice_qp_delta validation also computed the potentially overflowing 26 + init_qp_minus26 + slice_qp_delta inside IN_RANGE_OR_RETURN, and ParsePredWeightTable added delta_chroma_log2_weight_denom to luma_log2_weight_denom without first bounding it. In ParseSEI, the type/payload_size 0xff-run accumulation and payload_size * 8 could overflow. The invariant violated throughout is that these derived counts/limits remain representable in int.

The fix wraps each accumulation/product in base::CheckedNumeric<int>, returning kInvalidStream when IsValid()/AssignIfValid() fails, rewrites the slice_qp_delta check to bound the raw delta ([-qp_bd_offset_y - base_qp, 51 - base_qp]) so no overflowing sum is formed, and adds IN_RANGE_OR_RETURN(delta_chroma_log2_weight_denom, -7, 7) before its addition — every guard executes before the value is consumed.

Key insight
The single mistake was combining attacker-controlled bitstream fields with plain int arithmetic (sums, products, and 0xff-run accumulations), allowing signed overflow before any range check; the fix performs those computations with base::CheckedNumeric<int> and reorders the delta checks to bound raw inputs, rejecting the stream on overflow.

Attack Path

  1. Craft a malformed H.265 (or H.264) stream The attacker encodes hostile slice-header fields — e.g. large num_tile_columns_minus1 × pic_height_in_ctbs_y, large offset_len_minus1 with many num_entry_point_offsets, extreme delta_poc_msb_cycle_lt, or long SEI 0xff runs.
  2. Victim decodes the media The stream is parsed by ParseSliceHeader/ParseSEI via <video>, MSE, or a media file in the media/GPU decode path.
  3. Signed-integer overflow occurs (pre-patch) The plain-int products/sums overflow — undefined behavior that can yield a wrong limit, a corrupted num_bits_remain, or an implausible skip count.
  4. Overflowed count drives a bit-skip or bound An overflowed value could reach SKIP_BITS_OR_RETURN or a subsequent range comparison, mis-advancing or mis-validating the parser. (inferred — the patch demonstrates the overflow but no concrete OOB read/write, exploitation primitive, or escalation)

Impact Assessment

An attacker supplying a decoded H.265/H.264 bitstream can trigger signed-integer overflow in the media bitstream parsers (media/GPU process context); the sole precondition is that the victim decodes attacker-controlled media. The advisory rates bug 485203821 low severity (“Integer overflow in Media”) with no reported in-the-wild exploitation. The diff establishes only the integer overflow/UB; any out-of-bounds consequence or further impact is (inferred). (One commit fixes three related bugs — 485203821, 485115554, 485212874 — so the individual overflows are not separately attributed in the patch.)

Changed Functions

FunctionChangeNotes
while
media/parsers/h264_parser.cc
modified
if
media/parsers/h265_parser.cc
modified
while
media/parsers/h265_parser.cc
modified

Files Changed

  • media/parsers/h264_parser.cc
  • media/parsers/h265_parser.cc

Audit Directions

  • Products feeding skip/allocation sinks
    Audit expressions like count * (len + 1) or cols * rows - 1 built from parsed fields and passed to SKIP_BITS_OR_RETURN, allocations, or bound checks; wrap them in base::CheckedNumeric.
  • Overflow-before-check patterns
    Flag any IN_RANGE_OR_RETURN(a + b + c, ...) where the summed terms are attacker-influenced, since the sum can overflow before the range test; prefer bounding the raw input.
  • Equation-driven accumulations
    Review spec-equation implementations (e.g. POC/delta_poc recurrences, 0xff-run counters) across codec parsers for plain-int accumulation without checked arithmetic.
From 7cf2bae3b0688de45d5215e768ca987f2967fc46 Mon Sep 17 00:00:00 2001
From: Eugene Zemtsov <[email protected]>
Date: Wed, 18 Feb 2026 14:30:58 -0800
Subject: [PATCH] media: Fix integer overflows in H264 and H265 parsers

This change addresses multiple integer overflow issues in the
H.264 and H.265 bitstream parsers.

The following calculations are now protected or validated:
  - H.264/H.265 `ParseSEI`: The accumulation of SEI message `type` and
    `payload_size` is now protected using `base::CheckedNumeric`.
  - H.265 `ParseSliceHeader`:
     - The summation of `delta_poc_msb_cycle_lt` values is now protected
       using `base::CheckedNumeric`.
     - `slice_qp_delta` validation is refactored to check the delta against
       derived bounds instead of performing a potentially overflowing addition.
     - `num_entry_point_offsets` upper bound calculation and the subsequent
       bit skip calculation are now protected using `base::CheckedNumeric`.
  - H.265 `ParsePredWeightTable`:
     - `delta_chroma_log2_weight_denom` is now validated against constant
       bounds [-7, 7] before addition to prevent signed integer overflow.

Bug: 485203821, 485115554, 485212874
Change-Id: Ifc8da5426b0d9f0e3bbfed30d175e62af46bca22
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7586436
Reviewed-by: Ted (Chromium) Meyer <[email protected]>
Commit-Queue: Eugene Zemtsov <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1586702}
---

diff --git a/media/parsers/h264_parser.cc b/media/parsers/h264_parser.cc
index d0814220..03cafeb3 100644
--- a/media/parsers/h264_parser.cc
+++ b/media/parsers/h264_parser.cc
@@ -1542,22 +1542,43 @@
   // the parsed SEI messages, so we have to set a limit here.
   constexpr int kMaxParsedSEIMessages = 64;
   do {
-    int type = 0;
+    base::CheckedNumeric<int> type_checked = 0;
     READ_BITS_OR_RETURN(8, &byte);
     while (byte == 0xff) {
-      type += 255;
+      type_checked += 255;
       READ_BITS_OR_RETURN(8, &byte);
     }
-    type += byte;
+    type_checked += byte;
 
-    int payload_size = 0;
+    if (!type_checked.IsValid()) {
+      DVLOG(1) << "SEI type overflow";
+      return kInvalidStream;
+    }
+    int type = type_checked.ValueOrDie();
+
+    base::CheckedNumeric<int> payload_size_checked = 0;
     READ_BITS_OR_RETURN(8, &byte);
     while (byte == 0xff) {
-      payload_size += 255;
+      payload_size_checked += 255;
       READ_BITS_OR_RETURN(8, &byte);
     }
-    payload_size += byte;
-    int num_bits_remain = payload_size * 8;
+    payload_size_checked += byte;
+
+    if (!payload_size_checked.IsValid()) {
+      DVLOG(1) << "SEI payload size overflow";
+      return kInvalidStream;
+    }
+
+    int payload_size = payload_size_checked.ValueOrDie();
+    base::CheckedNumeric<int> num_bits_remain_checked =
+        payload_size_checked * 8;
+
+    if (!num_bits_remain_checked.IsValid()) {
+      DVLOG(1) << "SEI payload bits overflow";
+      return kInvalidStream;
+    }
+
+    int num_bits_remain = num_bits_remain_checked.ValueOrDie();
 
     DVLOG(4) << "Found SEI message type: " << type
              << " payload size: " << payload_size;
diff --git a/media/parsers/h265_parser.cc b/media/parsers/h265_parser.cc
index cd4800e9..06303f8 100644
--- a/media/parsers/h265_parser.cc
+++ b/media/parsers/h265_parser.cc
@@ -13,6 +13,7 @@
 #include "base/bits.h"
 #include "base/logging.h"
 #include "base/notreached.h"
+#include "base/numerics/checked_math.h"
 #include "base/numerics/safe_conversions.h"
 #include "media/base/decrypt_config.h"
 #include "media/base/video_codecs.h"
@@ -1272,9 +1273,11 @@
                 std::pow(2, 32 - sps->log2_max_pic_order_cnt_lsb_minus4 - 4));
             // Equation 7-52.
             if (i != 0 && i != shdr->num_long_term_sps) {
-              shdr->delta_poc_msb_cycle_lt[i] =
-                  shdr->delta_poc_msb_cycle_lt[i] +
-                  shdr->delta_poc_msb_cycle_lt[i - 1];
+              base::CheckedNumeric<int> sum = shdr->delta_poc_msb_cycle_lt[i];
+              sum += shdr->delta_poc_msb_cycle_lt[i - 1];
+              if (!sum.AssignIfValid(&shdr->delta_poc_msb_cycle_lt[i])) {
+                return kInvalidStream;
+              }
             }
           }
         }
@@ -1361,8 +1364,9 @@
       IN_RANGE_OR_RETURN(5 - shdr->five_minus_max_num_merge_cand, 1, 5);
     }
     READ_SE_OR_RETURN(&shdr->slice_qp_delta);
-    IN_RANGE_OR_RETURN(26 + pps->init_qp_minus26 + shdr->slice_qp_delta,
-                       -pps->qp_bd_offset_y, 51);
+    int base_qp = 26 + pps->init_qp_minus26;
+    IN_RANGE_OR_RETURN(shdr->slice_qp_delta, -pps->qp_bd_offset_y - base_qp,
+                       51 - base_qp);
 
     if (pps->pps_slice_chroma_qp_offsets_present_flag) {
       READ_SE_OR_RETURN(&shdr->slice_cb_qp_offset);
@@ -1410,15 +1414,26 @@
           (pps->num_tile_columns_minus1 + 1) * (pps->num_tile_rows_minus1 + 1) -
               1);
     } else {  // both are true
-      IN_RANGE_OR_RETURN(
-          num_entry_point_offsets, 0,
-          (pps->num_tile_columns_minus1 + 1) * sps->pic_height_in_ctbs_y - 1);
+      base::CheckedNumeric<int> limit = pps->num_tile_columns_minus1 + 1;
+      limit *= sps->pic_height_in_ctbs_y;
+      limit -= 1;
+      if (!limit.IsValid()) {
+        return kInvalidStream;
+      }
+      int limit_val = limit.ValueOrDie();
+      IN_RANGE_OR_RETURN(num_entry_point_offsets, 0, limit_val);
     }
     if (num_entry_point_offsets > 0) {
       int offset_len_minus1;
       READ_UE_OR_RETURN(&offset_len_minus1);
       IN_RANGE_OR_RETURN(offset_len_minus1, 0, 31);
-      SKIP_BITS_OR_RETURN(num_entry_point_offsets * (offset_len_minus1 + 1));
+      base::CheckedNumeric<int> bits_to_skip = offset_len_minus1 + 1;
+      bits_to_skip *= num_entry_point_offsets;
+      if (!bits_to_skip.IsValid()) {
+        return kInvalidStream;
+      }
+      int bits_to_skip_val = bits_to_skip.ValueOrDie();
+      SKIP_BITS_OR_RETURN(bits_to_skip_val);
     }
   }
 
@@ -2022,6 +2037,8 @@
   IN_RANGE_OR_RETURN(pred_weight_table->luma_log2_weight_denom, 0, 7);
   if (sps.chroma_array_type) {
     READ_SE_OR_RETURN(&pred_weight_table->delta_chroma_log2_weight_denom);
+    IN_RANGE_OR_RETURN(pred_weight_table->delta_chroma_log2_weight_denom, -7,
+                       7);
     pred_weight_table->chroma_log2_weight_denom =
         pred_weight_table->delta_chroma_log2_weight_denom +
         pred_weight_table->luma_log2_weight_denom;
@@ -2117,22 +2134,43 @@
   // the parsed SEI messages, so we have to set a limit here.
   constexpr int kMaxParsedSEIMessages = 64;
   do {
-    int type = 0;
+    base::CheckedNumeric<int> type_checked = 0;
     READ_BITS_OR_RETURN(8, &byte);
     while (byte == 0xff) {
-      type += 255;
+      type_checked += 255;
       READ_BITS_OR_RETURN(8, &byte);
     }
-    type += byte;
+    type_checked += byte;
 
-    int payload_size = 0;
+    if (!type_checked.IsValid()) {
+      DVLOG(1) << "SEI type overflow";
+      return kInvalidStream;
+    }
+    int type = type_checked.ValueOrDie();
+
+    base::CheckedNumeric<int> payload_size_checked = 0;
     READ_BITS_OR_RETURN(8, &byte);
     while (byte == 0xff) {
-      payload_size += 255;
+      payload_size_checked += 255;
       READ_BITS_OR_RETURN(8, &byte);
     }
-    payload_size += byte;
-    int num_bits_remain = payload_size * 8;
+    payload_size_checked += byte;
+
+    if (!payload_size_checked.IsValid()) {
+      DVLOG(1) << "SEI payload size overflow";
+      return kInvalidStream;
Loading diff…

Original Bug Report

reported by [email protected]

Signed Integer overflow in H264 SEI Parsing

Steps to reproduce the problem

  1. Build chrome libfuzzer with below mentioned args
  2. execute the fuzzer with the attached malformed H264 file
  3. Signed Integer overflow crash detected

Problem Description

A signed integer overflow occurs in chromiums H264 parser while processing SEI. The crash happens when multiplying a large parsed value.

Vulnerable file details

media/parsers/h264_parser.cc
Function : media::H264Parser::ParseSEI(media::H264SEI*)
Line : 1549

Build Args:

gn gen out/ASanMedia --args='
is_asan=true 
is_ubsan_security=true 
is_debug=false 
is_component_build=false 
proprietary_codecs=true 
ffmpeg_branding="Chrome" 
symbol_level=1 
use_remoteexec=false
use_libfuzzer=true 
mac_sdk_min="26" 
angle_enable_metal=false
'

autoninja -C out/ASanMedia media_h264_parser_fuzzer

Execution args

./media_h264_parser_fuzzer h264_sei_overflow.h264

Attached a poc file which contains the crafted sei payload for h264 which results in large parsed value causing the integer overflow

Summary

Signed Integer overflow in H264 SEI Parsing

Custom Questions

Crash state:

Crash state:

INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 4038822869
INFO: Loaded 1 modules   (3274258 inline 8-bit counters): 3274258 [0x104c70000, 0x104f8f612), 
ASanMedia/media_h264_parser_fuzzer: Running 1 inputs 1 time(s) each.
Running: /h264_sei_overflow.h264
../../media/parsers/h264_parser.cc:1549:40: runtime error: signed integer overflow: 268435456 * 8 cannot be represented in type 'int'
    #0 0x0001046f02c4 in media::H264Parser::ParseSEI(media::H264SEI*) media/parsers/h264_parser.cc:1549:40
    #1 0x00010457a01c in LLVMFuzzerTestOneInput media/parsers/h264_parser_fuzzertest.cc:65:22
    #2 0x0001045b8438 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) third_party/libFuzzer/src/FuzzerLoop.cpp:619:13
    #3 0x00010458ca74 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) third_party/libFuzzer/src/FuzzerDriver.cpp:329:6
    #4 0x0001045946a0 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) third_party/libFuzzer/src/FuzzerDriver.cpp:864:9
    #5 0x00010457a81c in main third_party/libFuzzer/src/FuzzerMain.cpp:20:10
    #6 0x00018486dd50 in start (/usr/lib/dyld:arm64e+0x8d50)
*
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ../../media/parsers/h264_parser.cc:1549:40 
Executed /h264_sei_overflow.h264 in 364 ms
***
*** NOTE: fuzzing was not performed, you have only
***       executed the target code on a fixed set of inputs.

***

Reporter credit:

Mohammed Yasar B & Ameen Basha M K

Additional Data

Category: Security
Chrome Channel: Not sure
Regression: N/A \

View on issue tracker