Branch: refs/heads/main
  Home:   https://github.com/WebKit/WebKit
  Commit: 9414a9722a8439557c0c3972eec476dc922a1566
      
https://github.com/WebKit/WebKit/commit/9414a9722a8439557c0c3972eec476dc922a1566
  Author: Ryosuke Niwa <[email protected]>
  Date:   2026-06-12 (Fri, 12 Jun 2026)

  Changed paths:
    M Source/WebCore/dom/ContainerNodeAlgorithms.cpp
    M Source/WebCore/dom/Node.cpp
    M Source/WebCore/dom/Node.h

  Log Message:
  -----------
  REGRESSION: Use-after-free in Node::m_shadowIncludingRoot via destructor 
cascade not propagating into shadow roots
https://bugs.webkit.org/show_bug.cgi?id=312712
rdar://175103172

Reviewed by Geoffrey Garen.

When a document is torn down via Document::removedLastRef through 
removeDetachedChildrenInContainer,
the <html> element is removed from the document. Since <html> is still in tree 
scope at this point,
notifyChildNodeRemoved is called, which walks the entire subtree - including 
shadow roots - and sets
m_shadowIncludingRoot to <html> for all descendants. This is correct at that 
moment.

Then <html> is freed when the loop's RefPtr releases it (children don't 
ref-count their parents -
m_parentNode is CheckedPtr). This triggers a destructor cascade: 
~ContainerNode(<html>) via
removeDetachedChildrenInContainer(<html>) processes <body>, then 
~ContainerNode(<body>) processes
the <video> element, and so on. Each step calls resetShadowIncludingRoot() on 
the direct child,
fixing that node's cache. However, since IsConnected and IsInShadowTree flags 
were cleared during
the initial notifyChildNodeRemoved walk, isInTreeScope() returns false, so 
notifyChildNodeRemoved
is skipped in this case. This means the shadow root and its descendants are 
never updated -
m_shadowIncludingRoot of these nodes still point to the now-freed <html>.

Nodes kept alive by mechanisms other than JS wrappers - such as 
HTMLMediaElement which survives as
an ActiveDOMObject - retain their shadow DOM with dangling 
m_shadowIncludingRoot pointers. When
these nodes are subsequently used (e.g., VTT cue display tree updates via an 
event loop task), the
stale pointer is dereferenced, causing an use-after-free.

This PR fixes this use-after-free bug by updating m_shadowIncludingRoot for 
removed subtrees when
the root's refCount is greater than 1 (i.e. there is an external reference to 
the node beyond the
RefPtr in removeDetachedChildrenInContainer).

No new tests since existing media tests such as 
media/track/webvtt-parser-does-not-leak.html would
hit debug assertions without this fix, and this bug requires a node to be kept 
alive by C++ code.

* Source/WebCore/dom/ContainerNodeAlgorithms.cpp:
(WebCore::removeDetachedChildrenInContainer):
* Source/WebCore/dom/Node.cpp:
(WebCore::Node::updateShadowIncludingRootForSubtree):
* Source/WebCore/dom/Node.h:

Originally-landed-as: 305413.700@safari-7624-branch (5d07bda85f02). 
rdar://176059252
Canonical link: https://commits.webkit.org/315148@main



To unsubscribe from these emails, change your notification settings at 
https://github.com/WebKit/WebKit/settings/notifications

Reply via email to