← All issues

Race condition in JSXPathResult::visitAdditionalChildren during GC

e8db86e

A confirmed concurrent GC UAF in XPath's node set handling. Before the fix, the GC thread iterated m_value.toNodeSet() directly while the main thread could trigger XPathNodeList::sort (e.g., via XPathNodeList::firstNode), reallocating the internal vector and invalidating the GC thread's iterator. The original FIXME comment literally read "This looks like it might race, but I'm not sure" — a known-unknown in shipping code.

Source/WebCore/xml/XPathResult.h

- XPath::NodeSet m_nodeSet; // FIXME: why duplicate the node set stored in m_value?
+ Lock m_nodeSetLock;
+ XPath::NodeSet m_nodeSet WTF_GUARDED_BY_LOCK(m_nodeSetLock);

Source/WebCore/xml/XPathResult.cpp

+template<typename Visitor>
+void XPathResult::visitAdditionalChildrenInGCThread(Visitor& visitor)
+{
+ Locker locker { m_nodeSetLock };
+ for (auto& node : m_nodeSet)
+ addWebCoreOpaqueRoot(visitor, node.get());
+}

Concurrent GC UAFs are one of the highest-value vulnerability classes in browser engines — they require no timing-precise triggering from JS because the GC thread is always running.

🔒

Coverage gaps in the new locking scheme and interactions with existing access patterns are worth security investigation.

Subscribe to read more