← Chrome — 2026-W32

53e38bbfa7c649f774fa753a19e13a1e66e9d160  [M144-LTS] mojo: don't overflow computation of channel read buffer size

severity medium class IntegerOverflow CVSS 4.8 signal score 7.69 mojo/ AI review CONFIRMED
Structural signals: null_guard (+2.59), msg_medium (+2.0), area:mojo/ (+1.8), surgical (+1.3)

The bug at a glance

The patch adds a CheckAdd overflow guard to the Mojo channel read-buffer size computation; the author states on the bug they are fairly confident it is not exploitable, and the change is defensive hardening on an IPC-reachable size calculation rather than a demonstrated corruption.

A compromised or malicious endpoint controlling the advertised next-read size could drive num_bytes + size_ to wrap a size_t, so a defender cares whether the wrapped (small) size then leads to an undersized buffer and a subsequent OOB write when the channel copies message bytes in.

Root cause

ReadBuffer::Reserve(num_bytes) grew the buffer via size_ = max(size_ * kGrowthFactor, num_occupied_bytes_ + num_bytes) without checking that num_bytes + size_ fits in size_t. A sufficiently large attacker-influenced num_bytes could overflow the addition, yielding a bogus (wrapped) size and an under-allocated buffer. The fix computes base::CheckAdd(num_bytes, size_) and returns nullptr on overflow; each platform read loop (fuchsia/posix/win) now treats a null buffer as a malformed/overflowed message and breaks the connection with a validation error instead of proceeding.

Key code

Reject read-buffer growth when the new-size addition overflows size_t

+    const auto new_size = base::CheckAdd(num_bytes, size_);
+    if (!new_size.IsValid()) {
+      return nullptr;
+    }
     if (num_occupied_bytes_ + num_bytes > size_) {

Patch walkthrough

  • mojo/core/channel.cc — Adds the CheckAdd overflow guard in ReadBuffer::Reserve and documents that it returns nullptr on overflow, preventing a wrapped size from producing an undersized allocation.
  • mojo/core/channel_posix.cc — Checks the GetReadBuffer() return for null (overflow) and, if so, sets read_error/validation_error and breaks, turning a potential OOB condition into a clean connection teardown.
  • mojo/core/channel_fuchsia.cc — Same null-buffer handling as posix in the Fuchsia read loop.
  • mojo/core/channel_win.cc — On a null buffer, calls OnError(kDisconnected) and returns, aborting the read rather than using a bad buffer.

Background

Mojo channel — The low-level transport carrying serialized IPC messages between Chromium processes.

ReadBuffer::Reserve — Ensures contiguous space for incoming bytes and returns the first writable address, growing the buffer as needed.

base::CheckAdd — Checked arithmetic helper that flags overflow instead of silently wrapping.

next_read_size / GetReadBuffer — The requested read capacity passed down per read iteration, influenced by prior message framing.

Vulnerability window

  1. Reachability — A peer process writes to a Mojo channel, driving the read loop to request additional buffer space (inferred).
  2. Trigger — num_bytes + size_ overflows size_t in Reserve, producing a wrapped size.
  3. Primitive — Pre-fix, an undersized buffer could be returned, enabling an out-of-bounds write of subsequent bytes (inferred, and per author likely not reachable).
  4. Escalation — Any heap corruption would need shaping into a useful primitive within the receiving process (inferred).

Proof of concept

Conceptually, a peer would advertise/stream read sizes engineered so the running size plus the next request wraps size_t; the overflow path is now rejected, and the author assesses the pre-fix path as likely unreachable, so no working trigger is provided.

Exploitation

  1. Control read size — Attacker-controlled endpoint influences num_bytes across read iterations toward the size_t boundary (inferred).
  2. Force overflow — Make num_bytes + size_ wrap so an undersized buffer is produced (inferred).
  3. Overwrite — Subsequent byte copy writes past the undersized allocation, corrupting adjacent heap (inferred, contested by author).

Detection & hunting

For defenders and detection engineers:

  • weggli/grep for size growth patterns like ‘size_ = std::max(… * kGrowthFactor, x + num_bytes)’ without a checked add — Audit buffer-grow routines that add attacker-influenced lengths to an existing size without CheckAdd/CheckMul.
  • grep for GetReadBuffer( callers that ignore a possible null return — Ensure every transport read loop handles the newly-possible null (overflow) return.

Audit directions

  • mojo/core and other IPC transports (NodeChannel, data pipe buffers) — Look for other size/offset arithmetic on externally-supplied lengths lacking checked-arithmetic guards.
  • Callers of GetReadBuffer across platforms — Confirm no remaining platform or variant proceeds with a null buffer after this change.

Before / after

Loading diff…