Closed Bug 880544 Opened 11 years ago Closed 11 years ago

"ABORT: Tear-off objects remain in hashtable at shutdown" with watch on SVGPathSegList

Categories

(Core :: SVG, defect)

x86_64
macOS
defect
Not set
critical

Tracking

()

VERIFIED FIXED
mozilla25
Tracking Status
firefox24 --- wontfix
firefox25 --- verified
firefox-esr17 --- wontfix
firefox-esr24 --- wontfix
b2g18 --- wontfix
b2g-v1.1hd --- wontfix
b2g-v1.2 --- unaffected

People

(Reporter: jruderman, Assigned: jwatt)

Details

(4 keywords, Whiteboard: [adv-main25+])

Attachments

(6 files, 3 obsolete files)

Attached image testcase (asserts on shutdown) (obsolete) —
###!!! ABORT: Tear-off objects remain in hashtable at shutdown.: 'mTable.Count() == 0', file content/svg/content/src/nsSVGAttrTearoffTable.h, line 28

nsSVGAttrTearoffTable<void, mozilla::DOMSVGPathSegList>::~nsSVGAttrTearoffTable

Security-sensitive because last time I filed a bug involving this assertion (bug 872790), it was marked as a dup of a security-sensitive bug.
Marking sec-critical, because this smells like a use-after-free.  Who should look at this?  Sounds like jwatt is away for all of June.
Keywords: sec-critical
David, could you have a look?
Flags: needinfo?(dzbarsky)
(In reply to :Ms2ger from comment #2)
> David, could you have a look?

I'll add it to my todo list.
Flags: needinfo?(dzbarsky)
Assignee: nobody → dzbarsky
Any progress here, David?
Assignee: dzbarsky → nobody
Is this something you could look into, Brian?  It looks like you've made the most changes to this file.  Thanks.
I can have a look but I'm currently travelling so it won't be until the middle of next week at the earliest. If anyone else has time before then, please have a look.
Can someone please CC me on bug 872774?
Thanks!
It seems like the use of Object.prototype.watch is keeping a reference to the DOMSVGPathSegList and preventing it from being GC'ed even once the tab has been closed.

If I open this testcase in a tab, close the tab, open about:memory and click GC, CC and Minimize Memory Usage, the DOMSVGPathSegList dtor is still not called. As a result the DOMSVGPathSegList object stays in the hash table, and the assertion fires on shutdown.
Attachment #759529 - Attachment is obsolete: true
Okay, this comes down to the generated DOMProxyHandler::hasOwn implementation for DOMSVGPathSegList (called under obj_watch()):

bool
DOMProxyHandler::hasOwn(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, bool* bp)
{
  MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
            "Should not have a XrayWrapper here");

  int32_t index = GetArrayIndexFromId(cx, id);
  if (IsArrayIndex(index)) {
    mozilla::DOMSVGPathSegList* self = UnwrapProxy(proxy);
    bool found;
    ErrorResult rv;
    mozilla::DOMSVGPathSeg* result;
    result = self->IndexedGetter(index, found, rv);
    rv.WouldReportJSException();
    if (rv.Failed()) {
      return ThrowMethodFailedWithDetails<true>(cx, rv, "SVGPathSegList", "getItem");
    }
    (void)result;

    *bp = found;
    return true;
  }


  JS::Rooted<JSObject*> expando(cx, GetExpandoObject(proxy));
  if (expando) {
    JSBool b = true;
    JSBool ok = JS_HasPropertyById(cx, expando, id, &b);
    *bp = !!b;
    if (!ok || *bp) {
      return ok;
    }
  }

  *bp = false;
  return true;
}

'result' is a raw pointer, so the DOMSVGPathSeg is simply leaked and, since it holds a strong reference to its DOMSVGPathSegList, that object is leaked too, causing the assertion.
DOMProxyHandler::delete_ would seem to have a similar bug:

bool
DOMProxyHandler::delete_(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, bool* bp)
{
  MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
            "Should not have a XrayWrapper here");

  int32_t index = GetArrayIndexFromId(cx, id);
  if (IsArrayIndex(index)) {
    mozilla::DOMSVGPathSegList* self = UnwrapProxy(proxy);
    bool found;
    ErrorResult rv;
    mozilla::DOMSVGPathSeg* result;
    result = self->IndexedGetter(index, found, rv);
    rv.WouldReportJSException();
    if (rv.Failed()) {
      return ThrowMethodFailedWithDetails<true>(cx, rv, "SVGPathSegList", "getItem");
    }
    (void)result;
    if (found) {
      *bp = false;
    } else {
      *bp = true;
    }
    // We always return here, even if the property was not found
    return true;
  }


  return dom::DOMProxyHandler::delete_(cx, proxy, id, bp);
}
> 'result' is a raw pointer, so the DOMSVGPathSeg is simply leaked

