<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[197271] releases/WebKitGTK/webkit-2.4</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/197271">197271</a></dd>
<dt>Author</dt> <dd>carlosgc@webkit.org</dd>
<dt>Date</dt> <dd>2016-02-28 02:44:58 -0800 (Sun, 28 Feb 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Merge <a href="http://trac.webkit.org/projects/webkit/changeset/182750">r182750</a> - Canvas drawImage() has a security hole when the image isn't yet fully loaded.
https://bugs.webkit.org/show_bug.cgi?id=58681.

Reviewed by Darin Adler.

Source/WebCore:

There is a race condition which may happen if an image from a different
origin is drawn on a canvas before it finishes loading. The check to taint
the canvas comes before drawing it. This check returns false if the image
is not completely loaded because we check the URL of the resource response.
If after this check and before the drawing, the image finishes loading, the
canvas will not be tainted but the image will be drawn.

The fix is to move the check to taint the canvas after drawing the image.
The only problem with this solution is basically the opposite of this bug:
we will become stricter than before with images which are from a different
origin and before they finish loading. The image has not finished loading,
so we do not draw it. Before we check for tainting, the image finishes
loading. So we decide to taint the canvas even the image is not drawn.

But this should not be a security issue anymore. I personally do not know
if it is even a correctness issue or not.

Test: http/tests/canvas/canvas-tainted-after-draw-image.html

* html/canvas/CanvasRenderingContext2D.cpp:
(WebCore::CanvasRenderingContext2D::drawImage):

LayoutTests:

This test confirms when we load an image from a different origin and try
drawing it on a canvas, the canvas is tainted if the image is completely
loaded and drawn. Otherwise the image is not drawn.

* http/tests/canvas/canvas-tainted-after-draw-image-expected.txt: Added.
* http/tests/canvas/canvas-tainted-after-draw-image.html: Added.
* http/tests/canvas/resources: Added.
* http/tests/canvas/resources/100x100-lime-rect.svg: Added.

Fix LayoutTests/http/tests/canvas/canvas-tainted-after-draw-image.html on all bots
following http://trac.webkit.org/changeset/182750.

Reviewed by Daniel Bates.

* http/tests/canvas/canvas-tainted-after-draw-image-expected.txt:
* http/tests/canvas/canvas-tainted-after-draw-image.html:
Set window.jsTestIsAsync true and call finishJSTest() to make the test
asynchronous, so the &quot;TEST COMPLETE&quot; message will be output after all the
test messages. Also delete the synchronous tests for data url image and
same-domain image since they can't be reliably tested.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#releasesWebKitGTKwebkit24LayoutTestsChangeLog">releases/WebKitGTK/webkit-2.4/LayoutTests/ChangeLog</a></li>
<li><a href="#releasesWebKitGTKwebkit24SourceWebCoreChangeLog">releases/WebKitGTK/webkit-2.4/Source/WebCore/ChangeLog</a></li>
<li><a href="#releasesWebKitGTKwebkit24SourceWebCorehtmlcanvasCanvasRenderingContext2Dcpp">releases/WebKitGTK/webkit-2.4/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#releasesWebKitGTKwebkit24LayoutTestshttptestscanvascanvastaintedafterdrawimageexpectedtxt">releases/WebKitGTK/webkit-2.4/LayoutTests/http/tests/canvas/canvas-tainted-after-draw-image-expected.txt</a></li>
<li><a href="#releasesWebKitGTKwebkit24LayoutTestshttptestscanvascanvastaintedafterdrawimagehtml">releases/WebKitGTK/webkit-2.4/LayoutTests/http/tests/canvas/canvas-tainted-after-draw-image.html</a></li>
<li>releases/WebKitGTK/webkit-2.4/LayoutTests/http/tests/canvas/resources/</li>
<li><a href="#releasesWebKitGTKwebkit24LayoutTestshttptestscanvasresources100x100limerectsvg">releases/WebKitGTK/webkit-2.4/LayoutTests/http/tests/canvas/resources/100x100-lime-rect.svg</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="releasesWebKitGTKwebkit24LayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: releases/WebKitGTK/webkit-2.4/LayoutTests/ChangeLog (197270 => 197271)</h4>
<pre class="diff"><span>
<span class="info">--- releases/WebKitGTK/webkit-2.4/LayoutTests/ChangeLog        2016-02-28 10:37:01 UTC (rev 197270)
+++ releases/WebKitGTK/webkit-2.4/LayoutTests/ChangeLog        2016-02-28 10:44:58 UTC (rev 197271)
</span><span class="lines">@@ -1,3 +1,33 @@
</span><ins>+2015-04-13  Said Abou-Hallawa  &lt;sabouhallawa@apple.com&gt;
+
+        Fix LayoutTests/http/tests/canvas/canvas-tainted-after-draw-image.html on all bots
+        following http://trac.webkit.org/changeset/182750.
+
+        Reviewed by Daniel Bates.
+
+        * http/tests/canvas/canvas-tainted-after-draw-image-expected.txt:
+        * http/tests/canvas/canvas-tainted-after-draw-image.html:
+        Set window.jsTestIsAsync true and call finishJSTest() to make the test 
+        asynchronous, so the &quot;TEST COMPLETE&quot; message will be output after all the
+        test messages. Also delete the synchronous tests for data url image and
+        same-domain image since they can't be reliably tested.
+
+2015-04-13  Said Abou-Hallawa  &lt;sabouhallawa@apple.com&gt;
+
+        Canvas drawImage() has a security hole when the image isn't yet fully loaded.
+        https://bugs.webkit.org/show_bug.cgi?id=58681.
+
+        Reviewed by Darin Adler.
+
+        This test confirms when we load an image from a different origin and try
+        drawing it on a canvas, the canvas is tainted if the image is completely
+        loaded and drawn. Otherwise the image is not drawn.
+
+        * http/tests/canvas/canvas-tainted-after-draw-image-expected.txt: Added.
+        * http/tests/canvas/canvas-tainted-after-draw-image.html: Added.
+        * http/tests/canvas/resources: Added.
+        * http/tests/canvas/resources/100x100-lime-rect.svg: Added.
+
</ins><span class="cx"> 2015-03-26  Zalan Bujtas  &lt;zalan@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Inline continuation code should not take anonymous containing wrapper granted.
</span></span></pre></div>
<a id="releasesWebKitGTKwebkit24LayoutTestshttptestscanvascanvastaintedafterdrawimageexpectedtxt"></a>
<div class="addfile"><h4>Added: releases/WebKitGTK/webkit-2.4/LayoutTests/http/tests/canvas/canvas-tainted-after-draw-image-expected.txt (0 => 197271)</h4>
<pre class="diff"><span>
<span class="info">--- releases/WebKitGTK/webkit-2.4/LayoutTests/http/tests/canvas/canvas-tainted-after-draw-image-expected.txt                                (rev 0)
+++ releases/WebKitGTK/webkit-2.4/LayoutTests/http/tests/canvas/canvas-tainted-after-draw-image-expected.txt        2016-02-28 10:44:58 UTC (rev 197271)
</span><span class="lines">@@ -0,0 +1,9 @@
</span><ins>+CONSOLE MESSAGE: line 58: Unable to get image data from canvas because the canvas has been tainted by cross-origin data.
+Tainting works correctly.
+Tainting works correctly.
+Tainting works correctly.
+Tainting works correctly.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+      
</ins></span></pre></div>
<a id="releasesWebKitGTKwebkit24LayoutTestshttptestscanvascanvastaintedafterdrawimagehtml"></a>
<div class="addfile"><h4>Added: releases/WebKitGTK/webkit-2.4/LayoutTests/http/tests/canvas/canvas-tainted-after-draw-image.html (0 => 197271)</h4>
<pre class="diff"><span>
<span class="info">--- releases/WebKitGTK/webkit-2.4/LayoutTests/http/tests/canvas/canvas-tainted-after-draw-image.html                                (rev 0)
+++ releases/WebKitGTK/webkit-2.4/LayoutTests/http/tests/canvas/canvas-tainted-after-draw-image.html        2016-02-28 10:44:58 UTC (rev 197271)
</span><span class="lines">@@ -0,0 +1,88 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;head&gt;
+  &lt;script src=&quot;../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;/head&gt;
+&lt;body&gt;
+  &lt;canvas id=&quot;sourceCanvas&quot; width=&quot;100&quot; height=&quot;100&quot;&gt;&lt;/canvas&gt;
+  &lt;canvas id=&quot;dataURLSynchronousCanvas&quot; width=&quot;100&quot; height=&quot;100&quot;&gt;&lt;/canvas&gt;
+  &lt;canvas id=&quot;dataURLAsynchronousCanvas&quot; width=&quot;100&quot; height=&quot;100&quot;&gt;&lt;/canvas&gt;
+  &lt;canvas id=&quot;sameDomainSynchronousCanvas&quot; width=&quot;100&quot; height=&quot;100&quot;&gt;&lt;/canvas&gt;
+  &lt;canvas id=&quot;sameDomainAsynchronousCanvas&quot; width=&quot;100&quot; height=&quot;100&quot;&gt;&lt;/canvas&gt;
+  &lt;canvas id=&quot;crossDomainSynchronousCanvas&quot; width=&quot;100&quot; height=&quot;100&quot;&gt;&lt;/canvas&gt;
+  &lt;canvas id=&quot;crossDomainAsynchronousCanvas&quot; width=&quot;100&quot; height=&quot;100&quot;&gt;&lt;/canvas&gt;
+  &lt;script&gt;
+    function drawCanvasBackground(id, color) {
+      var canvas = document.getElementById(id);
+      var context = canvas.getContext(&quot;2d&quot;);
+      context.fillStyle = color;
+      context.fillRect(0, 0, 100, 100);
+      return context;
+    }
+    
+    function incrementLoadedImagesCount() {
+      if (typeof incrementLoadedImagesCount.counter == 'undefined')
+        incrementLoadedImagesCount.counter = 0;
+        
+      if (++incrementLoadedImagesCount.counter == 4)
+          finishJSTest();
+    }
+
+    function drawAndGetImageDataSynchronous(id, imageSrc, shouldTaint) {
+      var context = drawCanvasBackground(id, '#f00'); // red
+      var image =  new Image();
+      image.src = imageSrc;
+      context.drawImage(image, 0, 0);
+
+      try {
+        var pixels = new Uint32Array(context.getImageData(0, 0, 1, 1).data.buffer);
+        if (pixels[0] == 0xFF0000FF)
+          debug(shouldTaint ? &quot;Tainting works correctly.&quot; : &quot;Tainting works incorrectly.&quot;);
+        else
+          debug(shouldTaint ? &quot;Tainting works incorrectly.&quot; : &quot;Tainting works correctly.&quot;);
+      }
+      catch (error) {
+        debug(shouldTaint ? &quot;Tainting works correctly.&quot; : &quot;Tainting works incorrectly.&quot;);
+      }
+      
+      incrementLoadedImagesCount();
+    }
+    
+    function drawAndGetImageDataAsynchronous(canvasId, imageSrc, shouldTaint) {
+      var context = drawCanvasBackground(canvasId, '#f00'); // red
+      var image =  new Image();
+
+      image.onload = function() {
+        context.drawImage(image, 0, 0);
+        try {
+          var pixels = new Uint32Array(context.getImageData(0, 0, 1, 1).data.buffer);
+          debug(shouldTaint ? &quot;Tainting works incorrectly.&quot; : &quot;Tainting works correctly.&quot;);
+        }
+        catch (error) {
+          debug(shouldTaint ? &quot;Tainting works correctly.&quot; : &quot;Tainting works incorrectly.&quot;);
+        }
+
+        incrementLoadedImagesCount();
+      }
+      image.src = imageSrc;
+    }
+
+    window.jsTestIsAsync = true;
+    
+    drawCanvasBackground(&quot;sourceCanvas&quot;, '#0f0'); // green
+    
+    // The dataURL and same-domain images should not be tainted. We should always be
+    // able to get the image data regardless whether it is drawn or not. But because
+    // we ask for the image data right after we load the image, we do not know the
+    // pixels values. So the synchronous case can't be tested.
+    drawAndGetImageDataAsynchronous(&quot;dataURLAsynchronousCanvas&quot;, sourceCanvas.toDataURL(), false);
+    drawAndGetImageDataAsynchronous(&quot;sameDomainAsynchronousCanvas&quot;, &quot;http://127.0.0.1:8000/canvas/resources/100x100-lime-rect.svg&quot;, false);
+    
+    // Cross domain image load should taint the canvas. The image should not be drawn
+    // so the synchronous case can be tested since we know the canvas pixel value.
+    drawAndGetImageDataSynchronous(&quot;crossDomainSynchronousCanvas&quot;, &quot;http://localhost:8000/canvas/resources/100x100-lime-rect.svg&quot;, true);
+    drawAndGetImageDataAsynchronous(&quot;crossDomainAsynchronousCanvas&quot;, &quot;http://localhost:8000/canvas/resources/100x100-lime-rect.svg&quot;, true);
+  &lt;/script&gt;
+  &lt;script src=&quot;../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="releasesWebKitGTKwebkit24LayoutTestshttptestscanvasresources100x100limerectsvg"></a>
<div class="addfile"><h4>Added: releases/WebKitGTK/webkit-2.4/LayoutTests/http/tests/canvas/resources/100x100-lime-rect.svg (0 => 197271)</h4>
<pre class="diff"><span>
<span class="info">--- releases/WebKitGTK/webkit-2.4/LayoutTests/http/tests/canvas/resources/100x100-lime-rect.svg                                (rev 0)
+++ releases/WebKitGTK/webkit-2.4/LayoutTests/http/tests/canvas/resources/100x100-lime-rect.svg        2016-02-28 10:44:58 UTC (rev 197271)
</span><span class="lines">@@ -0,0 +1,3 @@
</span><ins>+&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100&quot; height=&quot;100&quot;&gt;
+    &lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;100px&quot; height=&quot;100px&quot; fill=&quot;lime&quot;/&gt;
+&lt;/svg&gt;
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="releasesWebKitGTKwebkit24SourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: releases/WebKitGTK/webkit-2.4/Source/WebCore/ChangeLog (197270 => 197271)</h4>
<pre class="diff"><span>
<span class="info">--- releases/WebKitGTK/webkit-2.4/Source/WebCore/ChangeLog        2016-02-28 10:37:01 UTC (rev 197270)
+++ releases/WebKitGTK/webkit-2.4/Source/WebCore/ChangeLog        2016-02-28 10:44:58 UTC (rev 197271)
</span><span class="lines">@@ -1,3 +1,32 @@
</span><ins>+2015-04-13  Said Abou-Hallawa  &lt;sabouhallawa@apple.com&gt;
+
+        Canvas drawImage() has a security hole when the image isn't yet fully loaded.
+        https://bugs.webkit.org/show_bug.cgi?id=58681.
+
+        Reviewed by Darin Adler.
+
+        There is a race condition which may happen if an image from a different
+        origin is drawn on a canvas before it finishes loading. The check to taint
+        the canvas comes before drawing it. This check returns false if the image
+        is not completely loaded because we check the URL of the resource response.
+        If after this check and before the drawing, the image finishes loading, the
+        canvas will not be tainted but the image will be drawn.
+        
+        The fix is to move the check to taint the canvas after drawing the image.
+        The only problem with this solution is basically the opposite of this bug:
+        we will become stricter than before with images which are from a different
+        origin and before they finish loading. The image has not finished loading,
+        so we do not draw it. Before we check for tainting, the image finishes
+        loading. So we decide to taint the canvas even the image is not drawn.
+        
+        But this should not be a security issue anymore. I personally do not know
+        if it is even a correctness issue or not.
+
+        Test: http/tests/canvas/canvas-tainted-after-draw-image.html
+
+        * html/canvas/CanvasRenderingContext2D.cpp:
+        (WebCore::CanvasRenderingContext2D::drawImage):
+
</ins><span class="cx"> 2015-04-27  Darin Adler  &lt;darin@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Crashes under IDBDatabase::closeConnection
</span></span></pre></div>
<a id="releasesWebKitGTKwebkit24SourceWebCorehtmlcanvasCanvasRenderingContext2Dcpp"></a>
<div class="modfile"><h4>Modified: releases/WebKitGTK/webkit-2.4/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp (197270 => 197271)</h4>
<pre class="diff"><span>
<span class="info">--- releases/WebKitGTK/webkit-2.4/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp        2016-02-28 10:37:01 UTC (rev 197270)
+++ releases/WebKitGTK/webkit-2.4/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp        2016-02-28 10:44:58 UTC (rev 197271)
</span><span class="lines">@@ -1296,8 +1296,6 @@
</span><span class="cx">     if (!cachedImage)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    checkOrigin(image);
-
</del><span class="cx">     if (rectContainsCanvas(normalizedDstRect)) {
</span><span class="cx">         c-&gt;drawImage(cachedImage-&gt;imageForRenderer(image-&gt;renderer()), ColorSpaceDeviceRGB, normalizedDstRect, normalizedSrcRect, op, blendMode, ImageOrientationDescription());
</span><span class="cx">         didDrawEntireCanvas();
</span><span class="lines">@@ -1312,6 +1310,8 @@
</span><span class="cx">         c-&gt;drawImage(cachedImage-&gt;imageForRenderer(image-&gt;renderer()), ColorSpaceDeviceRGB, normalizedDstRect, normalizedSrcRect, op, blendMode, ImageOrientationDescription());
</span><span class="cx">         didDraw(normalizedDstRect);
</span><span class="cx">     }
</span><ins>+
+    checkOrigin(image);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* sourceCanvas, float x, float y, ExceptionCode&amp; ec)
</span></span></pre>
</div>
</div>

</body>
</html>