1530d752113bd06baecf849783df7ae1eea4fc1a Fix crash in flex and grid overlays
Triage note: Adds nullptr / DynamicTo guards. Reachable only when the inspector highlight (flex/grid overlay) is active via DevTools, so it is a controlled crash (DoS) rather than corruption; not exploitable.
The bug at a glance
ContentLayoutBoxFromNode() unconditionally did To(node->GetLayoutObject())->ContentLayoutBox(), which crashes when the node has no layout object (null deref) or when the layout object is not a LayoutBlock (bad down-cast). Both conditions are real and reachable, but only through DevTools flex/grid overlay rendering — the code lives in core/inspector/inspector_highlight.cc, which runs when a developer inspects an element or the CDP Overlay domain is driven. That inspector gating, plus the fact that To<> is DCHECK-guarded in debug and the worst release outcome is a renderer crash rather than a controllable primitive, keeps this at low/watch rather than patch-now. It is a genuine memory-safety defect (isSecurity=true) but not attacker-weaponizable against ordinary browsing.
The defender’s angle is the DevTools/CDP attack surface, not the open web. This crash fires inside inspector highlight path building, so it matters to anyone who exposes a DevTools/CDP endpoint (automation harnesses, remote-debugging setups, embedded WebViews with inspection enabled, or an attacker who can drive the Overlay domain). The old code assumed every node handed to the overlay had a LayoutBlock; the two ‘overlooked’ call sites actually accept a nullptr box. The interesting artifact is the To down-cast: in release, To<> is effectively an unchecked static_cast, so feeding a non-LayoutBlock layout object (e.g. a LayoutInline or a replaced/box that is not a block) would have reinterpret-cast it and then called ->ContentLayoutBox() on the wrong type — a type-confusion read, which the fix eliminates by switching to DynamicTo<>.
Root cause
ContentLayoutBoxFromNode(Node* node) is a helper used while building flex and grid inspector overlays. The original body was: auto* block = To(node->GetLayoutObject())->ContentLayoutBox(); DCHECK(block); return block;. This has two distinct defects. First, node->GetLayoutObject() can return null (the node is display:none, not yet laid out, or detached), and To(nullptr) followed by ->ContentLayoutBox() is a null-pointer dereference. Second, when a layout object exists but is not a LayoutBlock, To in a release build performs an unchecked cast (its type check is a DCHECK), so ContentLayoutBox() is invoked through a mis-typed pointer — a type-confusion access rather than a clean failure.
The fix restructures the helper to handle both cases the callers were already expecting to pass: it first fetches layout_object and returns nullptr if it is null; then it uses DynamicTo, which returns nullptr on a type mismatch instead of blindly casting; only inside the successful cast does it call ContentLayoutBox() and keep the DCHECK on the resulting box. Any non-block layout object now returns nullptr. The commit message (‘Overlooked two instances where we actually handle and expect a nullptr layout box’) confirms the callers already tolerate nullptr, so the previous unconditional cast was the bug.
Key code
Unconditional To cast+deref replaced with null and type checks (inspector_highlight.cc)
- auto* block = To<LayoutBlock>(node->GetLayoutObject())->ContentLayoutBox();
- DCHECK(block) << "Node " << node->nodeName() << " has no content layout box.";
- return block;
+ LayoutObject* layout_object = node->GetLayoutObject();
+ if (!layout_object) {
+ return nullptr;
+ }
+ if (auto* block = DynamicTo<LayoutBlock>(layout_object)) {
+ auto* content_layout_box = block->ContentLayoutBox();
+ DCHECK(content_layout_box)
+ << "Node " << node->nodeName() << " has no content layout box.";
+ return content_layout_box;
+ }
+ return nullptr;
Patch walkthrough
third_party/blink/renderer/core/inspector/inspector_highlight.cc— ContentLayoutBoxFromNode is rewritten. It now (1) reads node->GetLayoutObject() into a local and returns nullptr if null, eliminating the null deref; (2) uses DynamicTo instead of To, so a non-block layout object yields nullptr instead of an unchecked cast; (3) only dereferences ContentLayoutBox() on a confirmed LayoutBlock, keeping the DCHECK on the content box for debug diagnostics. The two flex/grid overlay call sites (referenced by Fixed: 471584872, 471557450) already handle the nullptr return.
Background
To vs DynamicTo — Blink’s casting helpers. To asserts the type with a (debug-only) DCHECK and otherwise casts unconditionally — in release it is essentially static_cast, so a wrong type is undefined behavior. DynamicTo checks the type at runtime in all builds and returns nullptr on mismatch.
inspector_highlight.cc — Blink code that computes the geometry/paths for DevTools element highlights and flex/grid overlays. It runs only when inspection/overlay features are active (DevTools open or CDP Overlay domain driven).
ContentLayoutBox() — A LayoutBlock accessor returning the content box used to lay out children; the overlay code uses it to draw flex/grid line and item boundaries.
LayoutBlock — The layout-tree class for block-level boxes. Not every layouted node is a LayoutBlock (e.g. inline, table, or replaced content), which is why an unchecked cast is unsafe.
Vulnerability window
- Introduced — ContentLayoutBoxFromNode shipped with an unconditional To cast and deref, assuming every highlighted node is a laid-out block.
- Reported — Two internal bugs (471584872, 471557450) captured crashes in flex and grid overlays where a node had no layout object or a non-block one.
- 2026-01-07 — Alex Rudenko lands 1530d75211 adding null and DynamicTo<> guards (CL 7408409, main@{#1565513}).
- Backport — No [M###] tag on the subject; treat as trunk-only unless a separate merge is confirmed.
Proof of concept
The commit ships no test, so no trigger is provided by the author. Honest trigger recipe: open DevTools (or drive the CDP Overlay.setShowFlexOverlays / setShowGridOverlays domain) on a document containing an element styled display:flex or display:grid whose relevant node resolves to either no layout object (e.g. a child made display:none, or a not-yet-laid-out node) or a layout object that is not a LayoutBlock. In an affected build the overlay path calls ContentLayoutBoxFromNode and crashes on the null deref or mis-typed cast. No exploit beyond a renderer crash is demonstrated, and the type-confusion cast is not shown to yield attacker-controlled memory access.
Exploitation
- Reachability — Blocked from ordinary web content: the vulnerable helper only runs for DevTools/CDP overlays. An attacker must be able to trigger inspector highlighting of a crafted element — realistic only in automation/remote-debugging or WebView-with-inspection scenarios, not a drive-by page.
- Primitive — Null-object path is a clean null deref (DoS). The non-block cast path is a type confusion, but it is an immediate read through a mis-typed pointer into ContentLayoutBox()’s vtable/fields; there is no shown mechanism to control the confused object’s contents, so it is best characterized as an uncontrolled crash, not a corruption primitive.
- Debug vs release — In DCHECK builds the To<> cast would have asserted, catching the type mismatch; only release builds reach undefined behavior, which further limits real-world exploitability.
Detection & hunting
For defenders and detection engineers:
- Renderer crash in inspector_highlight ContentLayoutBoxFromNode — Crash reports with frames through InspectorHighlight / flex or grid overlay path building and a null or mis-typed LayoutBlock access indicate this bug. Correlate with DevTools/CDP Overlay activity.
- CDP Overlay usage on untrusted content — In automation fleets, alert when Overlay.setShowFlexOverlays/setShowGridOverlays is invoked on attacker-influenced DOM, which is the only realistic reach for this crash.
Audit directions
- inspector_highlight.cc To<> casts — Grep the inspector/overlay code for other To/To casts of node->GetLayoutObject() results that lack null or DynamicTo guards; the same ’every highlighted node is a block’ assumption may persist elsewhere.
- Node-to-layout casts in CDP-reachable code — Broaden to any inspector/CDP-driven code that casts GetLayoutObject() output. DevTools-reachable type casts are a recurring source of renderer crashes and deserve DynamicTo<> hygiene review.