Chrome · Media
CVE-2026-5910
Integer Overflow in Media
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
whilemedia/parsers/h264_parser.cc |
modified | |
ifmedia/parsers/h265_parser.cc |
modified | |
whilemedia/parsers/h265_parser.cc |
modified |
Files Changed
media/parsers/h264_parser.ccmedia/parsers/h265_parser.cc
Patch
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 Slice Header Parsing
Steps to reproduce the problem
- Build Chrome libfuzzer with below mentioned args
- execute the fuzzer with attached malformed H265 file
- 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 \
References
On This Page