Closed Bug 691785 Opened 9 years ago Closed 8 years ago

Crash (NULL pointer dereference) [@ nsObjectLoadingContent::IsSuccessfulRequest(nsIRequest*) ]


(Core :: DOM: Core & HTML, defect)

Not set



Tracking Status
firefox7 --- wontfix
firefox8 --- wontfix
firefox9 --- affected
firefox10 --- fixed
blocking1.9.2 --- .26+
status1.9.2 --- wanted


(Reporter: rh01, Assigned: jst)


(Blocks 1 open bug)


(Keywords: crash, testcase, Whiteboard: [sg:dos][qa+])

Crash Data


(2 files)

When running the attached testcase file, firefox 3.6.23 crashes
in Linux(Ubuntu 10.04) and Windows XP caused by a NULL 
pointer dereference. I assume all OS platforms are affected.

Crash ID Windows XP:

Ubuntu 10.04:

Program received signal SIGSEGV, Segmentation fault.
0x01057804 in nsObjectLoadingContent::IsSuccessfulRequest (aRequest=0x0)
    at /mozilla-1.9.2/content/base/src/nsObjectLoadingContent.cpp:1520
1520      nsresult rv = aRequest->GetStatus(&status);

(gdb) bt 5
#0  0x01057804 in nsObjectLoadingContent::IsSuccessfulRequest (aRequest=0x0)
    at /mozilla-1.9.2/content/base/src/nsObjectLoadingContent.cpp:1520
#1  0x01054086 in nsObjectLoadingContent::OnStartRequest (this=0xb00d84e8, aRequest=0x0, aContext=0xac39b8f0)
    at /mozilla-1.9.2/content/base/src/nsObjectLoadingContent.cpp:506
#2  0x01d4935d in NS_InvokeByIndex_P () from ./
#3  0x00a278af in XPCWrappedNative::CallMethod (ccx=..., mode=CALL_METHOD)
    at /mozilla-1.9.2/js/src/xpconnect/src/xpcwrappednative.cpp:2722
#4  0x00a35027 in XPC_WN_CallMethod (cx=0xb2beb000, obj=0xb4feb220, argc=2, argv=0xb2dda0b8, vp=0xbf8971e4)
    at /mozilla-1.9.2/js/src/xpconnect/src/xpcwrappednativejsops.cpp:1740

Using an object html tag, the nsIRequestObserver interface can be 
queried via QueryInterface of <object>. Then the method OnStartRequest can be called in javascript using null for aRequest (see testcase.html).

In /mozilla-1.9.2/content/base/src/nsObjectLoadingContent.cpp :

490     // nsIRequestObserver
492     nsObjectLoadingContent::OnStartRequest(nsIRequest *aRequest,
493                                            nsISupports *aContext)
494     {
495       if (aRequest != mChannel) {
496         // This is a bit of an edge case - happens when a new load starts before the
497         // previous one got here
498         return NS_BINDING_ABORTED;
499       }
501       // We're done with the classifier
502       mClassifier = nsnull;
504       AutoNotifier notifier(this, PR_TRUE);
506       if (!IsSuccessfulRequest(aRequest)) {
507         LOG(("OBJLC [%p]: OnStartRequest: Request failed\n", this));
508         Fallback(PR_FALSE);
509         return NS_BINDING_ABORTED;
510       }

As mChannel == 0x0 and aRequest == 0x0 the aborting in line 495 is not
In line 506 IsSuccessfulRequest is called with aRequest = 0x0:

1516    /* static */ PRBool
1517    nsObjectLoadingContent::IsSuccessfulRequest(nsIRequest* aRequest)
1518    {
1519      nsresult status;
1520      nsresult rv = aRequest->GetStatus(&status);
1521      if (NS_FAILED(rv) || NS_FAILED(status)) {
1522        return PR_FALSE;
1523      }

This leads to the NULL pointer dereference when trying to call the GetStatus 
method of aRequest in line 1520:

(gdb) i r
eax            0x0      0
ecx            0xb00d8508       -1341291256
edx            0xbf896b60       -1081513120
ebx            0x23c7ff4        37519348
esp            0xbf896ab4       0xbf896ab4
ebp            0xbf896aec       0xbf896aec
esi            0xac373594       -1405667948
edi            0xb4feb220       -1258376672
eip            0x1057804        0x1057804 <nsObjectLoadingContent::IsSuccessfulRequest(nsIRequest*)+10>
eflags         0x210286 [ PF SF IF RF ID ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51

(gdb) disas
Dump of assembler code for function _ZN22nsObjectLoadingContent19IsSuccessfulRequestEP10nsIRequest:
   0x010577fa <+0>:     push   %ebp
   0x010577fb <+1>:     mov    %esp,%ebp
   0x010577fd <+3>:     push   %esi
   0x010577fe <+4>:     sub    $0x34,%esp
   0x01057801 <+7>:     mov    0x8(%ebp),%eax
=> 0x01057804 <+10>:    mov    (%eax),%eax
   0x01057806 <+12>:    add    $0x14,%eax
   0x01057809 <+15>:    mov    (%eax),%edx
   0x0105780b <+17>:    lea    -0xc(%ebp),%eax

Steps to reproduce:
1) Open testcase file
2) Click the button

The simplest patch would be to nullcheck aRequest in 
/mozilla-1.9.2/content/base/src/nsObjectLoadingContent.cpp line 495:
<   if (aRequest != mChannel) {
>   if (aRequest != mChannel || !aRequest) {

I assume this is a [sg:dos], but I don't know yet if other attack vectors
are possible 
1) Filling mChannel with a custom nsIRequest object via data=".." in the html object tag,
2) setting an aRequest pointer equal to mChannel and passing it to OnStartRequest and 
3) executing arbitrary code via aRequest->GetStatus(&status) 
There are already bugs which address issues with scripts
using random interfaces, and increasing attack surface 
(bug 605271 and references in its description, bug 634986, bug 634983).
Some of them are [sg:critical?] and [sg:critical] and can be used 
to execute arbitrary code.

