From 4bbc5e0950062e28a9e5c6b0e7c2c5f5b6eb8463 Mon Sep 17 00:00:00 2001 From: sideshowbarker Date: Thu, 18 Jun 2026 18:06:43 +0900 Subject: [PATCH] LibWeb: Fix crash from SVG resource boxes outliving removed element MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Crash when removing a , , or element that’s referenced via url(#id), and then GC’ing it. Cause: , , and are laid out as resource boxes attached to the referencing element's layout subtree, rather than their own. So those survive cleanup of their DOM ancestor. Once a removed // is collected, its resource box’s weak DOM node pointer is null. And dereferencing that trips an assert — both while painting and while tearing down the layout tree. Fix: When a , , or is removed, request a full layout-tree update while the element’s still alive. That drops the stale resource boxes (whose url(#id) references no longer resolve) before the node gets collected. Fixes https://github.com/LadybirdBrowser/ladybird/issues/10127 --- Libraries/LibWeb/SVG/SVGElement.cpp | 6 ++++++ .../SVG/clip-path-reference-removed-and-gced.html | 14 ++++++++++++++ .../Crash/SVG/mask-reference-removed-and-gced.html | 14 ++++++++++++++ .../SVG/pattern-reference-removed-and-gced.html | 14 ++++++++++++++ 4 files changed, 48 insertions(+) create mode 100644 Tests/LibWeb/Crash/SVG/clip-path-reference-removed-and-gced.html create mode 100644 Tests/LibWeb/Crash/SVG/mask-reference-removed-and-gced.html create mode 100644 Tests/LibWeb/Crash/SVG/pattern-reference-removed-and-gced.html diff --git a/Libraries/LibWeb/SVG/SVGElement.cpp b/Libraries/LibWeb/SVG/SVGElement.cpp index 25412932af..10feec7bf5 100644 --- a/Libraries/LibWeb/SVG/SVGElement.cpp +++ b/Libraries/LibWeb/SVG/SVGElement.cpp @@ -269,6 +269,12 @@ void SVGElement::removed_from(IsSubtreeRoot is_subtree_root, Node* old_ancestor, return; remove_from_use_element_that_reference_this(); + + // A , , or referenced via url(#id) is laid out as a resource box attached to the + // referencing element's layout subtree. So it outlives this element's own DOM node. Rebuild the layout tree + // while this element is still alive — so those stale resource boxes are dropped before this node is collected. + if (id().has_value()) + document().set_needs_full_layout_tree_update(true); } void SVGElement::remove_from_use_element_that_reference_this() diff --git a/Tests/LibWeb/Crash/SVG/clip-path-reference-removed-and-gced.html b/Tests/LibWeb/Crash/SVG/clip-path-reference-removed-and-gced.html new file mode 100644 index 0000000000..61c5e0da8f --- /dev/null +++ b/Tests/LibWeb/Crash/SVG/clip-path-reference-removed-and-gced.html @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/Tests/LibWeb/Crash/SVG/mask-reference-removed-and-gced.html b/Tests/LibWeb/Crash/SVG/mask-reference-removed-and-gced.html new file mode 100644 index 0000000000..ac618bb201 --- /dev/null +++ b/Tests/LibWeb/Crash/SVG/mask-reference-removed-and-gced.html @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/Tests/LibWeb/Crash/SVG/pattern-reference-removed-and-gced.html b/Tests/LibWeb/Crash/SVG/pattern-reference-removed-and-gced.html new file mode 100644 index 0000000000..646b5e884f --- /dev/null +++ b/Tests/LibWeb/Crash/SVG/pattern-reference-removed-and-gced.html @@ -0,0 +1,14 @@ + + + + + + + +