← Chrome — 2026-W04

0a75e6a832b16370dcaaf5f4fbcc9ecbed6103b8  Fix max bytes overflow in GeneratedCodeCacheContext initialization

severity low class IntegerOverflow CVSS 3.3 confidence 0.8 signal score 4.3 content/browser/code_cache AI review CONFIRMED
Primitive: max_bytes*kMaxWebUIPercent/100 overflows int when --disk-cache-size exceeds INT_MAX/2; fixed with int64 saturated_cast.
Triage note: Real signed-overflow/UB, but only reachable via the local --disk-cache-size command-line switch, not attacker-controllable from the web. Impact is a bad cache-size value, not memory corruption.

The bug at a glance

This is a signed-integer overflow in an arithmetic expression, but it is reachable only when the browser is started with an attacker-uncontrolled command-line flag (–disk-cache-size) set above INT_MAX/2 (~1.07 GB). There is no web-content path to influence max_bytes, so the trigger requires local control of how the browser is launched, which already implies a higher privilege than the bug grants. The consequence is at worst a mis-sized code-cache budget (a wrong, possibly negative, split between WebUI and open-web bytecode quotas), not memory corruption. Watch, not patch-now: real signed-overflow UB corrected defensively, but gated behind a local launch flag with no demonstrated memory-safety or web-reachable impact.

GeneratedCodeCacheContext computes how much of the on-disk code cache to reserve for WebUI JavaScript by multiplying the total cache budget (max_bytes, an int) by kMaxWebUIPercent (2) before dividing by 100. When –disk-cache-size pushes max_bytes above INT_MAX/2, the multiply overflows a 32-bit int and produces undefined behavior / a wrong (potentially negative) value that then flows into the cache-size accounting. The fix widens the multiply to int64 and saturates back to int, so an oversized cache budget can no longer overflow the quota computation.

Root cause

In GeneratedCodeCacheContext initialization the code splits the total code-cache budget between WebUI-origin bytecode and open-web bytecode. The pre-patch line computes max_bytes * kMaxWebUIPercent / 100 where max_bytes is a plain int and kMaxWebUIPercent is 2. The multiplication max_bytes * 2 is performed in 32-bit signed arithmetic, so once max_bytes exceeds INT_MAX/2 (about 1.07e9) the product overflows the range of int. Signed integer overflow is undefined behavior in C++, and in practice yields a wrapped, often negative, value. That value is then passed through std::min(..., disk_cache::kMaxWebUICodeCacheSize); a negative left operand of std::min propagates as the chosen minimum, and the subsequent max_bytes_js = max_bytes - max_bytes_webui_js computes the open-web quota from a nonsensical WebUI quota.

The root cause is that the intermediate product was allowed to exceed 32-bit range while the source operand (max_bytes) can be set arbitrarily large through the --disk-cache-size command-line switch. The fix promotes max_bytes to int64_t before multiplying, performs the * kMaxWebUIPercent / 100 in 64-bit space where no overflow is possible for any int-valued max_bytes, then uses base::saturated_cast<int> to clamp the result back into int range before the std::min. Saturated cast means an out-of-range positive product becomes INT_MAX rather than wrapping, and the subsequent std::min with kMaxWebUICodeCacheSize still bounds it to the intended ceiling.

The practical security relevance is thin: the arithmetic feeds cache-sizing accounting, not an allocation length or an index, and the input is a launch flag rather than web content. The value being corrected is the reason this is tagged security-relevant (undefined behavior on a size computation) rather than a pure cleanup, but nothing in the diff shows a corrupted length reaching an allocator or buffer.

Key code

The overflowing 32-bit multiply replaced by a 64-bit multiply with saturated_cast back to int

-        max_bytes_webui_js = std::min(max_bytes * kMaxWebUIPercent / 100,
-                                      disk_cache::kMaxWebUICodeCacheSize);
+        max_bytes_webui_js =
+            std::min(base::saturated_cast<int>(static_cast<int64_t>(max_bytes) *
+                                               kMaxWebUIPercent / 100),
+                     disk_cache::kMaxWebUICodeCacheSize);

