Race condition in JSXPathResult::visitAdditionalChildren during GC
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());
+}
Significance
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.