From df1815070cfd8d2ed6f7101d1b8d60d037c839e6 Mon Sep 17 00:00:00 2001 From: Cary Clark Date: Wed, 3 Jun 2009 16:01:34 -0400 Subject: browser security patches Bug 25420: REGRESSION: XMLHttpRequest allows loading from another origin - fix: http://trac.webkit.org/changeset/42983 Bug 24575: Cross-origin XMLHttpRequest is always allowed - fix: http://trac.webkit.org/projects/webkit/changeset/41667 Bug 21456: UXSS after navigation via directly referencing document - fix: http://trac.webkit.org/changeset/42223 Bug 22655: Stack overflow crash in WebCore::RenderBlock::layout() with deeply nested
s - fix: http://trac.webkit.org/projects/webkit/changeset/41938 --- WebCore/bindings/js/JSAudioConstructor.cpp | 6 +++++- WebCore/bindings/js/JSDOMWindowBase.cpp | 2 -- WebCore/bindings/js/JSDOMWindowCustom.h | 6 ------ WebCore/bindings/js/JSImageConstructor.cpp | 3 ++- WebCore/bindings/js/JSMessageChannelConstructor.cpp | 6 +++++- WebCore/bindings/js/JSXMLHttpRequestConstructor.cpp | 5 ++++- WebCore/bindings/js/ScriptController.cpp | 6 +----- WebCore/bindings/js/ScriptController.h | 2 -- WebCore/html/HTMLParser.cpp | 21 +++++++++++++++++++++ WebCore/html/HTMLParser.h | 5 +++++ WebCore/page/DOMWindow.cpp | 6 ++++++ WebCore/xml/XMLHttpRequest.cpp | 4 ++-- 12 files changed, 51 insertions(+), 21 deletions(-) diff --git a/WebCore/bindings/js/JSAudioConstructor.cpp b/WebCore/bindings/js/JSAudioConstructor.cpp index f0bdbe8..1231271 100644 --- a/WebCore/bindings/js/JSAudioConstructor.cpp +++ b/WebCore/bindings/js/JSAudioConstructor.cpp @@ -54,7 +54,11 @@ static JSObject* constructAudio(ExecState* exec, JSObject* constructor, const Ar { // FIXME: Why doesn't this need the call toJS on the document like JSImageConstructor? - RefPtr audio = new HTMLAudioElement(HTMLNames::audioTag, static_cast(constructor)->document()); + Document* document = static_cast(constructor)->document(); + if (!document) + return throwError(exec, ReferenceError, "Audio constructor associated document is unavailable"); + + RefPtr audio = new HTMLAudioElement(HTMLNames::audioTag, document); if (args.size() > 0) { audio->setSrc(args.at(exec, 0).toString(exec)); audio->scheduleLoad(); diff --git a/WebCore/bindings/js/JSDOMWindowBase.cpp b/WebCore/bindings/js/JSDOMWindowBase.cpp index 83bc202..d99abd9 100644 --- a/WebCore/bindings/js/JSDOMWindowBase.cpp +++ b/WebCore/bindings/js/JSDOMWindowBase.cpp @@ -157,8 +157,6 @@ void JSDOMWindowBase::updateDocument() JSDOMWindowBase::~JSDOMWindowBase() { - if (d()->impl->frame()) - d()->impl->frame()->script()->clearFormerWindow(asJSDOMWindow(this)); } ScriptExecutionContext* JSDOMWindowBase::scriptExecutionContext() const diff --git a/WebCore/bindings/js/JSDOMWindowCustom.h b/WebCore/bindings/js/JSDOMWindowCustom.h index 838abab..351f2dd 100644 --- a/WebCore/bindings/js/JSDOMWindowCustom.h +++ b/WebCore/bindings/js/JSDOMWindowCustom.h @@ -185,12 +185,6 @@ ALWAYS_INLINE bool JSDOMWindowBase::allowsAccessFromPrivate(const JSGlobalObject if (originWindow == targetWindow) return true; - // JS may be attempting to access the "window" object, which should be valid, - // even if the document hasn't been constructed yet. If the document doesn't - // exist yet allow JS to access the window object. - if (!originWindow->impl()->document()) - return true; - const SecurityOrigin* originSecurityOrigin = originWindow->impl()->securityOrigin(); const SecurityOrigin* targetSecurityOrigin = targetWindow->impl()->securityOrigin(); diff --git a/WebCore/bindings/js/JSImageConstructor.cpp b/WebCore/bindings/js/JSImageConstructor.cpp index 0dc55b4..54e8be7 100644 --- a/WebCore/bindings/js/JSImageConstructor.cpp +++ b/WebCore/bindings/js/JSImageConstructor.cpp @@ -56,7 +56,8 @@ static JSObject* constructImage(ExecState* exec, JSObject* constructor, const Ar } Document* document = static_cast(constructor)->document(); - + if (!document) + return throwError(exec, ReferenceError, "Image constructor associated document is unavailable"); // Calling toJS on the document causes the JS document wrapper to be // added to the window object. This is done to ensure that JSDocument::mark // will be called (which will cause the image element to be marked if necessary). diff --git a/WebCore/bindings/js/JSMessageChannelConstructor.cpp b/WebCore/bindings/js/JSMessageChannelConstructor.cpp index 8da9f6d..54ffb18 100644 --- a/WebCore/bindings/js/JSMessageChannelConstructor.cpp +++ b/WebCore/bindings/js/JSMessageChannelConstructor.cpp @@ -70,7 +70,11 @@ ConstructType JSMessageChannelConstructor::getConstructData(ConstructData& const JSObject* JSMessageChannelConstructor::construct(ExecState* exec, JSObject* constructor, const ArgList&) { - return asObject(toJS(exec, MessageChannel::create(static_cast(constructor)->scriptExecutionContext()))); + ScriptExecutionContext* context = static_cast(constructor)->scriptExecutionContext(); + if (!context) + return throwError(exec, ReferenceError, "MessageChannel constructor associated document is unavailable"); + + return asObject(toJS(exec, MessageChannel::create(context))); } void JSMessageChannelConstructor::mark() diff --git a/WebCore/bindings/js/JSXMLHttpRequestConstructor.cpp b/WebCore/bindings/js/JSXMLHttpRequestConstructor.cpp index d7f54de..ba9e6ed 100644 --- a/WebCore/bindings/js/JSXMLHttpRequestConstructor.cpp +++ b/WebCore/bindings/js/JSXMLHttpRequestConstructor.cpp @@ -43,7 +43,10 @@ JSXMLHttpRequestConstructor::JSXMLHttpRequestConstructor(ExecState* exec, Script static JSObject* constructXMLHttpRequest(ExecState* exec, JSObject* constructor, const ArgList&) { - RefPtr xmlHttpRequest = XMLHttpRequest::create(static_cast(constructor)->document()); + WebCore::Document* doc = static_cast(constructor)->document(); + if (!doc) + return throwError(exec, ReferenceError, "XMLHttpRequest constructor associated document is unavailable"); + RefPtr xmlHttpRequest = XMLHttpRequest::create(doc); return CREATE_DOM_OBJECT_WRAPPER(exec, XMLHttpRequest, xmlHttpRequest.get()); } diff --git a/WebCore/bindings/js/ScriptController.cpp b/WebCore/bindings/js/ScriptController.cpp index efd3a70..11d2945 100644 --- a/WebCore/bindings/js/ScriptController.cpp +++ b/WebCore/bindings/js/ScriptController.cpp @@ -133,7 +133,6 @@ void ScriptController::clearWindowShell() JSLock lock(false); m_windowShell->window()->clear(); - m_liveFormerWindows.add(m_windowShell->window()); m_windowShell->setWindow(m_frame->domWindow()); if (Page* page = m_frame->page()) { attachDebugger(page->debugger()); @@ -168,7 +167,7 @@ void ScriptController::initScript() JSLock lock(false); m_windowShell = new JSDOMWindowShell(m_frame->domWindow()); - updateDocument(); + m_windowShell->window()->updateDocument(); if (Page* page = m_frame->page()) { attachDebugger(page->debugger()); @@ -265,9 +264,6 @@ void ScriptController::updateDocument() JSLock lock(false); if (m_windowShell) m_windowShell->window()->updateDocument(); - HashSet::iterator end = m_liveFormerWindows.end(); - for (HashSet::iterator it = m_liveFormerWindows.begin(); it != end; ++it) - (*it)->updateDocument(); } void ScriptController::updateSecurityOrigin() diff --git a/WebCore/bindings/js/ScriptController.h b/WebCore/bindings/js/ScriptController.h index 28fd7e9..2a9ea45 100644 --- a/WebCore/bindings/js/ScriptController.h +++ b/WebCore/bindings/js/ScriptController.h @@ -101,7 +101,6 @@ public: const String* sourceURL() const { return m_sourceURL; } // 0 if we are not evaluating any script void clearWindowShell(); - void clearFormerWindow(JSDOMWindow* window) { m_liveFormerWindows.remove(window); } void updateDocument(); // Notifies the ScriptController that the securityOrigin of the current @@ -146,7 +145,6 @@ private: bool isJavaScriptAnchorNavigation() const; JSC::ProtectedPtr m_windowShell; - HashSet m_liveFormerWindows; Frame* m_frame; int m_handlerLineno; const String* m_sourceURL; diff --git a/WebCore/html/HTMLParser.cpp b/WebCore/html/HTMLParser.cpp index 0403dad..a719d7d 100644 --- a/WebCore/html/HTMLParser.cpp +++ b/WebCore/html/HTMLParser.cpp @@ -61,6 +61,13 @@ using namespace HTMLNames; static const unsigned cMaxRedundantTagDepth = 20; static const unsigned cResidualStyleMaxDepth = 200; +static const int minBlockLevelTagPriority = 3; + +// A cap on the number of tags with priority minBlockLevelTagPriority or higher +// allowed in blockStack. The cap is enforced by adding such new elements as +// siblings instead of children once it is reached. +static const size_t cMaxBlockDepth = 4096; + struct HTMLStackElem : Noncopyable { HTMLStackElem(const AtomicString& t, int lvl, Node* n, bool r, HTMLStackElem* nx) : tagName(t) @@ -117,6 +124,7 @@ HTMLParser::HTMLParser(HTMLDocument* doc, bool reportErrors) , current(doc) , didRefCurrent(false) , blockStack(0) + , m_blocksInStack(0) , m_hasPElementInScope(NotInScope) , head(0) , inBody(false) @@ -134,6 +142,7 @@ HTMLParser::HTMLParser(DocumentFragment* frag) , current(frag) , didRefCurrent(true) , blockStack(0) + , m_blocksInStack(0) , m_hasPElementInScope(NotInScope) , head(0) , inBody(true) @@ -320,6 +329,11 @@ bool HTMLParser::insertNode(Node* n, bool flat) if (inStrayTableContent && localName == tableTag) popBlock(tableTag); + if (tagPriority >= minBlockLevelTagPriority) { + while (m_blocksInStack >= cMaxBlockDepth) + popBlock(blockStack->tagName); + } + // let's be stupid and just try to insert it. // this should work if the document is well-formed Node* newNode = current->addChild(n); @@ -1306,6 +1320,8 @@ void HTMLParser::reopenResidualStyleTags(HTMLStackElem* elem, Node* malformedTab void HTMLParser::pushBlock(const AtomicString& tagName, int level) { blockStack = new HTMLStackElem(tagName, level, current, didRefCurrent, blockStack); + if (level >= minBlockLevelTagPriority) + m_blocksInStack++; didRefCurrent = false; if (tagName == pTag) m_hasPElementInScope = InScope; @@ -1408,6 +1424,10 @@ inline HTMLStackElem* HTMLParser::popOneBlockCommon() if (current && elem->node != current) current->finishParsingChildren(); + if (blockStack->level >= minBlockLevelTagPriority) { + ASSERT(m_blocksInStack > 0); + m_blocksInStack--; + } blockStack = elem->next; current = elem->node; didRefCurrent = elem->didRefNode; @@ -1482,6 +1502,7 @@ void HTMLParser::freeBlock() { while (blockStack) popOneBlock(); + ASSERT(!m_blocksInStack); } void HTMLParser::createHead() diff --git a/WebCore/html/HTMLParser.h b/WebCore/html/HTMLParser.h index 866835f..251b323 100644 --- a/WebCore/html/HTMLParser.h +++ b/WebCore/html/HTMLParser.h @@ -159,6 +159,11 @@ private: HTMLStackElem* blockStack; + // The number of tags with priority minBlockLevelTagPriority or higher + // currently in m_blockStack. The parser enforces a cap on this value by + // adding such new elements as siblings instead of children once it is reached. + size_t m_blocksInStack; + enum ElementInScopeState { NotInScope, InScope, Unknown }; ElementInScopeState m_hasPElementInScope; diff --git a/WebCore/page/DOMWindow.cpp b/WebCore/page/DOMWindow.cpp index 70ee79e..f28e356 100644 --- a/WebCore/page/DOMWindow.cpp +++ b/WebCore/page/DOMWindow.cpp @@ -764,9 +764,15 @@ DOMWindow* DOMWindow::top() const Document* DOMWindow::document() const { + // FIXME: This function shouldn't need a frame to work. if (!m_frame) return 0; + // The m_frame pointer is not zeroed out when the window is put into b/f cache, so it can hold an unrelated document/window pair. + // FIXME: We should always zero out the frame pointer on navigation to avoid accidentally accessing the new frame content. + if (m_frame->domWindow() != this) + return 0; + ASSERT(m_frame->document()); return m_frame->document(); } diff --git a/WebCore/xml/XMLHttpRequest.cpp b/WebCore/xml/XMLHttpRequest.cpp index f16755a..b62679b 100644 --- a/WebCore/xml/XMLHttpRequest.cpp +++ b/WebCore/xml/XMLHttpRequest.cpp @@ -934,6 +934,7 @@ void XMLHttpRequest::networkError() if (m_upload) m_upload->dispatchErrorEvent(); } + internalAbort(); } void XMLHttpRequest::abortError() @@ -1159,7 +1160,6 @@ void XMLHttpRequest::didFail() if (m_error) return; - internalAbort(); networkError(); } @@ -1294,7 +1294,7 @@ void XMLHttpRequest::didReceiveAuthenticationCancellation(const ResourceResponse void XMLHttpRequest::didReceiveData(const char* data, int len) { - if (m_inPreflight) + if (m_inPreflight || m_error) return; if (m_state < HEADERS_RECEIVED) -- cgit v1.1