Race condition in JSXPathResult::visitAdditionalChildren during GC
XPath의 node set 처리에서 발생하는 concurrent GC UAF가 실제로 확인된 취약점입니다.
패치 이전에는 GC thread가 m_value.toNodeSet()을 직접 순회하는 도중, main thread에서 XPathNodeList::sort가 호출될 수 있었습니다. XPathNodeList::firstNode 등을 통해 이 경로에 진입하면 내부 vector가 재할당되고, GC thread의 iterator가 무효화됩니다.
이 문제는 오래전부터 잠재적 위험으로 인식되고 있었습니다. 당시의 FIXME 주석에는 "This looks like it might race, but I'm not sure"라는 내용이 남아 있었는데, 배포된 코드 안에 잔존하던 known-unknown이었습니다.
Source/WebCore/xml/XPathResult.h
- XPath::NodeSet m_nodeSet; // FIXME: m_value에 저장된 node set을 왜 여기서도 중복으로 갖고 있는가?
+ 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 UAF는 browser engine에서 exploit 가치가 가장 높은 취약점 클래스 중 하나입니다. GC thread가 상시 실행 중인 만큼, JS 측에서 timing을 정밀하게 맞출 필요가 없습니다.