Uh.... an IndexedGetter is supposed to return a raw pointer.  And looking at DOMSVGPathSegList::IndexedGetter it does:

300     return ItemAt(aIndex);

where ItemAt is:

225     return mItems[aIndex].mItem;

Where is the leak, exactly?
jwatt explained to me on IRC what's going on.  The leak is actually inside IndexedGetter when it does EnsureItemAt: that creates a new object with zero refcount and stores a raw pointer to it.  The assumption is that after that ItemAt() will get called and stored in a refptr, but that never happens.

I suspect the right fix here is to:

1)  Change EnsureItemAt to return an already_AddRefed<SVGPathSeg> so the caller makes
    sure to take ownership, since we depend on that.
2)  Remove the "'resultNotAddRefed': [ 'getItem' ]" from the Bindings.conf entry for
    SVGPathSegList.
3)  Change the signatures of SVGPathSegList::GetItem and SVGPathSegList::IndexedGetter to
    return an already_AddRefed<SVGPathSeg>.
If the object in the hash table is actually alive at shutdown, then it sounds like this isn't actually a security problem.
Oh, and:

4)  Audit/fix in the same way all the other SVG list classes that have
    "'resultNotAddRefed': [ 'getItem' ]" in Bindings.conf (looks like SVGLenthList,
    SVGNumberList, SVGPointList, SVGTransformList, at first glance).
(In reply to Andrew McCreight [:mccr8] from comment #15)
> If the object in the hash table is actually alive at shutdown, then it
> sounds like this isn't actually a security problem.

That seems to be the case. At least I see no indication that this is anything other than a memory leak.
(In reply to Jonathan Watt [:jwatt] from comment #17)
> Anyone working on this...

In fact, I'll take this.
Assignee: nobody → jwatt
For no particular reason I patched SVGTransformList first. I checked that this testcase asserts without the patch.

Passed Try: https://tbpl.mozilla.org/?tree=Try&rev=133191ea54bc

I'll wait until after r+ to upload the patches for the other types since any review comments may apply to them too.
Attachment #772639 - Flags: review?(dzbarsky)
Comment on attachment 772639 [details] [diff] [review]
patch for SVGTransformList

Review of attachment 772639 [details] [diff] [review]:
-----------------------------------------------------------------

Seems sane.
Attachment #772639 - Flags: review?(dzbarsky) → review+
Attachment #772950 - Flags: review?(dzbarsky)
Attachment #772951 - Flags: review?(dzbarsky)
Attached patch patch for SVGPathSegList (obsolete) — Splinter Review
Attachment #772952 - Flags: review?(dzbarsky)
Attached patch patch for SVGPointsList (obsolete) — Splinter Review
Attachment #772953 - Flags: review?(dzbarsky)
Hmm, looks like I forgot to |hg add| the tests for the SVGPathSegList and SVGPointsList patches.
Attachment #772952 - Attachment is obsolete: true
Attachment #772952 - Flags: review?(dzbarsky)
Attachment #773138 - Flags: review?(dzbarsky)
Attachment #772953 - Attachment is obsolete: true
Attachment #772953 - Flags: review?(dzbarsky)
Attachment #773139 - Flags: review?(dzbarsky)
Comment on attachment 773139 [details] [diff] [review]
patch for SVGPointsList

Review of attachment 773139 [details] [diff] [review]:
-----------------------------------------------------------------

::: content/svg/content/src/crashtests/880544-5.svg
@@ +11,5 @@
> +  //]]></script>
> +
> +  <polygon id="e" points="0,0"/>
> +
> +</svg>

Did you add the <![CDATA[ because you plan to submit these tests somewhere?  I don't think we want this for our own tests.
Attachment #773139 - Flags: review?(dzbarsky) → review+
(In reply to David Zbarsky (:dzbarsky) from comment #29)
> Did you add the <![CDATA[ because you plan to submit these tests somewhere? 
> I don't think we want this for our own tests.

We don't; I just didn't bother cutting that out of Jesse's original testcase. It doesn't matters though, and isn't rally worth spending time updating the patches for I'd think.
Attachment #772950 - Flags: review?(dzbarsky) → review+
Attachment #772951 - Flags: review?(dzbarsky) → review+
Attachment #773138 - Flags: review?(dzbarsky) → review+
Confirmed assertion on shutdown, ASan FF24 2013-06-06.
Verified fixed, ASan FF25 2013-10-01.
Status: RESOLVED → VERIFIED
Whiteboard: [adv-main25+]
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: