← Chrome — 2026-W32

c775e69845919cec3857e5ed079cf5daddedfa8c  Add overflow guard to FetchDataLoaderAsString

severity low class IntegerOverflow CVSS 5.3 signal score 5.5 third_party/blink/renderer/core/ AI review CONFIRMED
Structural signals: msg_medium (+2.0), msg_soft (+1.0), area:third_party/blink/renderer/core/ (+2.5)

The bug at a glance

Web-reachable renderer crash (DoS): reading a very large fetch() body as text appended into a StringBuilder without an overflow guard, so a 1+GB decoded body overflowed the builder length and crashed the renderer. Impact is a crash requiring ~1GB of data, with no corruption primitive shown; the fix rejects instead of crashing.

Any origin can serve or stream a large response and call Response.text()/Body text consumption to crash the victim renderer; a defender cares about the unauthenticated, easily reproducible DoS and the general class of unguarded StringBuilder growth.

Root cause

FetchDataLoaderAsString appended each decoded chunk (and the final Flush()) directly into builder_ with no length check. When the accumulated decoded string exceeds StringBuilder’s capacity (around/above 1GB), the length computation overflows and the renderer crashes. The patch adds an Append() helper that first calls builder_.DoesAppendCauseOverflow(length) and, on overflow, cancels the consumer and reports DidFetchDataLoadFailed() (rejecting the promise) instead of appending, mirroring the existing guard in FetchDataLoaderAsArrayBuffer.

Key code

Overflow pre-check before appending to the StringBuilder; failure rejects the load

[[nodiscard]] bool Append(const String& string) {
  if (builder_.DoesAppendCauseOverflow(string.length())) {
    return false;
  }
  builder_.Append(string);
  return true;
}

Patch walkthrough

  • third_party/blink/renderer/core/fetch/fetch_data_loader.cc — Introduces a [[nodiscard]] Append() helper that checks StringBuilder::DoesAppendCauseOverflow before appending, and updates both the per-chunk Decode path and the final Flush path to, on overflow, end-read/cancel the consumer and call DidFetchDataLoadFailed() instead of crashing. This converts an over-large text body from a renderer crash into a clean promise rejection.
  • third_party/blink/web_tests/wpt_internal/fetch/response-text-too-large-manual.html — Adds a manual test streaming >1GiB into Response.text() and asserting it rejects with TypeError rather than crashing, documenting the threshold and expected behavior.

Background

FetchDataLoaderAsString — Consumes a fetch body stream, decodes bytes to text, and accumulates them in a WTF::StringBuilder for Response.text()/Body.text().

StringBuilder::DoesAppendCauseOverflow — Checks whether adding N characters would exceed the builder’s representable length before performing the append.

BytesConsumer — Blink abstraction that supplies body bytes incrementally and supports Cancel/EndRead.

Vulnerability window

  1. Reachability — Script fetches a large/streamed response and calls .text() (or Response(stream).text()).
  2. Trigger — Decoded characters accumulate in builder_ until the total exceeds StringBuilder capacity (~1GB).
  3. Primitive — Unguarded length growth overflows/over-allocates and crashes the renderer (DoS); no controllable corruption shown (inferred).
  4. Escalation — Not established beyond denial of service (inferred).

Proof of concept

Conceptually, build a ReadableStream that enqueues more than 1GiB of bytes and call new Response(stream).text(); pre-patch this crashed the renderer, post-patch the promise rejects with a TypeError. See the added manual WPT for the structure.

Exploitation

  1. Deliver — Serve or stream a >1GB body to a page that reads it as text.
  2. Crash — Trigger the unguarded StringBuilder growth to crash the renderer process (DoS).

Detection & hunting

For defenders and detection engineers:

  • grep/weggli for StringBuilder::Append on attacker-sized data without a preceding DoesAppendCauseOverflow/length check — Hunt accumulation loops that grow a StringBuilder from network/stream input without an overflow guard.
  • renderer crash reports in FetchDataLoader / StringBuilder::Append — Correlate OOM/length-overflow crashes with large text() consumption.

Audit directions

  • Other FetchDataLoader variants and body consumers in core/fetch — Confirm each accumulation path (string, blob, form-data) guards against size overflow like AsArrayBuffer does.
  • StringBuilder accumulation of web-controlled data blink-wide — Audit large-input append loops for missing DoesAppendCauseOverflow checks.

Before / after

Loading diff…