Low chrome Integer Overflow 📄 Reporter bug report 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInteger overflow in Media
DescriptionInteger overflow in Media
ComponentMedia
Bug ClassInteger Overflow
Tracker485212874
Fix commit7cf2bae3b068 (chromium/src) +82/-23
CISA KEVNot listed
CreditedAmeen Basha M K & Mohammed Yasar B
Disclosed2026-04-07

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
From 7cf2bae3b0688de45d5215e768ca987f2967fc46 Mon Sep 17 00:00:00 2001
From: Eugene Zemtsov <eugene@chromium.org>
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 <tmathmeyer@chromium.org>
Commit-Queue: Eugene Zemtsov <eugene@chromium.org>
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 am...@gmail.com

Integer Overflow in H265 Slice Header 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

An integer overflow exists in Chromium’s H.265 parser during slice header parsing. A crafted H.265 bitstream can trigger an overflow in arithmetic involving parsed slice parameters, resulting in crash

Vulnerable File Area:

media/parsers/h265_parser.cc
Function: media::H265Parser::ParseSliceHeader
Line: 1284

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 delta_poc_overflow.h265

A poc file with Crafted slice header values cause overflow during parsing is attached below

Summary

Integer Overflow in H265 Slice Header Parsing

Custom Questions

Crash state:

INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 4195144660
INFO: Loaded 1 modules   (2164628 inline 8-bit counters): 2164628 [0x5ba0205e9ac0, 0x5ba0207fa254), 
INFO: Loaded 1 PC tables (2164628 PCs): 2164628 [0x5ba0207fa258,0x5ba022901b98), 
./media_h265_parser_fuzzer: Running 1 inputs 1 time(s) each.
Running: /home/basha/Downloads/win_repro/delta_poc_overflow.h265
../../media/parsers/h265_parser.cc:1284:51: runtime error: signed integer overflow: 268435456 + 1879048192 cannot be represented in type 'int'
    #0 0x5ba01760b60a in media::H265Parser::ParseSliceHeader(media::H265NALU const&, media::H265SliceHeader*, media::H265SliceHeader*) media/parsers/h265_parser.cc:1284:51
    #1 0x5ba0133ac89d in LLVMFuzzerTestOneInput media/parsers/h265_parser_fuzzertest.cc:61:22
    #2 0x5ba0133f027c in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) third_party/libFuzzer/src/FuzzerLoop.cpp:619:13
    #3 0x5ba0133c12b1 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) third_party/libFuzzer/src/FuzzerDriver.cpp:328:6
    #4 0x5ba0133c9da0 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) third_party/libFuzzer/src/FuzzerDriver.cpp:863:9
    #5 0x5ba0133acfdd in main third_party/libFuzzer/src/FuzzerMain.cpp:20:10
    #6 0x7c06d182a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #7 0x7c06d182a28a in __libc_start_main csu/../csu/libc-start.c:360:3
    #8 0x5ba0132cfb29 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:1284:51 
Executed /home/basha/Downloads/win_repro/delta_poc_overflow.h265 in 184 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