Bug 31680

Summary: WebCore::Document::updateLayoutIgnorePendingStylesheets NULL pointer
Product: WebKit Reporter: Berend-Jan Wever <skylined>
Component: DOMAssignee: Nobody <webkit-unassigned>
Status: RESOLVED FIXED    
Severity: Normal CC: ap, darin, enrica, eric, mitz, morrita, tkent, webkit.review.bot
Priority: P1    
Version: 528+ (Nightly build)   
Hardware: PC   
OS: Windows Vista   
URL: http://skypher.com/SkyLined/Repro/WebKit/Bug%2031680%20-%20chrome!WebCore..Document..updateLayoutIgnorePendingStylesheets%20NULL%20pointer/repro.html
Bug Depends on:    
Bug Blocks: 37005    
Attachments:
Description Flags
Repro
none
patch v0
none
v1; guarded null document
none
v2; add a guard on setBaseAndExtent()
none
v3; used asssertion, added null checks more; enhanced test
none
v4; Added redirects for some methods, added tests for that
none
v5; took the feedback to respect compatibiity darin: review+, tkent: commit-queue-

Berend-Jan Wever
Reported 2009-11-19 13:07:03 PST
Created attachment 43518 [details] Repro The following HTML triggers a NULL pointer in chrome!WebCore::Document::updateLayoutIgnorePendingStylesheets: <SCRIPT> sel = window.getSelection(); doc = document.implementation.createDocumentType('c'); sel.setBaseAndExtent(doc); </SCRIPT> Relevant call stack: WebCore::Document::updateLayoutIgnorePendingStylesheets(void)+0x4 WebCore::VisiblePosition::canonicalPosition(class WebCore::Position * position = 0x0012f184)+0x3a WebCore::VisiblePosition::init(class WebCore::Position * position = 0x0012f184, WebCore::EAffinity affinity = DOWNSTREAM (1))+0x25 WebCore::VisiblePosition::VisiblePosition(class WebCore::Node * node = 0x05639fc0, int offset = 715827888, WebCore::EAffinity affinity = DOWNSTREAM (1))+0x46 WebCore::DOMSelection::setBaseAndExtent(class WebCore::Node * baseNode = 0x05639fc0, int baseOffset = 715827888, class WebCore::Node * extentNode = 0x00000000, int extentOffset = 429496759, int * ec = 0x0012f204)+0x39 WebCore::DOMSelectionInternal::setBaseAndExtentCallback(class v8::Arguments * args = 0x0112f254)+0x180
Attachments
Repro (139 bytes, text/html)
2009-11-19 13:07 PST, Berend-Jan Wever
no flags
patch v0 (6.49 KB, patch)
2010-03-28 22:53 PDT, Hajime Morrita
no flags
v1; guarded null document (3.40 KB, patch)
2010-03-29 00:46 PDT, Hajime Morrita
no flags
v2; add a guard on setBaseAndExtent() (4.26 KB, patch)
2010-03-29 19:06 PDT, Hajime Morrita
no flags
v3; used asssertion, added null checks more; enhanced test (7.87 KB, patch)
2010-03-30 04:12 PDT, Hajime Morrita
no flags
v4; Added redirects for some methods, added tests for that (14.06 KB, patch)
2010-03-30 22:15 PDT, Hajime Morrita
no flags
v5; took the feedback to respect compatibiity (12.82 KB, patch)
2010-04-01 03:13 PDT, Hajime Morrita
darin: review+
tkent: commit-queue-
Berend-Jan Wever
Comment 1 2009-11-19 13:10:28 PST
Added repro URL
Berend-Jan Wever
Comment 2 2009-11-19 14:15:43 PST
Fix title
Hajime Morrita
Comment 3 2010-03-28 22:53:00 PDT
Created attachment 51877 [details] patch v0
Hajime Morrita
Comment 4 2010-03-28 22:57:37 PDT
Instead of just guarding null access, this change make DocumentType to have non-null m_document. This should be more safe to guarantee non-null m_document than just gurding null access to it. This is because DocumenType is a subclass of Node and certain part of codebase assumes Node::m_document non-null.
mitz
Comment 5 2010-03-28 23:09:53 PDT
Comment on attachment 51877 [details] patch v0 <http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#Level-2-Core-DOM-createDocType> specifies that createDocumentType() should return a node with a null ownerDocument. Unless I’m missing something, this change contradicts the specification.
Alexey Proskuryakov
Comment 6 2010-03-28 23:24:57 PDT
Don't we have a regression test verifying that a newly created DocumentType node has no document? If we don't, please add one.
Hajime Morrita
Comment 7 2010-03-29 00:46:20 PDT
Created attachment 51880 [details] v1; guarded null document
Hajime Morrita
Comment 8 2010-03-29 00:50:58 PDT
Mitz, thank you for reviewing really quickly! > specifies that createDocumentType() should return a node with a null > ownerDocument. Unless I’m missing something, this change contradicts the > specification. You are right. I updated the patch that just guards null document. @ap >Don't we have a regression test verifying that a newly created DocumentType >node has no document? If we don't, please add one. The last patch even broke the regression on fast/. I tested only editing/. I'm so sorry about that... Updated patch certainly passes all test, as expected.
Alexey Proskuryakov
Comment 9 2010-03-29 08:40:11 PDT
Comment on attachment 51880 [details] v1; guarded null document This patch looks correct to me. But it seems wrong that such a node even ends up in Position. Perhaps it would be a better fix to ensure that this doesn't happen, I'm not sure. The place to make the check would be setBaseAndExtent(), and other functions in DOMSelection may need checks (and regression tests), too.
Darin Adler
Comment 10 2010-03-29 09:16:27 PDT
Comment on attachment 51880 [details] v1; guarded null document The fact that DocumentType nodes have no document stinks. The question is where to guard against these types of nodes. To me, canonicalPosition seems a bit low-level a place to be doing the check. I worry there are other leaf functions that lack this. It might be better to guard this incoming at places where nodes are passed in. r=me, though
Hajime Morrita
Comment 11 2010-03-29 19:06:08 PDT
Created attachment 51994 [details] v2; add a guard on setBaseAndExtent()
Hajime Morrita
Comment 12 2010-03-29 19:09:53 PDT
darin, ap, thank you for reviewing! >But it seems wrong that such a node even ends up in Position. Perhaps it would >be a better fix to ensure that this doesn't happen, I'm not sure. The place to >make the check would be setBaseAndExtent(), and other functions in DOMSelection >may need checks (and regression tests), too. >The fact that DocumentType nodes have no document stinks. The question is where >to guard against these types of nodes. To me, canonicalPosition seems a bit >low-level a place to be doing the check. I worry there are other leaf functions >that lack this. It might be better to guard this incoming at places where nodes are passed in. I agreed these concern and added a guard on setBaseAndExtent(). A guard on canonicalPosition() has kept as is because VibiblePosition is used many places on editing area and It looks safe to guard it anyway.
Hajime Morrita
Comment 13 2010-03-29 19:26:54 PDT
As ap mentioned before, ownerless node lead crashes on other DOMSelection APIs. I filed the bug on https://bugs.webkit.org/show_bug.cgi?id=36800 and I'll submit regressions for that after this patch landed.
Alexey Proskuryakov
Comment 14 2010-03-29 23:59:22 PDT
Comment on attachment 51994 [details] v2; add a guard on setBaseAndExtent() + The fix also adds a null guard for canonicalPosition() just in case. We don't usually add null checks just in case, especially in performance sensitive code. These can confuse people looking at the code to think that this can legitimately happen. You could add an ASSERT - it would be useless for catching errors, because we crash soon enough anyway, but it would serve as documentation that nodes are supposed to have a non-null document here. + // Because we don't know how to "select" ownerless nodes, we take them as null. I was going to suggest raising an exception instead (probably INVALID_ACCESS_ERR). I'm not sure if that's a good idea. But it made me think about nodes from different documents. Is there a check somewhere that we don't set the selection base and extent to nodes from another document? +It is OK not to crash. A better way to say this would be "PASSED if didn't crash".
Hajime Morrita
Comment 15 2010-03-30 04:12:36 PDT
Created attachment 52021 [details] v3; used asssertion, added null checks more; enhanced test
Hajime Morrita
Comment 16 2010-03-30 04:20:26 PDT
ap, thank you for reviewing again! I updated the patch. > We don't usually add null checks just in case, especially in performance > sensitive code. These can confuse people looking at the code to think that this > can legitimately happen. > > You could add an ASSERT - it would be useless for catching errors, because we > crash soon enough anyway, but it would serve as documentation that nodes are > supposed to have a non-null document here. Agreed. I replaced null check with ASSERT() > > I was going to suggest raising an exception instead (probably > INVALID_ACCESS_ERR). I'm not sure if that's a good idea. Fixed. > But it made me think > about nodes from different documents. Is there a check somewhere that we don't > set the selection base and extent to nodes from another document? For setBaseAndExtent(), there are 2 patterns of foreign document. And both somewhat work. Pattern 1: owner document of selection and of argument node differ document of argument node is used. so selection of argument node's document change. Pattern 2: owner document of baseNode and of extentNode differ: extentNode is replaced by baseNode. > +It is OK not to crash. > > A better way to say this would be "PASSED if didn't crash". Fixed. This fix also cares Bug 36800 because both has same root cause and I think it is better to fix them at single commit. for change cleanness.
Darin Adler
Comment 17 2010-03-30 12:04:44 PDT
Comment on attachment 52021 [details] v3; used asssertion, added null checks more; enhanced test Alexey pointed out what we should really be doing. This should not just be about null checking. > + if (node && !node->document()) { > + ec = INVALID_ACCESS_ERR; > + return; > + } Instead of checking for null we should check that the node is in the document of m_frame. It's not OK to set this to a node in another document. > + if ((baseNode && !baseNode->document()) > + || (extentNode && !extentNode->document())) { > + // We don't know how to "select" ownerless nodes. > + ec = INVALID_ACCESS_ERR; > + return; > + } Same comment. > + if (node && !node->document()) { > + ec = INVALID_ACCESS_ERR; > + return; > + } Same comment. > + if (!node->document()) { > + ec = INVALID_ACCESS_ERR; > + return; > + } Ditto. > - if (!n || selection->isNone()) > + if (!n || !n->document() || selection->isNone()) > return false; Again. > + if (!n->document()) { > + ec = INVALID_ACCESS_ERR; > + return; > + } Again.
Alexey Proskuryakov
Comment 18 2010-03-30 12:32:06 PDT
A style nit: there is no need to split short conditions like this into two lines. > + if ((baseNode && !baseNode->document()) > + || (extentNode && !extentNode->document())) { I'm very glad that you found and fixed many more problematic cases, besides the one originally reported here.
Hajime Morrita
Comment 19 2010-03-30 22:15:31 PDT
Created attachment 52127 [details] v4; Added redirects for some methods, added tests for that
Hajime Morrita
Comment 20 2010-03-30 22:29:02 PDT
Darin, ap, thank you for reviewing and for your suggestion! I updated the patch. > (From update of attachment 52021 [details]) > Alexey pointed out what we should really be doing. This should not just be > about null checking. > (snip) > Instead of checking for null we should check that the node is in the document > of m_frame. It's not OK to set this to a node in another document. Requiring m_frame->document() == node->document() is too strict and Doing so will break following tests: - editing/selection/4960137.html - editing/selection/drag-in-iframe.html These tests expect DOMSelection::setBaseAndExtent() to redirect operations to owner document of the argument node, instead of the document of the DOMSelection. Although these redirection was done inside SelectionController::setSelection(), This change do it inside DOMSelection to clarify intension. This change also redirect other DOMSelection API with Node as an argument for consistency. (And added DOMSelection::findControllerFor() to choose the destination of the redirection.) I hope I would remove code for implicit redirection from SelectionController and allow SelectionController to assume its operation closed inside the single document. But SelectionController is called from many place (especially editing) and such cleanup looks difficult at this time. So This change keeps it as is.
Darin Adler
Comment 21 2010-03-31 12:51:45 PDT
Comment on attachment 52127 [details] v4; Added redirects for some methods, added tests for that > + SelectionController* controller = findControllerFor(node); > + if (!controller) { > + ec = INVALID_ACCESS_ERR; > + return; > + } > + > + controller->moveTo(VisiblePosition(node, offset, DOWNSTREAM)); Is this the behavior we want or not? You are making a call on a particular window's selection object. Should this affect the selection in another window? Does any other browser implement this? What does that browser do? Is there any documentation for this? What does the documentation say? Does the HTML5 specification or another W3C specification mention this? What does it say?
Darin Adler
Comment 22 2010-03-31 12:52:42 PDT
(In reply to comment #21) > Is this the behavior we want or not? You are making a call on a particular > window's selection object. Should this affect the selection in another window? > Does any other browser implement this? What does that browser do? Is there any > documentation for this? What does the documentation say? Does the HTML5 > specification or another W3C specification mention this? What does it say? You mentioned two specific regression tests, but I think we need a little more research than that. And where I said "You" I meant the JavaScript code, not you specifically ;-)
Hajime Morrita
Comment 23 2010-04-01 03:12:30 PDT
Darin, thank you for reviewing. And I'm sorry for my short of attention to the standard compatibility. I updated the patch. > Does the HTML5 > specification or another W3C specification mention this? What does it say? HTML5 says that we should throw exceptions for nodes from foreign documents, as you mentioned. http://www.whatwg.org/specs/web-apps/current-work/#selection > Does any other browser implement this? What does that browser do? Is there any > documentation for this? What does the documentation say? Firefox accepts foreign nodes. But its behavior is different from us. In Firefox, selection of certain window (or document) can hold nodes from another document. But rendering doesn't reflect it. For example, when selection.collapse(foreignNode, 0), selection.anchorNode is set to foreignNode. with nothing selected(highlighted) on the foreign document (nor the document of the selection). This behavior is incompatible to HTML5 standard, It is also hard to follow for WebKit due to our VisiblePosition design that requires the valid nodes of selection's own document. IE doesn't support selection API. Updated patch goes between Firefox and HTML5 spec. The patch just ignores foreign node, without throwing an exception. Doing so, applications will keep running somehow even if they assume Firefox model, in contrast to throwing exception that will stop the program.
Hajime Morrita
Comment 24 2010-04-01 03:13:52 PDT
Created attachment 52279 [details] v5; took the feedback to respect compatibiity
Darin Adler
Comment 25 2010-04-01 16:36:31 PDT
Comment on attachment 52279 [details] v5; took the feedback to respect compatibiity > - if (!n || selection->isNone()) > + if (!n || m_frame->document() != n->document() || selection->isNone()) > return false; Why did you choose not to use your isValidForPosition helper function here? > +bool DOMSelection::isValidForPosition(Node* node) const > +{ > + ASSERT(m_frame); > + if (!node) > + return true; > + return (node->document() == m_frame->document()); > +} Extra parentheses here. r=me Lets send the feedback to the HTML5 folks about not the need to not raise exceptions in these cases for compatibility.
Hajime Morrita
Comment 26 2010-04-01 19:30:21 PDT
Darin, thank you for reviewing! (In reply to comment #25) > (From update of attachment 52279 [details]) > > - if (!n || selection->isNone()) > > + if (!n || m_frame->document() != n->document() || selection->isNone()) > > return false; > > Why did you choose not to use your isValidForPosition helper function here? This is because isValidForPosition() returns true even if node is null. > Lets send the feedback to the HTML5 folks about not the need to not raise > exceptions in these cases for compatibility. OK, I'll post the issue to whatwg@whatwg.org.
Kent Tamura
Comment 27 2010-04-01 19:58:49 PDT
> > + return (node->document() == m_frame->document()); > > +} > > Extra parentheses here. I'll land this patch with the above change.
Kent Tamura
Comment 28 2010-04-01 20:14:29 PDT
WebKit Review Bot
Comment 29 2010-04-01 21:01:47 PDT
http://trac.webkit.org/changeset/56962 might have broken Leopard Intel Release (Tests)
Kent Tamura
Comment 30 2010-04-02 01:35:18 PDT
Committed another test change for this. http://trac.webkit.org/changeset/56990
Note You need to log in before you can comment on or make changes to this bug.