Title: [203729] trunk
Revision
203729
Author
[email protected]
Date
2016-07-26 11:58:32 -0700 (Tue, 26 Jul 2016)

Log Message

Infinite Canvas context save() causes WebKit to crash
https://bugs.webkit.org/show_bug.cgi?id=159586
<rdar://problem/26759984>

Patch by Said Abou-Hallawa <[email protected]> on 2016-07-26
Reviewed by Simon Fraser.

Source/WebCore:

Limit the size of the canvas context state stack to 1024 * 16 saves. All
the saves which come after that limit will stay unrealized. The restore()
should not have any effect till there is no unrealized saves.

Test: fast/canvas/canvas-context-save-limit.html

* html/canvas/CanvasRenderingContext2D.cpp:
(WebCore::CanvasRenderingContext2D::realizeSaves):
(WebCore::CanvasRenderingContext2D::realizeSavesLoop):
* html/canvas/CanvasRenderingContext2D.h:

LayoutTests:

* fast/canvas/canvas-context-save-limit-expected.txt: Added.
* fast/canvas/canvas-context-save-limit.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (203728 => 203729)


--- trunk/LayoutTests/ChangeLog	2016-07-26 18:00:57 UTC (rev 203728)
+++ trunk/LayoutTests/ChangeLog	2016-07-26 18:58:32 UTC (rev 203729)
@@ -1,3 +1,14 @@
+2016-07-26  Said Abou-Hallawa  <[email protected]>
+
+        Infinite Canvas context save() causes WebKit to crash
+        https://bugs.webkit.org/show_bug.cgi?id=159586
+        <rdar://problem/26759984>
+
+        Reviewed by Simon Fraser.
+
+        * fast/canvas/canvas-context-save-limit-expected.txt: Added.
+        * fast/canvas/canvas-context-save-limit.html: Added.
+
 2016-07-26  Youenn Fablet  <[email protected]>
 
         DOMTokenList should be iterable

Added: trunk/LayoutTests/fast/canvas/canvas-context-save-limit-expected.txt (0 => 203729)


--- trunk/LayoutTests/fast/canvas/canvas-context-save-limit-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/canvas/canvas-context-save-limit-expected.txt	2016-07-26 18:58:32 UTC (rev 203729)
@@ -0,0 +1,14 @@
+CONSOLE MESSAGE: line 29: CanvasRenderingContext2D.save() has been called without a matching restore() too many times. Ignoring save().
+CONSOLE MESSAGE: line 29: CanvasRenderingContext2D.save() has been called without a matching restore() too many times. Ignoring save().
+This test verifies that the size of CanvasRenderingContext2D state stack does not grow beyond a certain limit.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS ctx.fillStyle is "#ff0000"
+PASS ctx.fillStyle is "#0000ff"
+PASS ctx.fillStyle is "#00ff00"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/canvas/canvas-context-save-limit.html (0 => 203729)


--- trunk/LayoutTests/fast/canvas/canvas-context-save-limit.html	                        (rev 0)
+++ trunk/LayoutTests/fast/canvas/canvas-context-save-limit.html	2016-07-26 18:58:32 UTC (rev 203729)
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+    <script src=""
+</head>
+<body>
+    <canvas id="canvas"/>
+    <script>
+        description("This test verifies that the size of CanvasRenderingContext2D state stack does not grow beyond a certain limit.");
+        const RED = '#ff0000';
+        const GREEN = '#00ff00';
+        const BLUE = '#0000ff';
+        const SAVE_STACK_SIZE = 1024 * 16;
+        const MAX_SAVES = SAVE_STACK_SIZE - 1;
+
+        var c = document.getElementById("canvas");
+        var ctx = c.getContext("2d");
+
+        for (var i = 0; i < MAX_SAVES * 2; ++i) {
+            if (!i || i >= MAX_SAVES)
+                ctx.fillStyle = GREEN;
+            else if (i == MAX_SAVES - 1)
+                ctx.fillStyle = BLUE;
+            else
+                ctx.fillStyle = RED;
+            ctx.save();
+        }
+
+        ctx.fillStyle = RED;
+
+        for (var i = MAX_SAVES * 2 - 1; i >= 0; --i) {
+            ctx.restore();   
+            if (i == MAX_SAVES * 2 - 1)
+                shouldBeEqualToString('ctx.fillStyle', RED);
+            else if (i == MAX_SAVES - 1)
+                shouldBeEqualToString('ctx.fillStyle', BLUE);
+            else if (!i)
+                shouldBeEqualToString('ctx.fillStyle', GREEN);
+        }
+    </script>
+    <script src=""
+</body>
+</html>
\ No newline at end of file

