Bug 1736243 Comment 16 Edit History

Note: The actual edited comment in the bug view page will always show the original commenter’s name and original timestamp.

A bit more about what's happening here:

- We call ReflowSVGNonDisplayText for the SVG Text frame with id #a.
- This invalidates its rendering observers (a collection of SVGTextPathObserver objects), pulling them into a local variable like so in [`SVGRenderingObserverSet::InvalidateAll`](https://searchfox.org/mozilla-central/rev/080e18fa4748456003164f58b0d925b8c3826a67/layout/svg/SVGObserverUtils.cpp#1058,1063-1067):
```c++
  const auto observers = std::move(mObservers);

  for (const auto& observer : observers) {
    observer->OnNonDOMMutationRenderingChange();
  }
```

- Its first rendering observer is the next SVG text frame.   As part of invalidating there, we call ReflowSVGNonDisplayText() on that next text frame.
- That next text frame has two lines in its anonymous block.  During this reflow, it deletes a no-longer-needed next-continuation for its textPath's inline-frame (deleting the part that was in the second line).
- While deleting this frame, we of course have to delete all of its frame-properties, including its SVGTextPathObserver.  **This is bad since we are currently iterating over `observers` (many stack levels up) which includes a pointer to this object, which it hasn't yet reached in its iteration.**
- In the SVGTextPathObserver destructor, SVGTextPathObserver attempts to unregister from the observation target (for safety, so that it doesn't get a callback after it's been deleted).  But this fails because the observation target doesn't have the observer in its `mObservers` collection anymore -- it has moved to the local `observers` variable, which we don't have access to modify.
- Later on, the above-quoted ` observer : observers` presumably reaches the now-deleted SVGTextPathObserver, and triggers a use-after-free and we crash.
A bit more about what's happening here:

- We call ReflowSVGNonDisplayText for the SVG Text frame with id `a`.
- This invalidates its rendering observers (a collection of `SVGTextPathObserver` objects), pulling them into a local variable like so in [`SVGRenderingObserverSet::InvalidateAll`](https://searchfox.org/mozilla-central/rev/080e18fa4748456003164f58b0d925b8c3826a67/layout/svg/SVGObserverUtils.cpp#1058,1063-1067):
```c++
  const auto observers = std::move(mObservers);

  for (const auto& observer : observers) {
    observer->OnNonDOMMutationRenderingChange();
  }
```

- Its first rendering observer is the next SVG text frame.   As part of invalidating there, we call ReflowSVGNonDisplayText() on that next text frame.
- That next text frame has two lines in its anonymous block.  During this reflow, it deletes a no-longer-needed next-continuation for its textPath's inline-frame (deleting the part that was in the second line).
- While deleting this frame, we of course have to delete all of its frame-properties, including its SVGTextPathObserver.  **This is bad since we are currently iterating over `observers` (many stack levels up) which includes a pointer to this object, which it hasn't yet reached in its iteration.**
- In the SVGTextPathObserver destructor, SVGTextPathObserver attempts to unregister from the observation target (for safety, so that it doesn't get a callback after it's been deleted).  But this fails because the observation target doesn't have the observer in its `mObservers` collection anymore -- it has moved to the local `observers` variable, which we don't have access to modify.
- Later on, the above-quoted `for (const auto& observer : observers) {` loop presumably reaches the now-deleted `SVGTextPathObserver` (which still has a straggling pointer in the `observers` collection).  We use that pointer and trigger a use-after-free when using it, and we crash.
A bit more about what's happening here:

- We call ReflowSVGNonDisplayText for the SVG Text frame with id `a`.
- This invalidates its rendering observers (a collection of `SVGTextPathObserver` objects), pulling them into a local variable like so in [`SVGRenderingObserverSet::InvalidateAll`](https://searchfox.org/mozilla-central/rev/080e18fa4748456003164f58b0d925b8c3826a67/layout/svg/SVGObserverUtils.cpp#1058,1063-1067):
```c++
  const auto observers = std::move(mObservers);

  for (const auto& observer : observers) {
    observer->OnNonDOMMutationRenderingChange();
  }
```

- Its first rendering observer is the next SVG text frame.   As part of invalidating there, we call ReflowSVGNonDisplayText() on that next text frame.
- That next text frame has two lines in its anonymous block.  During this reflow, it deletes a no-longer-needed next-continuation for its textPath's inline-frame (deleting the part that was in the second line).
- While deleting this frame, we of course have to delete all of its frame-properties, including its SVGTextPathObserver.  **This is bad since we are currently iterating over `observers` (many stack levels up) which includes a pointer to this object, which it hasn't yet reached in its iteration.**
- In the SVGTextPathObserver destructor, SVGTextPathObserver attempts to unregister from the observation target (for safety, so that it doesn't get a callback after it's been deleted).  But this fails because the observation target doesn't have the observer in its `mObservers` collection anymore -- it has moved to the local `observers` variable, which we don't have access to modify.
- Later on, the above-quoted `for (const auto& observer : observers) {` loop presumably reaches the now-deleted `SVGTextPathObserver` (which still has a straggling pointer that was left behind in the `observers` collection).  We use that pointer and trigger a use-after-free when using it, and we crash.

Back to Bug 1736243 Comment 16