Closed Bug 359516 Opened 18 years ago Closed 17 years ago

"ASSERTION: unbalanced Will/DidModify calls" and scary crash involving DOMNodeRemoved and SVG filters [@ nsSVGUtils::RemoveObserver]

Categories

(Core :: SVG, defect)

PowerPC
macOS
defect
Not set
critical

Tracking

()

RESOLVED FIXED

People

(Reporter: jruderman, Assigned: tor)

References

Details

(Keywords: assertion, crash, testcase, Whiteboard: [sg:critical?] post 1.8-branch)

Crash Data

Attachments

(3 files, 4 obsolete files)

Loading the testcase causes:

###!!! ASSERTION: unbalanced Will/DidModify calls: 'mModifyNestCount>0', file /Users/admin/trunk/mozilla/content/svg/content/src/nsSVGValue.cpp, line 92

Reloading or quitting then causes a crash.  The top of the stack looks like:

Cannot access memory at address 0xdddddddd
0   CallQueryInterface<nsISupports, nsISVGValue>(nsISupports*, nsISVGValue**)
1   nsSVGUtils::RemoveObserver
2   FilterPropertyDtor
3   nsPropertyTable::PropertyList::DeletePropertyFor
...

Accessing memory at 0xdddddddd means the code read a pointer from a deleted object and then dereferenced it, so it's likely to be a security hole.
Attached image testcase
Whiteboard: [sg:critical?]
Summary: "ASSERTION: unbalanced Will/DidModify calls" and scary crash involving DOMNodeRemoved and SVG filters → "ASSERTION: unbalanced Will/DidModify calls" and scary crash involving DOMNodeRemoved and SVG filters [@ nsSVGUtils::RemoveObserver]
In an opt buiild, reloading triggers "pure virtual method called", a strange type of crash that doesn't trigger a normal crash dialog.
CCing people from bug 349880 and bug 344749, fixed crash bugs with the same stack signature, [@ nsSVGUtils::RemoveObserver].
Flags: blocking1.9?
In RemoveChildAt in the following function the existing filter frame is destroyed and a new one created.

nsresult
nsSVGFilterElement::RemoveChildAt(PRUint32 aIndex, PRBool aNotify)
{
  WillModify();
  nsresult rv = nsSVGFilterElementBase::RemoveChildAt(aIndex, aNotify);
  DidModify();

  return rv;
}

Thus WillModify and DidModify are called on different objects and then it all screws up from there. When the new filter frame is destroyed you get no notification and the path geometry frame crashes tries to clean reference the deleted filter frame.

Having said all this, I don't know how to fix it.
Attached patch possible approach to fix (obsolete) — Splinter Review
Assignee: general → tor
Status: NEW → ASSIGNED
Attached patch update to tip (obsolete) — Splinter Review
Attachment #246632 - Attachment is obsolete: true
Attached patch minor update (obsolete) — Splinter Review
Replaces hand-wired observer scheme for filters with nsIMutationObserver.
Attachment #246747 - Attachment is obsolete: true
Attachment #246852 - Flags: review?(bugmail)
Comment on attachment 246852 [details] [diff] [review]
minor update

Please don't call AddRef() and Release() directly, use NS_ADDREF/NS_RELEASE, or even better nsCOMPtrs.

>Index: layout/svg/base/src/nsSVGUtils.cpp

