Closed Bug 842078 Opened 11 years ago Closed 11 years ago

Global-buffer-overflow in nsDocLoader::Stop

Categories

(Core :: DOM: Navigation, defect)

x86_64
All
defect
Not set
normal

Tracking

()

RESOLVED FIXED
mozilla22
Tracking Status
firefox19 --- unaffected
firefox20 --- unaffected
firefox21 + fixed
firefox22 + fixed
firefox-esr17 --- unaffected
b2g18 --- unaffected

People

(Reporter: inferno, Assigned: tbsaunde)

References

Details

(5 keywords)

Attachments

(3 files, 1 obsolete file)

Attached file Testcase
>==13973== ERROR: AddressSanitizer: global-buffer-overflow on address 0x7f13e6712bd0 at pc 0x7f13e146005b bp 0x7fffffe17430 sp 0x7fffffe17428
>READ of size 8 at 0x7f13e6712bd0 thread T0
>    #0 0x7f13e146005a in ChildAt src/uriloader/base/nsDocLoader.h:112
>    #1 0x7f13e146005a in nsDocLoader::Stop() src/uriloader/base/nsDocLoader.cpp:274
>    #2 0x7f13e13f9602 in nsDocShell::Stop(unsigned int) src/docshell/base/nsDocShell.cpp:4677
>    #3 0x7f13e13f9eac in non-virtual thunk to nsDocShell::Stop(unsigned int) src/docshell/base/nsDocShell.cpp:4688
>    #4 0x7f13e03c8118 in nsHTMLDocument::Open(JSContext*, nsAString_internal const&, nsAString_internal const&, mozilla::ErrorResult&) src/content/html/document/src/nsHTMLDocument.cpp:1569
>    #5 0x7f13e21fcfa0 in mozilla::dom::HTMLDocumentBinding::open(JSContext*, JS::Handle<JSObject*>, nsHTMLDocument*, unsigned int, JS::Value*) src/objdir-ff-asan/dom/bindings/HTMLDocumentBinding.cpp:520
>    #6 0x7f13e21f93b8 in mozilla::dom::HTMLDocumentBinding::genericMethod(JSContext*, unsigned int, JS::Value*) src/objdir-ff-asan/dom/bindings/HTMLDocumentBinding.cpp:1538
>    #7 0x7f13e38ecaa3 in native src/js/src/jscntxtinlines.h:327
>    #8 0x7f13e38ecaa3 in js::InvokeKernel(JSContext*, JS::CallArgs, js::MaybeConstruct) src/js/src/jsinterp.cpp:367
>    #9 0x7f13e38defab in js::Interpret(JSContext*, js::StackFrame*, js::InterpMode) src/js/src/jsinterp.cpp:2344
>    #10 0x7f13e38cddc1 in js::RunScript(JSContext*, js::StackFrame*) src/js/src/jsinterp.cpp:324
>    #11 0x7f13e38ec9c2 in js::InvokeKernel(JSContext*, JS::CallArgs, js::MaybeConstruct) src/js/src/jsinterp.cpp:381
>    #12 0x7f13e38eda1d in Invoke src/js/src/jsinterp.h:135
>    #13 0x7f13e38eda1d in js::Invoke(JSContext*, JS::Value const&, JS::Value const&, unsigned int, JS::Value*, JS::Value*) src/js/src/jsinterp.cpp:414
>    #14 0x7f13e397fa16 in js::BaseProxyHandler::call(JSContext*, JSObject*, unsigned int, JS::Value*) src/js/src/jsproxy.cpp:273
>    #15 0x7f13e3a91521 in call src/js/src/jswrapper.cpp:295
>    #16 0x7f13e3a91521 in js::CrossCompartmentWrapper::call(JSContext*, JSObject*, unsigned int, JS::Value*) src/js/src/jswrapper.cpp:662
>    #17 0x7f13e399c11b in call src/js/src/jsproxy.cpp:2436
>    #18 0x7f13e399c11b in proxy_Call(JSContext*, unsigned int, JS::Value*) src/js/src/jsproxy.cpp:2972
>    #19 0x7f13e38ecb26 in CallJSNative src/js/src/jscntxtinlines.h:327
>    #20 0x7f13e38ecb26 in js::InvokeKernel(JSContext*, JS::CallArgs, js::MaybeConstruct) src/js/src/jsinterp.cpp:360
>    #21 0x7f13e38defab in js::Interpret(JSContext*, js::StackFrame*, js::InterpMode) src/js/src/jsinterp.cpp:2344
>    #22 0x7f13e38cddc1 in js::RunScript(JSContext*, js::StackFrame*) src/js/src/jsinterp.cpp:324
>    #23 0x7f13e38ec9c2 in js::InvokeKernel(JSContext*, JS::CallArgs, js::MaybeConstruct) src/js/src/jsinterp.cpp:381
>    #24 0x7f13e38eda1d in Invoke src/js/src/jsinterp.h:135
>    #25 0x7f13e38eda1d in js::Invoke(JSContext*, JS::Value const&, JS::Value const&, unsigned int, JS::Value*, JS::Value*) src/js/src/jsinterp.cpp:414
>    #26 0x7f13e37ad27a in JS_CallFunctionValue(JSContext*, JSObject*, JS::Value, unsigned int, JS::Value*, JS::Value*) src/js/src/jsapi.cpp:5737
>    #27 0x7f13e2191f36 in mozilla::dom::EventHandlerNonNull::Call(JSContext*, JSObject*, nsIDOMEvent*, mozilla::ErrorResult&) src/objdir-ff-asan/dom/bindings/EventHandlerBinding.cpp:48
>    #28 0x7f13e07e54b2 in Call<nsISupports *> src/../../../dist/include/mozilla/dom/EventHandlerBinding.h:59
>    #29 0x7f13e07e54b2 in nsJSEventListener::HandleEvent(nsIDOMEvent*) src/dom/src/events/nsJSEventListener.cpp:248
>    #30 0x7f13dff7601f in operator class nsIDOMEventListener * src/content/events/src/nsEventListenerManager.cpp:923
>    #31 0x7f13dff7601f in nsEventListenerManager::HandleEventInternal(nsPresContext*, nsEvent*, nsIDOMEvent**, nsIDOMEventTarget*, nsEventStatus*, nsCxPusher*) src/content/events/src/nsEventListenerManager.cpp:990
>    #32 0x7f13dffd13ce in CurrentTarget src/content/events/src/nsEventListenerManager.h:278
>    #33 0x7f13dffd13ce in nsEventTargetChainItem::HandleEvent(nsEventChainPostVisitor&, bool, nsCxPusher*) src/content/events/src/nsEventDispatcher.cpp:181
>    #34 0x7f13dffd078e in nsEventTargetChainItem::HandleEventTargetChain(nsEventChainPostVisitor&, nsDispatchingCallback*, bool, nsCxPusher*) src/content/events/src/nsEventDispatcher.cpp:310
>    #35 0x7f13dffd4ac4 in nsEventDispatcher::Dispatch(nsISupports*, nsPresContext*, nsEvent*, nsIDOMEvent*, nsEventStatus*, nsDispatchingCallback*, nsCOMArray<nsIDOMEventTarget>*) src/content/events/src/nsEventDispatcher.cpp:678
>    #36 0x7f13df5cf200 in nsDocumentViewer::LoadComplete(tag_nsresult) src/layout/base/nsDocumentViewer.cpp:1034
>    #37 0x7f13e140d2a3 in nsDocShell::EndPageLoad(nsIWebProgress*, nsIChannel*, tag_nsresult) src/docshell/base/nsDocShell.cpp:6643
>    #38 0x7f13e140a815 in nsDocShell::OnStateChange(nsIWebProgress*, nsIRequest*, unsigned int, tag_nsresult) src/docshell/base/nsDocShell.cpp:6471
>    #39 0x7f13e140accc in non-virtual thunk to nsDocShell::OnStateChange(nsIWebProgress*, nsIRequest*, unsigned int, tag_nsresult) src/docshell/base/nsDocShell.cpp:6478
>    #40 0x7f13e1463276 in nsDocLoader::DoFireOnStateChange(nsIWebProgress*, nsIRequest*, int&, tag_nsresult) src/uriloader/base/nsDocLoader.cpp:1299
>    #41 0x7f13e1462753 in nsDocLoader::doStopDocumentLoad(nsIRequest*, tag_nsresult) src/uriloader/base/nsDocLoader.cpp:879
>    #42 0x7f13e146089c in nsDocLoader::DocLoaderIsEmpty(bool) src/uriloader/base/nsDocLoader.cpp:769
>    #43 0x7f13e14608b7 in ChildDoneWithOnload src/uriloader/base/nsDocLoader.h:194
>    #44 0x7f13e14608b7 in nsDocLoader::DocLoaderIsEmpty(bool) src/uriloader/base/nsDocLoader.cpp:772
>    #45 0x7f13e1461c5f in nsDocLoader::OnStopRequest(nsIRequest*, nsISupports*, tag_nsresult) src/uriloader/base/nsDocLoader.cpp:653
>    #46 0x7f13e14623d9 in non-virtual thunk to nsDocLoader::OnStopRequest(nsIRequest*, nsISupports*, tag_nsresult) src/uriloader/base/nsDocLoader.cpp:657
>    #47 0x7f13dee9c92b in nsLoadGroup::RemoveRequest(nsIRequest*, nsISupports*, tag_nsresult) src/netwerk/base/src/nsLoadGroup.cpp:676
>    #48 0x7f13dfd1192e in nsDocument::DoUnblockOnload() src/content/base/src/nsDocument.cpp:7623
>    #49 0x7f13dfd114eb in nsDocument::UnblockOnload(bool) src/content/base/src/nsDocument.cpp:7551
>    #50 0x7f13dfcf5648 in nsDocument::DispatchContentLoadedEvents() src/content/base/src/nsDocument.cpp:4411
>    #51 0x7f13dfd2dacc in nsRunnableMethodImpl<void (nsDocument::*)(), true>::Run() src/../../../dist/include/nsThreadUtils.h:367
>    #52 0x7f13e269d423 in nsThread::ProcessNextEvent(bool, bool*) src/xpcom/threads/nsThread.cpp:627
>    #53 0x7f13e25e4d80 in NS_ProcessNextEvent_P(nsIThread*, bool) src/objdir-ff-asan/xpcom/build/nsThreadUtils.cpp:238
>    #54 0x7f13e1dbeafc in mozilla::ipc::MessagePump::Run(base::MessagePump::Delegate*) src/ipc/glue/MessagePump.cpp:82
>    #55 0x7f13e2723f59 in RunInternal src/ipc/chromium/src/base/message_loop.cc:215
>    #56 0x7f13e2723f59 in RunHandler src/ipc/chromium/src/base/message_loop.cc:208
>    #57 0x7f13e2723f59 in MessageLoop::Run() src/ipc/chromium/src/base/message_loop.cc:182
>    #58 0x7f13e1ae9fac in nsBaseAppShell::Run() src/widget/xpwidgets/nsBaseAppShell.cpp:163
>    #59 0x7f13e15cca0a in nsAppStartup::Run() src/toolkit/components/startup/nsAppStartup.cpp:288
>    #60 0x7f13dee22635 in XREMain::XRE_mainRun() src/toolkit/xre/nsAppRunner.cpp:3871
>    #61 0x7f13dee2343f in XREMain::XRE_main(int, char**, nsXREAppData const*) src/toolkit/xre/nsAppRunner.cpp:3938
>    #62 0x7f13dee242d9 in XRE_main src/toolkit/xre/nsAppRunner.cpp:4141
>    #63 0x4212b2 in do_main src/browser/app/nsBrowserApp.cpp:224
>    #64 0x4212b2 in main src/browser/app/nsBrowserApp.cpp:521
>    #65 0x7f13e7a1a76c in
>    #66 0x4205d4 in
>0x7f13e6712bd0 is located 48 bytes to the left of global variable '_ZN7mozilla9CallStack5kNoneE (src/objdir-ff-asan/xpcom/build/DeadlockDetector.cpp)' (0x7f13e6712c00) of size 8
>0x7f13e6712bd0 is located 8 bytes to the right of global variable '_ZN14nsTArrayHeader9sEmptyHdrE (src/objdir-ff-asan/xpcom/build/nsTArray.cpp)' (0x7f13e6712bc0) of size 8
>  '_ZN14nsTArrayHeader9sEmptyHdrE (src/objdir-ff-asan/xpcom/build/nsTArray.cpp)' is ascii string ''
>Shadow bytes around the buggy address:
>  0x0fe2fccda520: 01 f9 f9 f9 f9 f9 f9 f9 01 f9 f9 f9 f9 f9 f9 f9
>  0x0fe2fccda530: 00 00 00 00 f9 f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9
>  0x0fe2fccda540: 00 f9 f9 f9 f9 f9 f9 f9 01 f9 f9 f9 f9 f9 f9 f9
>  0x0fe2fccda550: 00 00 00 00 f9 f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9
>  0x0fe2fccda560: 04 f9 f9 f9 f9 f9 f9 f9 01 f9 f9 f9 f9 f9 f9 f9
>=>0x0fe2fccda570: 00 00 00 f9 f9 f9 f9 f9 00 f9[f9]f9 f9 f9 f9 f9
>  0x0fe2fccda580: 00 f9 f9 f9 f9 f9 f9 f9 01 f9 f9 f9 f9 f9 f9 f9
>  0x0fe2fccda590: 01 f9 f9 f9 f9 f9 f9 f9 01 f9 f9 f9 f9 f9 f9 f9
>  0x0fe2fccda5a0: 01 f9 f9 f9 f9 f9 f9 f9 01 f9 f9 f9 f9 f9 f9 f9
>  0x0fe2fccda5b0: 00 f9 f9 f9 f9 f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9
>  0x0fe2fccda5c0: 00 f9 f9 f9 f9 f9 f9 f9 01 f9 f9 f9 f9 f9 f9 f9
>Shadow byte legend (one shadow byte represents 8 application bytes):
>  Addressable:           00
>  Partially addressable: 01 02 03 04 05 06 07
>  Heap left redzone:     fa
>  Heap righ redzone:     fb
>  Freed Heap region:     fd
>  Stack left redzone:    f1
>  Stack mid redzone:     f2
>  Stack right redzone:   f3
>  Stack partial redzone: f4
>  Stack after return:    f5
>  Stack use after scope: f8
>  Global redzone:        f9
>  Global init order:     f6
>  Poisoned by user:      f7
>  ASan internal:         fe
>==13973== ABORTING
>
>
Component: General → DOM
Product: Firefox → Core
Component: DOM → Document Navigation
Couldn't reproduce on a non-Asan build.
Now I managed to reproduce.
Assignee: nobody → bugs
But I'll let Trevor to fix this and other cases where array[i] used to be safe but isn't anymore.
Assignee: bugs → nobody
Blocks: 841434
Note, VoidArray::operator[] is safe, nsTArray::operator[] isn't.
(In reply to Olli Pettay [:smaug] from comment #2)
> Now I managed to reproduce.

interesting, I still can't (by typing the path to test.html in the url bar several times)

(In reply to Olli Pettay [:smaug] from comment #3)
> But I'll let Trevor to fix this and other cases where array[i] used to be
> safe but isn't anymore.

So, the cases we need to fix are the ones where we can mutate the array while iterating it, however I think its pretty infesable to tell which places do this just by code inspection.  I still can't see how Stop() does it, but I suspect its calling Cancel() on mLoadGroup.

(In reply to Olli Pettay [:smaug] from comment #4)
> Note, VoidArray::operator[] is safe, nsTArray::operator[] isn't.

I really doubt we want to play wack a mole with security bugs to find all the places we do this, but I'm not sure we really want to keep using SafeElementAt() everywhere, or using nsTObserverArray and iterating over it everywhere either.  I guess we could use SafeElementAt() remove the null checks and play wack a mole with non exploitable crashes, but that seems worse than just using nsTObserverArray iterators everywhere.
Assignee: nobody → trev.saunders
So, this is now in Aurora. I think we should just back out bug 841434, and fix that bug
in some other way in m-c
(In reply to Olli Pettay [:smaug] from comment #6)
> So, this is now in Aurora. I think we should just back out bug 841434, and
> fix that bug
> in some other way in m-c

So, I tend to think it would be easier to just decide how we want to fix the issue and do that.  For example if we don't really care about fixing the issue of mutation we could just make ChildAt() into SafeChildAt() and be done with it.
of course you should look over the remaining callers of ChildAt() and make sure I'm right that we can leave them all as is.
Attachment #716220 - Flags: review?(bzbarsky)
Comment on attachment 716220 [details] [diff] [review]
bug 842078 - properly handle mutations to mChildList while iterating over it

Olli, do you have the bandwidth to review this properly?  If not, punt back to me, but it'll be sometime next week...
Attachment #716220 - Flags: review?(bzbarsky) → review?(bugs)
Comment on attachment 716220 [details] [diff] [review]
bug 842078 - properly handle mutations to mChildList while iterating over it

>@@ -104,21 +104,21 @@ protected:
>     virtual nsresult SetDocLoaderParent(nsDocLoader * aLoader);
> 
>     bool IsBusy();
> 
>     void Destroy();
>     virtual void DestroyChildren();
> 
>     nsIDocumentLoader* ChildAt(int32_t i) {
>-        return mChildList[i];
>+        return mChildList.ElementAt(i);
>     }
So, this used to be safe when we had voidarray member variable.
Have you gone through *all* the cases when ChildAt is called?
Actually, I think we should keep ChildAt as safe, as it was before the regression and
remove SafeChildAt.



>@@ -252,17 +252,17 @@ protected:
>     nsCOMPtr<nsIRequest>       mDocumentRequest;       // [OWNER] ???compare with document
> 
>     nsDocLoader*               mParent;                // [WEAK]
> 
>     nsVoidArray                mListenerInfoList;
> 
>     nsCOMPtr<nsILoadGroup>        mLoadGroup;
>     // We hold weak refs to all our kids
>-    nsTArray<nsDocLoader*>                   mChildList;
>+    nsTObserverArray<nsDocLoader*>                   mChildList;
You could remove some spaces before mChildList.
Attachment #716220 - Flags: review?(bugs) → review-
(In reply to Olli Pettay [:smaug] from comment #10)
> Comment on attachment 716220 [details] [diff] [review]
> bug 842078 - properly handle mutations to mChildList while iterating over it
> 
> >@@ -104,21 +104,21 @@ protected:
> >     virtual nsresult SetDocLoaderParent(nsDocLoader * aLoader);
> > 
> >     bool IsBusy();
> > 
> >     void Destroy();
> >     virtual void DestroyChildren();
> > 
> >     nsIDocumentLoader* ChildAt(int32_t i) {
> >-        return mChildList[i];
> >+        return mChildList.ElementAt(i);
> >     }
> So, this used to be safe when we had voidarray member variable.
> Have you gone through *all* the cases when ChildAt is called?

all the ones in nsDocLoader / nsDocShell yes, though I suppose renaming it might have been good to be 110% sure

> Actually, I think we should keep ChildAt as safe, as it was before the
> regression and
> remove SafeChildAt.

if we really don't care about extra checks sure that's fine with me.
We have had the checks there since the beginning of time, and this isn't very hot code, so
I'd prefer keeping ChildAt safe.
(But still move to use observer array)
(In reply to Olli Pettay [:smaug] from comment #12)
> We have had the checks there since the beginning of time, and this isn't
> very hot code, so
> I'd prefer keeping ChildAt safe.
> (But still move to use observer array)

ok
Attachment #717252 - Flags: review?(bugs) → review+
Comment on attachment 717252 [details] [diff] [review]
bug 842078 - properly handle mutations to mChildList while iterating over it

[Security approval request comment]
How easily could an exploit be constructed based on the patch?

once you figure out the next question you need to create an exploit based on us calling methods on a deleted object

Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?

it's probably fairly obvious the rason for the change is to protect against mutation while iterating over an array combined with the fact is associated with a security bug that probably makes it obvious that its possible to create a out of bounds read.

Which older supported branches are affected by this flaw?

only aurora

If not all supported branches, which bug introduced the flaw?

bug 841434

Do you have backports for the affected branches? If not, how different, hard to create, and risky will they be?

shouldn't be more risky than this patch, and should apply with no trouble. backing out the causing bug should also be easy

How likely is this patch to cause regressions; how much testing does it need?

it's very unlikely but possible that it could introduce a null deref, and this fixes behaviour in the case of mutation while iterating which could also theoretically break something.
Attachment #717252 - Flags: sec-approval?
the right fix scares me a little mostly just because this is docshell stuff, and the real bug has probably existed since 99 so given we know how to fix it lets just back out for aurora
Comment on attachment 718012 [details] [diff] [review]
backout for aurora

[Approval Request Comment]
Bug caused by (feature/regressing bug #): 
User impact if declined: 
Testing completed (on m-c, etc.): 
Risk to taking this patch (and alternatives if risky): 
String or UUID changes made by this patch:
Attachment #718012 - Flags: approval-mozilla-aurora?
Attachment #717252 - Flags: sec-approval? → sec-approval+
(In reply to Trevor Saunders (:tbsaunde) from comment #17)
> Comment on attachment 718012 [details] [diff] [review]
> backout for aurora
> 
> [Approval Request Comment]
> Bug caused by (feature/regressing bug #): 
> User impact if declined:  exploitable bug could cause crashes
> Testing completed (on m-c, etc.):  none (branch only backout)
> Risk to taking this patch (and alternatives if risky): basically none, it brings us back to the behaviour we had forever.   we could take the patch to be landed on m-c that actually fixes the issue, but that seems more risky
> String or UUID changes made by this patch: none
Comment on attachment 718012 [details] [diff] [review]
backout for aurora

Approving backout of 841434, fixing a sec-crit bug.
Attachment #718012 - Flags: approval-mozilla-aurora? → approval-mozilla-aurora+
https://hg.mozilla.org/mozilla-central/rev/1444bada530a

Can we land a test for this?
Status: NEW → RESOLVED
Closed: 11 years ago
Flags: in-testsuite?
Resolution: --- → FIXED
Target Milestone: --- → mozilla22
> Can we land a test for this?

in theory certainly we should be able to test at least the Stop() change, but the test case is not great, and its really not clear what exactly causes us  to mutate the children list while we're iterating over it so it seems hard :/
Flags: sec-bounty? → sec-bounty+
Group: core-security
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: