Closed Bug 686044 Opened 13 years ago Closed 13 years ago

Crash with after path.pathSegList.appendItem and GC

Categories

(Core :: SVG, defect)

x86_64
All
defect
Not set
critical

Tracking

()

VERIFIED FIXED
mozilla9
Tracking Status
firefox7 - affected
firefox8 + fixed
firefox9 + fixed
firefox10 + fixed
status1.9.2 --- unaffected
status1.9.1 --- unaffected

People

(Reporter: jruderman, Assigned: dholbert)

References

Details

(4 keywords, Whiteboard: [sg:critical][qa!])

Crash Data

Attachments

(3 files, 1 obsolete file)

Security-sensitive becase:
* It's scary that GC is involved,
* the reduced testcase mysteriously requires a loop,
* I saw an (unsafe) "invalid array index" once.
Attached file stack trace
(In reply to Jesse Ruderman from comment #0)
> * It's scary that GC is involved,

(BTW: presumably this requires https://www.squarefree.com/extensions/domFuzzLite.xpi to reliably trigger the crash, given the fuzzPriv.GC() call)
I can repro on Linux64 with the XPI from comment 3. (Sorry for the outdated link in comment 2.)

So far, it always crashes for me when i hits 39, in the testcase's loop. (i.e. it crashes after "39" has been printed to my terminal).
OS: Mac OS X → All
So, in this chunk of code (with aValue == "M4,4")...
> 51 SVGAnimatedPathSegList::SetBaseValueString(const nsAString& aValue)
> 52 {
[...]
> 67   DOMSVGPathSegList *baseValWrapper =
> 68     DOMSVGPathSegList::GetDOMWrapperIfExists(GetBaseValKey());
> 69   if (baseValWrapper) {
> 70     baseValWrapper->InternalListWillChangeTo(newBaseValue);
> 71   }
https://mxr.mozilla.org/mozilla-central/source/content/svg/content/src/SVGAnimatedPathSegList.cpp#67

What's happening is:
 - We do have an existing baseValWrapper.
 - We hit line 70, the call to InternalListWillChangeTo.
 - This removes the last reference to |baseValWrapper|
 - In so doing, we call RemovingFromList() on its only entry, and remove the last reference to it.  (see code below)
 - So |baseValWrapper| dies while we're still in the middle of a method running on it.

We actually have code for a kung-fu death-grip in place here, but we're not activating the death-grip because our new list's length (3) is larger than our old length (1), so we think it's unnecessary:
> 158   nsRefPtr<DOMSVGPathSegList> kungFuDeathGrip;
> 159   if (aNewValue.Length() < length) {
> 160     // RemovingFromList() might clear last reference to |this|.
> 161     // Retain a temporary reference to keep from dying before returning.
> 162     kungFuDeathGrip = this;
> 163   }
> 164 
> 165   while (index < length && dataIndex < dataLength) {
> 166     newSegType = SVGPathSegUtils::DecodeType(aNewValue.mData[dataIndex]);
> 167     if (ItemAt(index) && ItemAt(index)->Type() != newSegType) {
> 168       ItemAt(index)->RemovingFromList();

https://mxr.mozilla.org/mozilla-central/source/content/svg/content/src/DOMSVGPathSegList.cpp#158

I don't immediately remember why we think the death-grip is unnecessary in that case, though... Clearly it would help to prevent the crash in this testcase.  (maybe that's because something else is failing too, though.)
(In reply to Daniel Holbert [:dholbert] (vacation 9/17-9/24) from comment #5)
> What's happening is:
>  - We do have an existing baseValWrapper.
>  - We hit line 70, the call to InternalListWillChangeTo.
>  - This removes the last reference to |baseValWrapper|
>  - In so doing, we call RemovingFromList() on its only entry, and remove the
> last reference to it.  (see code below)
>  - So |baseValWrapper| dies while we're still in the middle of a method
> running on it.

(Sorry, ignore the 3rd bullet-point there ("This removes the last...") -- I clarified what I meant by it in the 4th bullet point, and then forgot to delete the original un-clarified version.)
As noted in bug 669584 comment #0:
> The fix for bug 639728 forgot that items are created lazily. Checking that
> the list length is going to change to zero is not enough. The last remaining
> reference to the object may be a single item at an arbitrary index
[...NOTE: that's true here -- and the arbitrary index happens to be index 0...]
> so really we have to check whether the list length is decreasing.

In this case, there's the additional complication that we **drop** the old DOMSVGPathSeg if the segment type has changed, per line 167 quoted in comment 5.  (We don't do that for other list-types, because they don't have the "different segment types have different data lengths" complication that paths have.)

So I suspect that for DOMSVGPathSegList, we want to *always* use a death-grip, regardless of whether the list-length is decreasing or increasing.
Attached patch fix (obsolete) — Splinter Review
(In reply to Daniel Holbert [:dholbert] (vacation 9/17-9/24) from comment #7)
> So I suspect that for DOMSVGPathSegList, we want to *always* use a
> death-grip, regardless of whether the list-length is decreasing or
> increasing.

Actually not quite "always" -- we only want the death-grip if we've got a nonzero length.  (If we deathgrip unconditionally, we delete ourselves while we're still inside in our constructor -- because we hit this code & add/remove the deathgrip & delete ourselves, all before the code that created us has gotten a chance to |NS_ADDREF| the newly-constructed object)

So -- this patch adds a deathgrip if(length).
Attachment #559646 - Flags: review?(jwatt)
(Marking "depends-on" bug 669584.  Absolutely not a regression from that bug, but still related, as the patch here further-relaxes an "if" check that was partially-relaxed in that bug.)
Assignee: nobody → dholbert
Depends on: 669584
Whiteboard: [sg:critical]
Comment on attachment 559646 [details] [diff] [review]
fix

r=jwatt with f2f comments addressed (see upcoming commit from dholbert).
Attachment #559646 - Flags: review?(jwatt) → review+
Added explanatory comment per jwatt's request.

I'll land this later today.  (It's currently going through TryServer with another patch I want to land, just as an absolute sanity-check since I can't treewatch-after-landing effectively during the F2F. :))
Attachment #559646 - Attachment is obsolete: true
https://hg.mozilla.org/integration/mozilla-inbound/rev/6806beccdeaf
Status: NEW → ASSIGNED
Target Milestone: --- → mozilla9
Whiteboard: [sg:critical] → [sg:critical][inbound]
https://hg.mozilla.org/mozilla-central/rev/6806beccdeaf
Status: ASSIGNED → RESOLVED
Closed: 13 years ago
Resolution: --- → FIXED
Whiteboard: [sg:critical][inbound] → [sg:critical]
Comment on attachment 560424 [details] [diff] [review]
fix w/ added comment [r=jwatt]

Requesting approval to land on Beta & Aurora channels.

Risk Assessment:
 - Targeted fix (one-liner, ignoring comments)
 - Basically as low-risk as it gets. As noted in bug 639728 comment 20 (which added the original code that this bug extends), this has zero expected behavior-change, aside from fixing crashes.  It just adds a kungFuDeathGrip in one (non-recursive) function, to keep something alive until the function returns.

Reward:
 - Fixes an sg:crit, which is probably unlikely to be triggered by "real" web content, but which bad guys could be poking at now that this has landed on trunk.

I know release-train-bump-day is coming up in just over a week -- if we haven't already spun up our final Firefox 7 builds (or if we have, but we end up respinning them), it would be great to get this in.

(Minor logistical complication: I'm about to leave on a camping trip in an off-the-grid backwoodsy area, so I won't be able to land this on the branches myself, once approval is granted.  I'm hoping jwatt or someone else can take care of that for me. :))
Attachment #560424 - Flags: approval-mozilla-beta?
Attachment #560424 - Flags: approval-mozilla-aurora?
Unless there is a reason why we would expect this to discovered externally we will wait as we are late in the game for Firefox 7 on beta. Denying approval for beta
Attachment #560424 - Flags: approval-mozilla-beta? → approval-mozilla-beta-
Attachment #560424 - Flags: approval-mozilla-aurora? → approval-mozilla-aurora+
Attachment #560424 - Flags: approval-mozilla-beta- → approval-mozilla-beta?
(In reply to Christian Legnitto [:LegNeato] from comment #15)
> Unless there is a reason why we would expect this to discovered externally
> we will wait as we are late in the game for Firefox 7 on beta. Denying
> approval for beta

It seems to me that the push to aurora makes this very discoverable externally. We're pretty sure that malicious people are monitoring pushes containing bug numbers that can't be accessed (a good indication of a security fix), and reverse engineering exploits from those pushes. Hence renominating for beta. As Daniel said, this should be zero risk.
Attachment #560424 - Flags: approval-mozilla-beta? → approval-mozilla-beta-
qa+ for verification in Firefox 8 and 9 using the attached testcase.
Whiteboard: [sg:critical] → [sg:critical][qa+]
Note: as with related bug 669584 & bug 639728, this shouldn't affect 3.6.x.  These are all bugs in SVG list code that was completely rewritten for Firefox 4.

Hence, setting status-1.9.* = unaffected.
FWIW, I tried to test this in Firefox 3.6.x to be absoulutely-positively sure of Comment 19, but I can't, because dom fuzz lite doesn't support 3.6.x.  (However, Jesse did give me a custom dom fuzz lite to try[1], with a different GC command[2], and I didn't hit the bug with that.)

From reading through the analogous 3.6.x code[3], it looks like our internal SVG list objects are the same as the heavyweight DOM objects -- whereas in Firefox >4, we create tearoffs when necessary for DOM objects.   Without the on-the-fly tearoffs being created, there's no chance of this bug happening.

So, I'm confident that 1.9.*=unaffected is correct.

[1] https://www.squarefree.com/extensions/domFuzzLite-for-firefox-3.6.xpi
[2] var evt = document.createEvent("Events"); evt.initEvent("please-gc", true, false); document.dispatchEvent(evt);
[3] http://mxr.mozilla.org/firefox/source/content/svg/content/src/nsSVGPathSegList.cpp
Verified fixed in 11.0a1 (2011-12-01).
Status: RESOLVED → VERIFIED
Verified fixed in Mozilla/5.0 (Windows NT 6.1; rv:9.0) Gecko/20100101 Firefox/9.0
Keywords: verified-beta
Verified fixed in Mozilla/5.0 (Windows NT 6.1; rv:10.0a2) Gecko/20111205 Firefox/10.0a2
Keywords: verified-aurora
Whiteboard: [sg:critical][qa+] → [sg:critical][qa!]
Fwiw, it's better to have testcase to not rely on extensions. It makes testing in older builds, across version, etc, easier.
Martijn, how do you recommend forcing GC from a testcase?
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: