| <!DOCTYPE html> |
| <div id="container"> |
| <span id="inlineBlock" style="display:inline-block; width:50px;"> |
| <div> |
| <br> |
| <span style="padding-left:30px;"><span style="padding-left:30px;"><div id="victim" style="float:left;"></div> |
| </span></span> |
| </div> |
| </span> |
| <div id="intruder" style="display:none;"></div> |
| </div> |
| <script src="../../../resources/testharness.js"></script> |
| <script src="../../../resources/testharnessreport.js"></script> |
| <script> |
| test(() => { |
| // The comments here assume that crbug.com/835613 is unfixed. |
| |
| // Trigger layout. |
| document.body.offsetTop; |
| |
| // Make two specific tree changes in the same style |
| // update. Inserting #intruder will require #container to have block |
| // children (it had inline children up until this point). It will |
| // have to insert an anonymous block around #inlineBlock. As part of |
| // inserting an anonymous block, all the children that get wrapped |
| // inside it will have their FloatingObjects cleared. The code that |
| // does this is in LayoutBoxModelObject::MoveChildTo(). It calls |
| // RemoveFloatingObjectsFromDescendants() (although that's really |
| // there to cope with a completely different scenario than this, and |
| // it's actually pretty unnecessary to clear the float in this case |
| // (the float is even inside a new formatting context)). Anyway, the |
| // bug is that we forget about RootInlineBox objects that may hold |
| // pointers to the FloatingObjects that it's removing. |
| // |
| // Then #intruder can be inserted after the new anonymous block. |
| intruder.style.display = "block"; |
| |
| // When we move on and remove #victim (the float), it will attempt |
| // to remove its FloatingObject(s). |
| // LayoutBlockFlow::MarkAllDescendantsWithFloatsForLayout() will |
| // call RemoveFloatingObject() to look for the FloatingObject, but |
| // it's already gone! So we won't get to the part where we'd |
| // otherwise mark the line that contains the FloatingObject dirty. |
| // It requires a rather special tree [1] to actually trigger a |
| // read-after-free, though. |
| // |
| // [1] The ex-float must be adjacent to an inline that's inside |
| // another inline, and the sum of the inline-start border/padding of |
| // those inlines must be larger than the inline size of the |
| // containing block. Aaand we need a line in front of all this |
| // (hence the BR). |
| victim.style.display = "none"; |
| }, "PASS if no assertion failure or heap-use-after-free"); |
| </script> |