Patch walkthrough

  • content/browser/code_cache/generated_code_cache_context.cc — Adds the include for base/numerics/safe_conversions.h and rewrites the WebUI-quota computation so max_bytes is cast to int64_t before the * kMaxWebUIPercent / 100 multiply, then saturated_cast clamps the (now overflow-free) result before std::min bounds it to kMaxWebUICodeCacheSize. This removes the signed-overflow UB and guarantees a sane, non-negative WebUI quota for any legal int value of max_bytes.

Background

GeneratedCodeCacheContext — Browser-process object that owns the on-disk cache for compiled JavaScript/WASM bytecode. It splits its total byte budget between WebUI-origin code (chrome:// pages) and open-web code, reserving only a small percentage (kMaxWebUIPercent = 2%) for WebUI.

–disk-cache-size — A command-line switch that overrides the browser’s computed disk-cache budget. It is set at launch (e.g. in a shortcut, script, or enterprise policy), not by web content, and can be given values far larger than INT_MAX/2, which is what makes the overflow reachable.

base::saturated_cast — A numeric cast that clamps out-of-range values to the destination type’s min/max instead of wrapping. Here it converts the 64-bit product back to int, turning any oversized positive result into INT_MAX rather than a wrapped negative value.

Signed integer overflow (UB) — In C++ the result of a signed integer operation that exceeds the type’s range is undefined behavior; compilers may assume it never happens and optimize accordingly, and in practice the value wraps. Correcting it by widening the intermediate type is standard hardening.

Vulnerability window

  1. Introduced — The WebUI/open-web quota split was written with a 32-bit max_bytes * kMaxWebUIPercent / 100 expression, which is safe only while max_bytes stays below INT_MAX/2.
  2. Reachable — Any launch that passes –disk-cache-size above ~1.07 GB drives max_bytes over INT_MAX/2, overflowing the multiply during code-cache context init.
  3. Fixed — Commit 0a75e6a832 (Jan 22 2026, main@{#1573086}) widens the multiply to int64 and saturates back to int, eliminating the overflow.

Proof of concept

The commit shipped no test. An honest trigger recipe: launch Chrome with --disk-cache-size=2200000000 (above INT_MAX/2) and reach GeneratedCodeCacheContext initialization; the pre-patch max_bytes * 2 overflows 32-bit int and yields a wrong/negative WebUI quota. This is observable only as mis-sized cache accounting (and UB under UBSan), not as a crash or memory corruption. No exploit is demonstrated and none is claimed; the input is a local launch flag, so there is no web-content trigger.

Exploitation

  1. Reach the code path — Requires launching the browser with a large –disk-cache-size. An attacker who can already control the browser command line has more direct capabilities than this bug provides, so the practical attack surface is near zero.
  2. Turn overflow into impact — The overflowed value feeds cache byte-budget accounting, not an allocation size or buffer index in the diff. At most it mis-splits the code-cache quota; no path to memory corruption is shown.
  3. Weaponization — None demonstrated. This is defensive correction of undefined behavior on a size computation, not an exploitable primitive.

Detection & hunting

For defenders and detection engineers:

  • Anomalous launch flags — Flag processes launched with –disk-cache-size set above ~2 GB (or above INT_MAX/2 ~1073741823), which is the only known trigger for the pre-patch overflow.
  • UBSan/sanitizer builds — On instrumented builds, signed-integer-overflow reports in generated_code_cache_context.cc during code-cache init indicate the vulnerable expression is being exercised.

Audit directions

  • content/browser/code_cache and disk_cache size math — Grep for max_bytes and percentage/fraction computations (e.g. * k...Percent / 100) done in int before widening; look for other budget splits that multiply an int cache size by a factor before dividing.
  • Command-line-fed size values — Audit all consumers of –disk-cache-size and similar switches for arithmetic performed in 32-bit before bounds-checking or before flowing into allocation/quota logic; prefer int64 intermediates plus saturated_cast.

Before / after

Loading diff…