Modified: trunk/Source/WebCore/ChangeLog (203728 => 203729)


--- trunk/Source/WebCore/ChangeLog	2016-07-26 18:00:57 UTC (rev 203728)
+++ trunk/Source/WebCore/ChangeLog	2016-07-26 18:58:32 UTC (rev 203729)
@@ -1,3 +1,22 @@
+2016-07-26  Said Abou-Hallawa  <[email protected]>
+
+        Infinite Canvas context save() causes WebKit to crash
+        https://bugs.webkit.org/show_bug.cgi?id=159586
+        <rdar://problem/26759984>
+
+        Reviewed by Simon Fraser.
+
+        Limit the size of the canvas context state stack to 1024 * 16 saves. All
+        the saves which come after that limit will stay unrealized. The restore() 
+        should not have any effect till there is no unrealized saves.
+         
+        Test: fast/canvas/canvas-context-save-limit.html
+
+        * html/canvas/CanvasRenderingContext2D.cpp:
+        (WebCore::CanvasRenderingContext2D::realizeSaves):
+        (WebCore::CanvasRenderingContext2D::realizeSavesLoop):
+        * html/canvas/CanvasRenderingContext2D.h: 
+
 2016-07-26  Youenn Fablet  <[email protected]>
 
         DOMTokenList should be iterable

Modified: trunk/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp (203728 => 203729)


--- trunk/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp	2016-07-26 18:00:57 UTC (rev 203728)
+++ trunk/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp	2016-07-26 18:58:32 UTC (rev 203729)
@@ -351,6 +351,17 @@
     context.drawBidiText(m_font, run, point, action);
 }
 
+void CanvasRenderingContext2D::realizeSaves()
+{
+    if (m_unrealizedSaveCount)
+        realizeSavesLoop();
+
+    if (m_unrealizedSaveCount) {
+        static NeverDestroyed<String> consoleMessage(ASCIILiteral("CanvasRenderingContext2D.save() has been called without a matching restore() too many times. Ignoring save()."));
+        canvas()->document().addConsoleMessage(MessageSource::Rendering, MessageLevel::Error, consoleMessage);
+    }
+}
+
 void CanvasRenderingContext2D::realizeSavesLoop()
 {
     ASSERT(m_unrealizedSaveCount);
@@ -357,6 +368,8 @@
     ASSERT(m_stateStack.size() >= 1);
     GraphicsContext* context = drawingContext();
     do {
+        if (m_stateStack.size() > MaxSaveCount)
+            break;
         m_stateStack.append(state());
         if (context)
             context->save();

Modified: trunk/Source/WebCore/html/canvas/CanvasRenderingContext2D.h (203728 => 203729)


--- trunk/Source/WebCore/html/canvas/CanvasRenderingContext2D.h	2016-07-26 18:00:57 UTC (rev 203728)
+++ trunk/Source/WebCore/html/canvas/CanvasRenderingContext2D.h	2016-07-26 18:58:32 UTC (rev 203729)
@@ -309,7 +309,7 @@
         CanvasDidDrawApplyAll = 0xffffffff
     };
 
-    State& modifiableState() { ASSERT(!m_unrealizedSaveCount); return m_stateStack.last(); }
+    State& modifiableState() { ASSERT(!m_unrealizedSaveCount || m_stateStack.size() >= MaxSaveCount); return m_stateStack.last(); }
     const State& state() const { return m_stateStack.last(); }
 
     void applyLineDash() const;
@@ -325,11 +325,7 @@
     GraphicsContext* drawingContext() const;
 
     void unwindStateStack();
-    void realizeSaves()
-    {
-        if (m_unrealizedSaveCount)
-            realizeSavesLoop();
-    }
+    void realizeSaves();
     void realizeSavesLoop();
 
     void applyStrokePattern();
@@ -385,6 +381,7 @@
     PlatformLayer* platformLayer() const override;
 #endif
 
+    static const unsigned MaxSaveCount = 1024 * 16;
     Vector<State, 1> m_stateStack;
     unsigned m_unrealizedSaveCount { 0 };
     bool m_usesCSSCompatibilityParseMode;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to