← Chrome — 2026-W02

28bb9dc0c36311c944d281be55295f635615dcdb  [Masonry] Remove CHECK of `grid_layout_data`

severity low class LogicError CVSS 3.1 confidence 0.6 signal score 3.8 third_party/blink/renderer AI review CONFIRMED
Primitive: A CHECK(grid_layout_data) added earlier was crashing renderers on real pages; null grid_layout_data is already handled by the following nullptr path.
Triage note: Removes an over-strict CHECK reachable from web content (masonry/grid layout), fixing a renderer DoS. CHECK aborts are controlled crashes, so security-relevant but not exploitable.

The bug at a glance

An earlier masonry CL (6925900) added CHECK(grid_layout_data) at the top of LayoutGrid::CollectTrackSizesForComputedStyle, and that CHECK was firing in the wild — CHECK aborts the renderer in all builds, so a reachable null grid_layout_data became a reliable renderer crash on real pages. The fix simply deletes the CHECK, because the very next line already handles null by returning early. Severity is low/watch: the impact is a renderer-process crash (denial of service scoped to the tab), the null state is reached by legitimate masonry/grid content rather than a crafted memory-corruption trigger, and there is no primitive beyond the abort. This is a reliability regression fix, not a memory-safety fix; the removed CHECK was the defect, and its removal restores the pre-existing graceful null handling.

The defender’s angle is reversed from a typical silent fix: nothing here makes memory safer, and the interesting fact is that a CHECK was crashing users. That CHECK reaching in the wild (Bug 472222702, ‘hitting in the wild’) tells an auditor that null grid_layout_data is genuinely reachable from web content during computed-style track-size collection — an invariant the masonry author wrongly assumed. For someone building a DoS against a target on the affected Stable range, new IntersectionObserver-free content that drives a masonry/grid element into the CHECK path was a one-line renderer kill. Post-fix the code falls through to the existing ‘if (!grid_layout_data) return track_sizes;’ so the null is absorbed. The angle is also a caution: CHECKs added on layout invariants that touch web-reachable style resolution are a recurring self-inflicted DoS source.

Root cause

LayoutGrid::CollectTrackSizesForComputedStyle(const GridLayoutData* grid_layout_data, GridTrackSizingDirection track_direction) collects the resolved track sizes for the computed-style (resolved value) API. A prior masonry change (CL 6925900) inserted CHECK(grid_layout_data) as the first statement. Immediately below it, the method already contained the tolerant path: it declares Vector<LayoutUnit, 1> track_sizes; then ‘if (!grid_layout_data) return track_sizes;’ — returning an empty vector when there is no layout data. The CHECK therefore contradicted the code directly beneath it: it turned a case the method was designed to handle gracefully into a fatal abort.

Because grid_layout_data can legitimately be null at this point (masonry/grid boxes whose layout data is absent when computed style is queried — e.g. before or without full layout, or for certain masonry configurations), the CHECK fired for ordinary users. CHECK, unlike DCHECK, is compiled into release builds and aborts the process, so every hit was a renderer crash. The fix deletes the single CHECK line, leaving the null branch to return the empty track_sizes vector as originally intended. The author explicitly notes no repro case was found and therefore no test was added, which is why this is unproven at the level of a specific web trigger but confirmed reachable by field crash reports.

Key code

The over-strict CHECK removed; the existing null branch below already handles it (layout_grid.cc)

 Vector<LayoutUnit, 1> LayoutGrid::CollectTrackSizesForComputedStyle(
     const GridLayoutData* grid_layout_data,
     GridTrackSizingDirection track_direction) {
-  CHECK(grid_layout_data);
   Vector<LayoutUnit, 1> track_sizes;
   if (!grid_layout_data)
     return track_sizes;

Patch walkthrough

  • third_party/blink/renderer/core/layout/grid/layout_grid.cc — A single line, CHECK(grid_layout_data), is deleted from the top of LayoutGrid::CollectTrackSizesForComputedStyle. The immediately following ‘if (!grid_layout_data) return track_sizes;’ already returns an empty result for the null case, so removing the CHECK restores graceful handling and stops the renderer abort. No other logic changes; no test added (author had no repro).

Background

CHECK vs DCHECK — CHECK is a release-build assertion that intentionally crashes the process when it fails; DCHECK is stripped in release. A CHECK on a reachable-null value is a guaranteed field crash, not a debug-only diagnostic.

GridLayoutData — Blink structure holding resolved grid/masonry track layout results. It may be absent (null) when computed-style track sizes are requested without corresponding layout data.

CollectTrackSizesForComputedStyle — Serves the resolved-value (getComputedStyle) query for grid-template track sizes; reachable from script that reads computed style of a grid/masonry element.

Masonry layout — An experimental CSS layout mode built atop the grid implementation; CL 6925900 was masonry work that introduced the offending CHECK.

Vulnerability window

  1. Pre-existing — CollectTrackSizesForComputedStyle handled null grid_layout_data by returning an empty vector.
  2. CL 6925900 — Masonry work added CHECK(grid_layout_data) at method entry, converting the tolerated null case into a fatal abort.
  3. In the wild — The CHECK began hitting real users (Bug 472222702), i.e. renderer crashes on pages that reach the method with null layout data.
  4. 2026-01-09 — Alison Maher lands 28bb9dc0c3 removing the CHECK to fall through to the existing null handling (CL 7418260, main@{#1566984}); no repro, no test.
  5. Backport — No [M###] tag on the subject; treat as trunk-only unless a separate stable merge is confirmed, though a field-hitting CHECK is a strong merge candidate.

Proof of concept

The author states ‘I don’t have a repro case, so there is no test added.’ No trigger is proven. Honest trigger recipe: the crash requires reaching CollectTrackSizesForComputedStyle with a null GridLayoutData, which field reports show happens for some grid/masonry element whose computed track sizes are queried (e.g. getComputedStyle on grid-template-columns/rows) before or without resolved layout data. Reproducing it reliably would require reconstructing that layout-data-absent state, which the Chromium author could not do. There is no exploit and no memory-safety primitive — the only outcome is a renderer abort (DoS).

Exploitation

  1. Reachability — Web-reachable in principle: getComputedStyle on a grid/masonry element can drive track-size collection, and field crashes confirm the null path is hit. But the exact input is unknown even to the vendor, so weaponization as a deterministic DoS is unproven.
  2. Impact ceiling — Outcome is a CHECK abort of the renderer process — a tab/DoS crash. There is no read/write, no type confusion, no controllable state; the removed CHECK never corrupted memory, it only aborted.
  3. Post-fix — After the fix the null simply returns an empty track-size vector, so there is nothing left to exploit on this path.

Detection & hunting

For defenders and detection engineers:

  • Renderer CHECK failure at CollectTrackSizesForComputedStyle — On affected builds, crash reports citing CHECK(grid_layout_data) in layout_grid.cc are exactly this bug; a spike correlates with grid/masonry content and computed-style queries.
  • Grid/masonry computed-style access patterns — If hunting the trigger, instrument getComputedStyle reads of grid-template properties on masonry elements and log when grid_layout_data is null at method entry.

Audit directions

  • CHECKs added in recent masonry/grid CLs — Review CL 6925900 and sibling masonry changes for other CHECK/CHECK_* assertions placed on layout data or computed-style paths; the same false invariant may exist elsewhere and be a latent field-crash/DoS.
  • Redundant assertions contradicting downstream null handling — Grep layout code for CHECK(x) immediately preceding ‘if (!x) return’, a pattern that signals an assertion added over already-tolerant code — each is a candidate DoS regression.

Before / after

Loading diff…