I would appreciate, if you correct me in case something is wrong with this
analysis or if you add something to it. Thanks :)

Attachment #564567 - Attachment mime type: text/plain → text/html
I wrongly was assuming the problem with QueryInterface exists only in the 3.6 branch so i didn't test higher versions. But I realized that higher versions are also affected:

WinXP Fx 10.0a1 nightly:

WinXP Fx 7.0.1:

Linux (x86_64) Fx 10.0a1

backported Iceweasel 7.0.1 in Debian squeeze crashes aswell (no crashreporter).
Version: 1.9.2 Branch → Trunk
Attachment #564567 - Attachment description: testcase to crash firefox 3.6.23 → testcase to crash firefox
Would we be able to QI to a fake request and do Bad Stuff, or would wrappers save us? What other functions can we call to do unexpected stuff? The attached testcase is sg:dos but we'll wait to investigate other possibilities.

Jst thinks we need something like the fix in bug 604262 while we're waiting for bug 605271 to save us.
Ever confirmed: true
Well, at least on trunk, the only way we can get past the first conditional is if aRequest == mChannel, which can only happen if mChannel is null, so it's DOS only there.  I didn't look at the other branches.  Note that it's totally possible that there are security issues associated with other methods on this interface, or with other interfaces nsObjectLoadingContent implements.  This needs an audit ...

I really hate XPConnect sometimes.
The non-classinfo-interfaces entry points from script on nsObjectLoadingContent are:

sg:dos. See comment 3.

Unknown.  If we pass in a non-null request (e.g. a fake object) we'll bounce out of the function with NS_BINDING_ABORTED.  If we pass in a null request and mChannel is already null (and mFinalListener is non-null) we'll pass an arbitrary attacker-controlled nsISupports to mFinalListener::OnStopRequest.  This needs further investigation by somebody who knows this code for:

1) Can mFinalListener ever be non-null if mChannel is null.
2) Can all listeners that might be mFinalListener handle a null aRequest without crashing?
3) Can all listeners that might be mFinalListener not do bad things with an attacker-controlled aContext that does crazy stuff?

Probably nothing: Can't fake an nsIInputStream through XPConnect (it has noscript methods) so unless there are DOM objects implementing nsIInputStream that hostile script can pass in here this method can't be called.  If the script can get its hands on an nsIInputStream, then this has the same problems as the previous method.

Nothing: This is a getter returning an object that should not have class info and should fail.

Nothing: Method is not implemented.

Probably nothing: String getter, shouldn't hurt anything.

Probably nothing: Integer getter, shouldn't hurt anything.

Probably nothing: Method can be called, but shouldn't hurt anything.

Nothing: method is harmless.

Nothing: We'll error out on the first conditional every time.

Hope that was useful.  Somebody who knows this code better should investigate OnDataAvailable/OnStopRequest.
Whiteboard: investigate OnDataAvailable/OnStopRequest.
Thanks for the investigation Kyle! I'll take this bug and dig into the remaining methods.
Assignee: nobody → jst
This bug here I found with a custom dumb fuzzer. I could also reproduce bug 634986 and bug 604262. It was kind of messy and worked only with Fx 3.6 so I rewrote it. If it helps, I can provide it here. It enumerates interfaces on some html elements as well as the interfaces functions and tries to call them.
Attached patch Fix.Splinter Review
I believe this covers what's important here. It's not even clear that this is all necessary, but better to be safe than sorry until we have a system in place that lets us do this right.
Attachment #570196 - Flags: review?(khuey)
Comment on attachment 570196 [details] [diff] [review]

Review of attachment 570196 [details] [diff] [review]:

I worry that we'll see fallout like Bug 631421 from this, but I'm not sure that there's a better way to find that fallout other than checking this in :-/

::: content/base/src/nsObjectLoadingContent.cpp
@@ +802,5 @@
> +nsObjectLoadingContent::OnDataAvailable(nsIRequest *aRequest,
> +                                        nsISupports *aContext,
> +                                        nsIInputStream *aInputStream,
> +                                        PRUint32 aOffset, PRUint32 aCount)

Ah, much nicer.
Attachment #570196 - Flags: review?(khuey) → review+
Yup, perfectly valid worry :(

One thing that makes me feel a little bit better about this than what happened in bug 604262 is that this passed on try right away whereas the fix for bug 604262 took many attempts and many fixes in random places before we were able to land it. But by a little, I really do mean only a little.
I don't think there's any security risk here from what I can tell in the code, so I'm marking this an sg:dos.
Whiteboard: investigate OnDataAvailable/OnStopRequest. → [sg:dos]
Keywords: checkin-needed

reproduces this on Beta/9 Mac OS X. Note the source in each:

<object id="dupa">
Blocks: 532972
Whiteboard: [sg:dos] → [sg:dos][qa+]
We should try to land this in 3.6.26
blocking1.9.2: --- → .26+
Whiteboard: [sg:dos][qa+] → [sg:dos][qa+] [c-n: for 1.9.2
Group: core-security
Removing the checkin-needed since 1.9.2 is no longer supported.
Keywords: checkin-needed
Whiteboard: [sg:dos][qa+] [c-n: for 1.9.2 → [sg:dos][qa+]
You need to log in before you can comment on or make changes to this bug.