>+class nsSVGFilterProperty : public nsStubMutationObserver {
...
>+  nsCOMPtr<nsIContent> mObservedFilter;
>   nsISVGFilterFrame *mFilter;
>+  nsIFrame *mFrame;  // frame being filtered
>+  nsRect mFilterRect;

Since you're holding a strong reference to mObservedFilter NodeWillBeDestroyed will never be called. And are you sure this won't cause circular references?

with that fixed r/sr=me. Though you may want to get input from someone that knows this code better than I do.
Attachment #246852 - Flags: superreview+
Attachment #246852 - Flags: review?(bugmail)
Attachment #246852 - Flags: review+
Comment on attachment 246852 [details] [diff] [review]
minor update

>Index: layout/svg/base/src/nsSVGFilterFrame.cpp
>===================================================================
>RCS file: /cvsroot/mozilla/layout/svg/base/src/nsSVGFilterFrame.cpp,v
>retrieving revision 1.24
>diff -u -8 -p -r1.24 nsSVGFilterFrame.cpp
>--- layout/svg/base/src/nsSVGFilterFrame.cpp	27 Nov 2006 17:30:56 -0000	1.24
>+++ layout/svg/base/src/nsSVGFilterFrame.cpp	28 Nov 2006 20:59:05 -0000
>@@ -48,26 +48,22 @@

...

>    */
>   virtual nsIAtom* GetType() const;
> 
> private:
>@@ -108,21 +93,17 @@ private:
> private:
>   nsCOMPtr<nsIDOMSVGAnimatedEnumeration> mFilterUnits;
>   nsCOMPtr<nsIDOMSVGAnimatedEnumeration> mPrimitiveUnits;
>   nsCOMPtr<nsIDOMSVGAnimatedInteger> mFilterResX;
>   nsCOMPtr<nsIDOMSVGAnimatedInteger> mFilterResY;

These member variables could go now as we are not tracking them couldn't they. We could get the values from the element as and when necessary as we do in nsSVGMaskFrame.
(In reply to comment #9)
> >   nsCOMPtr<nsIDOMSVGAnimatedEnumeration> mFilterUnits;
> >   nsCOMPtr<nsIDOMSVGAnimatedEnumeration> mPrimitiveUnits;
> >   nsCOMPtr<nsIDOMSVGAnimatedInteger> mFilterResX;
> >   nsCOMPtr<nsIDOMSVGAnimatedInteger> mFilterResY;
> 
> These member variables could go now as we are not tracking them couldn't they.
> We could get the values from the element as and when necessary as we do in
> nsSVGMaskFrame.

I'd like to do that in a followup bug.

(In reply to comment #10)
> I'd like to do that in a followup bug.
> 

I have no problem with that for what it's worth.
(In reply to comment #8)
> (From update of attachment 246852 [details] [diff] [review])
> Please don't call AddRef() and Release() directly, use NS_ADDREF/NS_RELEASE, or
> even better nsCOMPtrs.

Switched for NS_ADDREF/NS_RELEASE - not sure what you meant by the comment of using nsCOMPtrs, as this object is being stored as a nsFrame property, which aren't refcounted.

> >Index: layout/svg/base/src/nsSVGUtils.cpp
> 
> >+class nsSVGFilterProperty : public nsStubMutationObserver {
> ...
> >+  nsCOMPtr<nsIContent> mObservedFilter;
> >   nsISVGFilterFrame *mFilter;
> >+  nsIFrame *mFrame;  // frame being filtered
> >+  nsRect mFilterRect;
> 
> Since you're holding a strong reference to mObservedFilter NodeWillBeDestroyed
> will never be called. And are you sure this won't cause circular references?

Yes, I think that might create a circular ref - switched to a weak ref.

> with that fixed r/sr=me. Though you may want to get input from someone that
> knows this code better than I do.

I plan on getting a second review from someone more familiar with the svg code.

Attachment #246852 - Attachment is obsolete: true
Attachment #250507 - Flags: review?(bugmail)
Comment on attachment 250507 [details] [diff] [review]
update to tip, don't addref/release directly, switch to weak ref

Does nsSVGFilterProperty not need to listen to CharacterDataChanged? It's called whenever the data in a textnode (or similar) is changed.

Use nsWeakRef rather than nsCOMPtr<nsIWeakReference>.

Is this going to add weak references to a lot of nodes? There are a couple of objects allocated for each weak reference to content nodes so it's not free. But if this is only one per filter or some such it should be fine.

You can use the already existing nsPropertyTable::SupportsDtorFunc function rather than creating a new FilterPropertyDtor. You just have to make sure that you're setting a valid nsISupports pointer. This happens to already be the case, but it'd be nice with an explicit cast to nsISupports before setting the property.

r/sr=sicking with that, though I'm leaving the r field for someone better at svg to look at this.
Attachment #250507 - Flags: review?(jonas) → superreview+
(In reply to comment #14)
> (From update of attachment 250507 [details] [diff] [review])
> Does nsSVGFilterProperty not need to listen to CharacterDataChanged? It's
> called whenever the data in a textnode (or similar) is changed.

No, filters don't depend on character data, just the attributes of the various filter elements.

> Is this going to add weak references to a lot of nodes? There are a couple of
> objects allocated for each weak reference to content nodes so it's not free.
> But if this is only one per filter or some such it should be fine.

It's one weak reference per use of a filter.  Filters aren't heavily uses, and tend to be applied to containers of objects rather than individual primitives, so there won't be too many weakrefs around.
Attachment #252928 - Flags: review?(roc)
How do you protect against nsSVGFilterProperty using a stale/dead mFrame, if the frame is destroyed/reconstructed while the content is still alive? Seems to me that we'll still hold a refcount to the nsSVGFilterProperty (because it's still listening for mutations) so it won't be destroyed.

Also,
+  mFrame->AddStateBits(NS_STATE_SVG_FILTERED);

I think this should be set in nsSVGUtils, because that's where we test for the property. It seems better to keep those operations together.
Moved the RemoveMutationObserver to nsSVGUtils::StyleEffects (need to rename that method one of these days), which is called by all SVG frames' Destroy method.

Also moved the AddStateBits per comment.
Attachment #252928 - Attachment is obsolete: true
Attachment #253656 - Flags: review?(roc)
Attachment #252928 - Flags: review?(roc)
Checked in.
Status: ASSIGNED → RESOLVED
Closed: 17 years ago
Resolution: --- → FIXED
Blocks: 374766
Since a fix for this has already been landed, it probably doesn't need to block 1.9. I tested this in a 1.8 branch debug build and didn't see the assertion.
Flags: blocking1.9?
Whiteboard: [sg:critical?] → [sg:critical?] post 1.8-branch
1.8 branch did not support SVG Filters.
Flags: wanted1.8.1.x-
Flags: wanted1.8.0.x-
Group: security
Crashtest checked in.
Flags: in-testsuite+
Crash Signature: [@ nsSVGUtils::RemoveObserver]
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: