Nullptr deref in WebCore::RenderTreeBuilder::Block::attachIgnoringContinuation when parent and beforeChild are siblings
https://bugs.webkit.org/show_bug.cgi?id=212116
<rdar://problem/62993844>

Reviewed by Simon Fraser.

Source/WebCore:

This patch fixes the case when a nested fragmented context has a spanner and we try to form a continuation while this nested fragmented context is being destroyed.

1. The continuation is triggered by a style change that turns a previously out-of-flow block container into an inflow box
(and the parent inline level container can't have the box as a direct child anymore).
2. An unrelated style change nukes the nested fragmented context. We need to "re-assign" the spanner to the parent fragment.

These 2 changes are split into 2 phases; first we take care of the tree mutation triggered by the continuation (updateRendererStyle), while
we do the fragmented context cleanup (updateAfterDescendants) in a separate step.
This 2 phase setup confuses the "where to put this spanner" logic.

This patch addresses the issue by keeping the spanner inside the about-to-be-destroyed fragmented context while forming the continuation (phase #1) and let the second phase (updateAfterDescendants)
deal with the spanner moving.

Test: fast/multicol/nested-multicol-with-spanner-and-continuation.html

* rendering/updating/RenderTreeBuilderMultiColumn.cpp:
(WebCore::isValidColumnSpanner):

LayoutTests:

* fast/multicol/nested-multicol-with-spanner-and-continuation-expected.txt: Added.
* fast/multicol/nested-multicol-with-spanner-and-continuation.html: Added.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@262093 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 0ab3b50..9b153df 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,14 @@
+2020-05-22  Zalan Bujtas  <zalan@apple.com>
+
+        Nullptr deref in WebCore::RenderTreeBuilder::Block::attachIgnoringContinuation when parent and beforeChild are siblings
+        https://bugs.webkit.org/show_bug.cgi?id=212116
+        <rdar://problem/62993844>
+
+        Reviewed by Simon Fraser.
+
+        * fast/multicol/nested-multicol-with-spanner-and-continuation-expected.txt: Added.
+        * fast/multicol/nested-multicol-with-spanner-and-continuation.html: Added.
+
 2020-05-22  Kenneth Russell  <kbr@chromium.org>
 
         [ANGLE - iOS] webgl/1.0.3/conformance/extensions/ext-sRGB.html is failing
diff --git a/LayoutTests/fast/multicol/nested-multicol-with-spanner-and-continuation-expected.txt b/LayoutTests/fast/multicol/nested-multicol-with-spanner-and-continuation-expected.txt
new file mode 100644
index 0000000..4179f59
--- /dev/null
+++ b/LayoutTests/fast/multicol/nested-multicol-with-spanner-and-continuation-expected.txt
@@ -0,0 +1,2 @@
+PASS if no crash when the nested fragmented context is being destroyed with a spanner in it while forming a new continuation.
+
diff --git a/LayoutTests/fast/multicol/nested-multicol-with-spanner-and-continuation.html b/LayoutTests/fast/multicol/nested-multicol-with-spanner-and-continuation.html
new file mode 100644
index 0000000..bc24318
--- /dev/null
+++ b/LayoutTests/fast/multicol/nested-multicol-with-spanner-and-continuation.html
@@ -0,0 +1,17 @@
+<style>
+.container { 
+    columns: 2;
+}
+#innerContainer { 
+    position: absolute;
+    columns: 2;
+}
+</style>
+PASS if no crash when the nested fragmented context is being destroyed with a spanner in it while forming a new continuation.
+<div class=container><span><div id=innerContainer><div style="column-span: all;"></div></div></span></div><script>
+if (window.testRunner)
+    testRunner.dumpAsText();
+document.body.offsetHeight;
+innerContainer.style.position = "static";
+innerContainer.style.webkitColumns = "1";
+</script>
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 8f04a82..045beb2 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,29 @@
+2020-05-22  Zalan Bujtas  <zalan@apple.com>
+
+        Nullptr deref in WebCore::RenderTreeBuilder::Block::attachIgnoringContinuation when parent and beforeChild are siblings
+        https://bugs.webkit.org/show_bug.cgi?id=212116
+        <rdar://problem/62993844>
+
+        Reviewed by Simon Fraser.
+
+        This patch fixes the case when a nested fragmented context has a spanner and we try to form a continuation while this nested fragmented context is being destroyed.
+
+        1. The continuation is triggered by a style change that turns a previously out-of-flow block container into an inflow box
+        (and the parent inline level container can't have the box as a direct child anymore).
+        2. An unrelated style change nukes the nested fragmented context. We need to "re-assign" the spanner to the parent fragment.
+
+        These 2 changes are split into 2 phases; first we take care of the tree mutation triggered by the continuation (updateRendererStyle), while
+        we do the fragmented context cleanup (updateAfterDescendants) in a separate step.
+        This 2 phase setup confuses the "where to put this spanner" logic.
+
+        This patch addresses the issue by keeping the spanner inside the about-to-be-destroyed fragmented context while forming the continuation (phase #1) and let the second phase (updateAfterDescendants)
+        deal with the spanner moving.
+
+        Test: fast/multicol/nested-multicol-with-spanner-and-continuation.html
+
+        * rendering/updating/RenderTreeBuilderMultiColumn.cpp:
+        (WebCore::isValidColumnSpanner):
+
 2020-05-22  Chris Dumez  <cdumez@apple.com>
 
         Revoking an object URL immediately after triggering navigation causes navigation to fail
diff --git a/Source/WebCore/rendering/updating/RenderTreeBuilderMultiColumn.cpp b/Source/WebCore/rendering/updating/RenderTreeBuilderMultiColumn.cpp
index 8c1cf06..fb69567 100644
--- a/Source/WebCore/rendering/updating/RenderTreeBuilderMultiColumn.cpp
+++ b/Source/WebCore/rendering/updating/RenderTreeBuilderMultiColumn.cpp
@@ -107,9 +107,18 @@
             // implement (not to mention specify behavior).
             return ancestor == &fragmentedFlow;
         }
-        // This ancestor (descendent of the fragmentedFlow) will create columns later. The spanner belongs to it.
-        if (is<RenderBlockFlow>(*ancestor) && downcast<RenderBlockFlow>(*ancestor).willCreateColumns())
-            return false;
+        if (is<RenderBlockFlow>(*ancestor)) {
+            auto& blockFlowAncestor = downcast<RenderBlockFlow>(*ancestor);
+            if (blockFlowAncestor.willCreateColumns()) {
+                // This ancestor (descendent of the fragmentedFlow) will create columns later. The spanner belongs to it.
+                return false;
+            }
+            if (blockFlowAncestor.multiColumnFlow()) {
+                // While this ancestor (descendent of the fragmentedFlow) has a fragmented flow context, this context is being destroyed.
+                // However the spanner still belongs to it (will most likely be moved to the parent fragmented context as the next step).
+                return false;
+            }
+        }
         ASSERT(ancestor->style().columnSpan() != ColumnSpan::All || !isValidColumnSpanner(fragmentedFlow, *ancestor));
         if (ancestor->isUnsplittableForPagination())
             return false;