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 WebRTC
DescriptionInteger overflow in WebRTC
ComponentWebRTC
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.
Tracker501881082
Fix commit5207e12e4866 (chromium/src) +36/-0
CWECWE-190 (Integer overflow)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedAshutosh
Reported2026-04-12 (per vendor advisory)
Fix landed2026-07-27 (public git)
Disclosed2026-08-25
Patch gap29 days — fix public in source before the advisory

Background

`RTCRtpEncodingParameters.maxBitrate`
a per-encoding upper bound, in bits per second, that a page sets for an outgoing RTP stream, either at creation through addTransceiver(kind, {sendEncodings}) or later through RTCRtpSender.setParameters().
`RTCRtpTransceiverInit`
the dictionary passed to RTCPeerConnection.addTransceiver(). Blink converts it with ToRtpTransceiverInit into a native webrtc init structure whose send_encodings go to the WebRTC engine.
`ExceptionState::ThrowRangeError`
the Blink mechanism for raising a JavaScript RangeError. The patched functions return right after calling it (std::nullopt / EmptyPromise()), so the invalid parameters are never passed on.
WebRTC bitrate configuration
the native WebRTC code (running in the renderer process) that uses each encoding’s max bitrate to configure encoders and bandwidth allocation.

Root Cause Analysis

Blink’s WebRTC bindings accepted maxBitrate: 0 in sendEncodings (for addTransceiver) and in encodings (for RTCRtpSender::setParameters). They passed it unchecked into the native WebRTC parameter structures. The advisory classifies the result as an integer overflow in WebRTC. The diff does not show the downstream arithmetic, but a zero maximum bitrate breaks the invariant that a configured bitrate cap is a positive value. Code that subtracts from, scales or divides by it can then wrap or produce out-of-range values (inferred).

The fix adds an up-front loop over the supplied encodings in both entry points: if any encoding hasMaxBitrate() with a value of 0, it throws a RangeError and returns early. In the addTransceiver path this check runs before ToRtpTransceiverInit and the existing send_encodings validation. In setParameters it runs before the promise resolver is created and before parameters are handed to the sender. The zero value therefore never reaches native WebRTC code.

Key insight
The Web API layer accepted a degenerate maxBitrate of zero and passed it to native bitrate code that does not handle it safely. The patch tightens input validation at both JavaScript entry points so zero is rejected with a RangeError before any conversion or native call.

Attack Path

  1. Page creates a peer connection ordinary web content constructs an RTCPeerConnection. No special permission is required for this (the regression tests use a plain new RTCPeerConnection()).
  2. Supply a zero bitrate cap the page calls addTransceiver('audio' or 'video', {sendEncodings: [{maxBitrate: 0}]}), or edits sender.getParameters().encodings[0].maxBitrate = 0 and calls setParameters().
  3. Value reaches native WebRTC before the fix, the encoding was converted and passed to the WebRTC sender configuration with a max bitrate of zero.
  4. Integer overflow in bitrate handling native rate-control or allocation arithmetic that operates on the zero cap overflows or wraps, corrupting derived values in the renderer (inferred; the specific sink is not in the diff).

Impact Assessment

The bug can be triggered from web content through standard WebRTC JavaScript APIs, and the integer overflow happens in WebRTC code in the renderer process. Any memory-safety consequence, such as out-of-bounds access driven by the wrapped value, or any route to code execution in the sandboxed renderer is not shown by the patch and is (inferred). The vendor advisory rates this low severity and does not report in-the-wild exploitation; NVD scores it CVSS 8.8.

Changed Functions

FunctionChangeNotes
for
third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
modified

Files Changed

  • third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-addTransceiver.https.html
  • third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-setParameters.html
  • third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
  • third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc

Audit Directions

  • Zero/degenerate numeric parameters crossing into native media code
    review other RTCRtpEncodingParameters and codec fields (scaleResolutionDownBy, maxFramerate, scalabilityMode-derived values, minBitrate interplay) that Blink forwards to WebRTC. Make sure zero, negative and extreme values are rejected at the IDL boundary or handled safely natively.
  • Validation parity across entry points
    when a parameter can be set both at creation (addTransceiver, addTrack) and later (setParameters), confirm every path runs the same validation. Also check the native WebRTC RtpSender::SetParameters checks, so non-Blink callers are covered too.
  • Arithmetic on bitrate/rate values
    audit WebRTC bitrate allocators and rate controllers for subtraction, scaling or division on configured caps that assume a positive value. Prefer checked arithmetic or clamping in those places.
From 5207e12e48669e9f2a924cc77d51bcaf57efbedf Mon Sep 17 00:00:00 2001
From: Tommi <tommi@chromium.org>
Date: Mon, 27 Jul 2026 05:33:07 -0700
Subject: [PATCH] Fix rtp sender max bitrate

Reject maxBitrate = 0 in setParameters and addTransceiver.

