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.
Tracker485115554
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
CreditedAmeen Basha M K & Mohammed Yasar B
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>`
a wrapper type that performs arithmetic while tracking overflow, exposing IsValid(), ValueOrDie(), and AssignIfValid() so callers can detect and reject out-of-range results.
SEI messages
Supplemental Enhancement Information units whose type and payload_size are encoded as a run of 0xff bytes plus a final byte, i.e. accumulated as 255 * n + last, parsed by ParseSEI in both h264_parser.cc and h265_parser.cc.
H.265 slice-header fields
delta_poc_msb_cycle_lt[], slice_qp_delta, num_entry_point_offsets, and entry-point offset_len_minus1, all read from the bitstream and combined arithmetically in ParseSliceHeader.
`IN_RANGE_OR_RETURN` / `SKIP_BITS_OR_RETURN`
macros that respectively enforce an inclusive range (returning failure otherwise) and advance the bitstream by a bit count; both can be fed overflowing expressions if the inputs are not pre-checked.

Root Cause Analysis

Several arithmetic combinations of attacker-controlled bitstream values in the H.264/H.265 parsers were performed with plain int, so a crafted stream could drive them to signed-integer overflow (undefined behavior) before any bounds check saw a sane value. In ParseSEI, the accumulators type += 255 / payload_size += 255 (over a long 0xff run) and num_bits_remain = payload_size * 8 could overflow int; the invariant that these counters stay representable was unenforced. In H.265 ParseSliceHeader, delta_poc_msb_cycle_lt[i] + delta_poc_msb_cycle_lt[i-1] (Equation 7-52), the num_entry_point_offsets upper-bound (num_tile_columns_minus1 + 1) * pic_height_in_ctbs_y - 1, and the skip count num_entry_point_offsets * (offset_len_minus1 + 1) could each overflow; additionally slice_qp_delta was validated by computing the potentially overflowing 26 + init_qp_minus26 + slice_qp_delta inside IN_RANGE_OR_RETURN. In ParsePredWeightTable, delta_chroma_log2_weight_denom was added to luma_log2_weight_denom without first bounding it.

The fix wraps each accumulation/product in base::CheckedNumeric<int> and returns kInvalidStream on !IsValid()/failed AssignIfValid, refactors 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 the addition — each guard runs before its value is used.

Key insight
The core mistake was accumulating and multiplying attacker-controlled bitstream quantities in plain int, permitting signed-overflow UB before validation; the fix routes those computations through base::CheckedNumeric<int> (and reorders the slice_qp_delta/delta_chroma_log2_weight_denom checks to bound raw inputs) so overflow is detected and the stream rejected.

Attack Path

  1. Craft a malformed H.264/H.265 stream The attacker builds NAL units with, for example, an SEI message using a long run of 0xff bytes so type/payload_size accumulate past INT_MAX, or an H.265 slice header with hostile num_tile_columns_minus1, pic_height_in_ctbs_y, offset_len_minus1, or delta_poc_msb_cycle_lt values.
  2. Victim decodes the media The stream reaches ParseSEI / ParseSliceHeader via a <video> element, MSE, or a media file in the media/GPU parsing path.
  3. Signed-integer overflow occurs (pre-patch) The plain-int accumulations or products overflow, which is undefined behavior and can yield a wrong (e.g. small or negative) num_bits_remain, limit, or skip count.
  4. Overflowed value used downstream A corrupted size/skip/limit could feed SKIP_BITS_OR_RETURN or subsequent bounds logic with an implausible value, causing mis-parsing. (inferred — the patch demonstrates the overflow but no specific OOB read/write, memory-corruption primitive, or escalation)

Impact Assessment

An attacker who supplies a decoded H.264/H.265 bitstream can provoke signed-integer overflow in the media bitstream parsers (media/GPU process context), with the only precondition that the victim decodes attacker-controlled media. The advisory rates bug 485115554 low severity (“Integer overflow in Media”) and reports no in-the-wild exploitation. The patch establishes only the integer-overflow/UB; any resulting out-of-bounds access or further impact is (inferred) and not demonstrated by the diff. (This commit fixes multiple related bugs — 485203821, 485115554, 485212874 — under one change.)

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

  • Unchecked accumulators over `0xff` runs
    Audit any parser loop of the form x += 255; ... x += last for missing overflow protection, across all SEI/NAL/extension-data readers.
  • Products of two parsed quantities
    Review multiplications like count * size, a * b - 1, or n * (len + 1) built from bitstream fields; these should use base::CheckedNumeric before feeding range checks or skip/allocation sinks.
  • Validation that computes the sum it is checking
    Prefer bounding the raw delta (as the slice_qp_delta/delta_chroma_log2_weight_denom fixes do) rather than evaluating base + delta inside IN_RANGE_OR_RETURN, where the intermediate can overflow before the check.
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]

Integer Overflow in H265 SEI Parsing

Steps to reproduce the problem

  1. Build Chrome libfuzzer with below mentioned args
  2. execute the fuzzer with attached malformed H265 file
  3. Integer Overflow Crash Detected

Problem Description

A signed integer overflow occurs in Chromium’s H.265 parser while processing SEI (Supplemental Enhancement Information) data. The overflow happens during multiplication of a large parsed value, which can lead to crash

Vulnerable File Details

media/parsers/h265_parser.cc
Function: media::H265Parser::ParseSEI
Line: 2144

Build Args:

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

autoninja -C out/Media media_h265_parser_fuzzer

Execution Args:

./media_h265_parser_fuzzer sei_overflow.h265

Attached a poc file, The file contains a crafted SEI payload with a large parsed field value designed to overflow during size calculation.

Summary

Integer Overflow in H265 SEI Parsing

Custom Questions

Crash state:

INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 4166765090
INFO: Loaded 1 modules   (2164628 inline 8-bit counters): 2164628 [0x630b9b2a4ac0, 0x630b9b4b5254), 
INFO: Loaded 1 PC tables (2164628 PCs): 2164628 [0x630b9b4b5258,0x630b9d5bcb98), 
./media_h265_parser_fuzzer: Running 1 inputs 1 time(s) each.
Running: /home/basha/Downloads/win_repro/sei_overflow.h265
../../media/parsers/h265_parser.cc:2144:40: runtime error: signed integer overflow: 268435456 * 8 cannot be represented in type 'int'
    #0 0x630b922e623c in media::H265Parser::ParseSEI(media::H265SEI*) media/parsers/h265_parser.cc:2144:40
    #1 0x630b8e0677b7 in LLVMFuzzerTestOneInput media/parsers/h265_parser_fuzzertest.cc:43:22
    #2 0x630b8e0ab27c in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) third_party/libFuzzer/src/FuzzerLoop.cpp:619:13
    #3 0x630b8e07c2b1 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) third_party/libFuzzer/src/FuzzerDriver.cpp:328:6
    #4 0x630b8e084da0 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) third_party/libFuzzer/src/FuzzerDriver.cpp:863:9
    #5 0x630b8e067fdd in main third_party/libFuzzer/src/FuzzerMain.cpp:20:10
    #6 0x7ef19742a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #7 0x7ef19742a28a in __libc_start_main csu/../csu/libc-start.c:360:3
    #8 0x630b8df8ab29 in _start (/home/basha/Desktop/chromefuzz/chromium/src/out/UBSanMedia/media_h265_parser_fuzzer+0x7c11b29) (BuildId: 851ee95e8c3708a1)

SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ../../media/parsers/h265_parser.cc:2144:40 
Executed /home/basha/Downloads/win_repro/sei_overflow.h265 in 275 ms
***
*** NOTE: fuzzing was not performed, you have only
***       executed the target code on a fixed set of inputs.
***

Reporter credit:

Ameen Basha M K & Mohammed Yasar B

Additional Data

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

View on issue tracker