Bug: 501881082, 501879826
Change-Id: Ia2f70e2c561deff3ad00595a31089078f8417250
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8148539
Reviewed-by: Guido Urdaneta <guidou@chromium.org>
Commit-Queue: Tomas Gunnarsson <tommi@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1668537}
---
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
index c31760f..2256945 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
@@ -2031,6 +2031,15 @@
     ExceptionState& exception_state,
     const RTCRtpTransceiverInit* init,
     const String kind) {
+  if (init->hasSendEncodings()) {
+    for (const auto& encoding : init->sendEncodings()) {
+      if (encoding->hasMaxBitrate() && encoding->maxBitrate() == 0) {
+        exception_state.ThrowRangeError("maxBitrate must be greater than 0.");
+        return std::nullopt;
+      }
+    }
+  }
+
   auto webrtc_init = ToRtpTransceiverInit(execution_context, init, kind);
   // Validate sendEncodings.
   for (auto& encoding : webrtc_init.send_encodings) {
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
index 8d89499..e76fb187 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
@@ -822,6 +822,16 @@
     const RTCSetParameterOptions* options,
     ExceptionState& exception_state) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  if (parameters->hasEncodings()) {
+    for (const auto& encoding : parameters->encodings()) {
+      if (encoding->hasMaxBitrate() && encoding->maxBitrate() == 0) {
+        exception_state.ThrowRangeError("maxBitrate must be greater than 0.");
+        return EmptyPromise();
+      }
+    }
+  }
+
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver<IDLUndefined>>(
       script_state, exception_state.GetContext());
   auto promise = resolver->Promise();
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-addTransceiver.https.html b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-addTransceiver.https.html
index 1a81319c..ed04478 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-addTransceiver.https.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-addTransceiver.https.html
@@ -88,6 +88,14 @@
     assert_throws_js(TypeError, () => pc.addTransceiver('invalid'));
   }, 'addTransceiver() with string argument as invalid kind should throw TypeError');
 
+  test(t => {
+    const pc = new RTCPeerConnection();
+    t.add_cleanup(() => pc.close());
+    assert_throws_js(RangeError, () => pc.addTransceiver('audio', {
+      sendEncodings: [{ maxBitrate: 0 }]
+    }));
+  }, 'addTransceiver() with maxBitrate set to 0 should throw RangeError');
+
   /*
     5.1.  addTransceiver
       The initial value of mid is null.
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-setParameters.html b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-setParameters.html
index 3c03184..4c26c84 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-setParameters.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-setParameters.html
@@ -56,4 +56,13 @@
   }, `setParameters() with already used parameters should reject with InvalidStateError if the event loop has been relinquished`);
 }
 
+  promise_test(async t => {
+    const pc = new RTCPeerConnection();
+    t.add_cleanup(() => pc.close());
+    const { sender } = pc.addTransceiver('audio');
+    const param = sender.getParameters();
+    param.encodings[0].maxBitrate = 0;
+    await promise_rejects_js(t, RangeError, sender.setParameters(param));
+  }, `setParameters() with maxBitrate set to 0 should reject with RangeError`);
+
 </script>
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-addTransceiver.https.html b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-addTransceiver.https.html
index 1a81319c..ed04478 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-addTransceiver.https.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-addTransceiver.https.html
@@ -88,6 +88,14 @@
     assert_throws_js(TypeError, () => pc.addTransceiver('invalid'));
   }, 'addTransceiver() with string argument as invalid kind should throw TypeError');
 
+  test(t => {
+    const pc = new RTCPeerConnection();
+    t.add_cleanup(() => pc.close());
+    assert_throws_js(RangeError, () => pc.addTransceiver('audio', {
+      sendEncodings: [{ maxBitrate: 0 }]
+    }));
+  }, 'addTransceiver() with maxBitrate set to 0 should throw RangeError');
+
   /*
     5.1.  addTransceiver
       The initial value of mid is null.
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-setParameters.html b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-setParameters.html
index 3c03184..4c26c84 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-setParameters.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-setParameters.html
@@ -56,4 +56,13 @@
   }, `setParameters() with already used parameters should reject with InvalidStateError if the event loop has been relinquished`);
 }
 
+  promise_test(async t => {
+    const pc = new RTCPeerConnection();
+    t.add_cleanup(() => pc.close());
+    const { sender } = pc.addTransceiver('audio');
+    const param = sender.getParameters();
+    param.encodings[0].maxBitrate = 0;
+    await promise_rejects_js(t, RangeError, sender.setParameters(param));
+  }, `setParameters() with maxBitrate set to 0 should reject with RangeError`);
+
 </script>
Loading diff…

Original Bug Report

reported by as...@gmail.com

RTCRtpSender.setParameters accepts maxBitrate > UINT32_MAX (MAX_SAFE_INTEGER, -1) without TypeError. WebIDL unsigned long spec violation. uint32→int32 cast is undefined behavior in C++.


Report description

RTCRtpSender.setParameters accepts maxBitrate > UINT32_MAX (MAX_SAFE_INTEGER, -1) without TypeError. WebIDL unsigned long spec violation. uint32→int32 cast is undefined behavior in C++.


Bug location

Where do you want to report your vulnerability?

Chrome VRP – Report security issues affecting the Chrome browser. See program rules

Which URL (or repository) have you found the vulnerability in?

https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/modules/peerconnection/


The problem

Please describe the technical details of the vulnerability

Summary

RTCRtpSender.setParameters() accepts maxBitrate values exceeding the uint32 range without throwing TypeError, violating the WebIDL spec.

The WebRTC spec defines maxBitrate as WebIDL unsigned long, which maps to uint32_t (0 to 4,294,967,295). Per WebIDL §3.2.5, converting values outside this range should produce specific results:

  • Values > 2^32 should be modulo-reduced (ToUint32)
  • NaN should become 0
  • Infinity should become 0 (via ToUint32)

However, the actual behavior shows these values are silently accepted and the truncated/converted values are passed to the internal BitrateAllocator where max_bitrate_bps is stored as std::optional<int> (signed 32-bit).

Tested Values

Input WebIDL ToUint32 Stored as int32 Expected Actual
4294967295 (UINT32_MAX) 4294967295 OVERFLOW (UB) Accept Accept
4294967296 (UINT32_MAX+1) 0 0 Accept(0) Accept
MAX_SAFE_INTEGER 4294967295 OVERFLOW (UB) Accept(ff) Accept
Infinity 0 0 Accept(0) Accept
-1 4294967295 OVERFLOW (UB) Accept(ff) Accept

The critical issue is that std::optional<int> cannot hold 4,294,967,295. Storing UINT32_MAX in a signed int32 is undefined behavior in C++.

Root Cause

In third_party/webrtc/api/rtp_parameters.h:

  • max_bitrate_bps is std::optional<int> (signed 32-bit)
  • WebIDL produces uint32 values up to 4,294,967,295
  • Assigning uint32 > INT32_MAX to int is undefined behavior
  • UBSan would flag: “value 4294967295 is outside the range of representable values of type ‘int’”

Security Impact

  1. UndefinedBehavior in C++ can be exploited by compilers to optimize away safety checks
  2. The BitrateAllocator receives a corrupted max_bitrate value, potentially causing integer overflow in bandwidth calculations
  3. Multiple senders with UINT32_MAX maxBitrate: sum overflows both int32 and uint32 (3 × 4294967295 = 12,884,901,885)

Reproduction

  1. Open attached v7_webidl_violation_poc.html
  2. Wait for Phase 1 to complete
  3. Observe that values > UINT32_MAX are marked [!!!] ACCEPTED
  4. Phase 2 exercises these values with actual media flow

Suggested Fix

  1. Change max_bitrate_bps from std::optional<int> to std::optional<uint32_t>
  2. OR add range validation in Blink→WebRTC conversion: if (max_bitrate > INT32_MAX) reject with RangeError

Difference from CVE-2026-5912

CVE-2026-5912 is an integer overflow in SDP text parsing (ParseBandwidthLine). This finding is in the setParameters() API path, which is architecturally separate and was NOT addressed by the CVE-2026-5912 fix.

Form Field: Impact Analysis

The WebIDL unsigned long → C++ int32 type mismatch causes undefined behavior when maxBitrate values ≥ 2,147,483,648 are set via the JavaScript API.

Who can exploit: Any JavaScript running in a page with WebRTC access.

What they gain:

  1. Undefined behavior in the BitrateAllocator — compiler may optimize away subsequent safety checks
  2. Integer overflow when summing multiple senders’ max_bitrate values
  3. Combined with the setParameters(Infinity) policy bypass (separate report), provides two independent ways to corrupt bandwidth state

This is a defense-in-depth issue: the type mismatch (uint32→int32) should be fixed regardless of whether an exploitable crash results, as undefined behavior in C++ is a security anti-pattern.



#### Impact analysis

The WebIDL unsigned long to C++ int32 type mismatch causes undefined behavior when maxBitrate values &gt;= 2,147,483,648 are set via the JavaScript API.

Who can exploit: Any JavaScript running in a page with WebRTC access.

What they gain:
1. Undefined behavior in the BitrateAllocator — compiler may optimize away subsequent safety checks
2. Integer overflow when summing multiple senders' max_bitrate values (3 x 4294967295 = 12,884,901,885 — exceeds both int32 and uint32)
3. Combined with the setParameters(Infinity) policy bypass (separate report), provides two independent ways to corrupt bandwidth state

This is a defense-in-depth issue: the type mismatch (uint32 to int32) should be fixed regardless of whether an exploitable crash results, as undefined behavior in C++ is a security anti-pattern.



---

### The cause


#### What version of Chrome have you found the security issue in?

146.0.0.0 [stable] + 147.0.7727.55 [stable]


#### Is the security issue related to a crash?

No, it is not related to a crash.


#### Choose the type of vulnerability

Other


#### How would you like to be publicly acknowledged for your report?

Ashutosh
View on issue tracker