Commit 355464e4 authored by Yutaka Hirano's avatar Yutaka Hirano Committed by Commit Bot

Disallow non-origin-clean ImageBitmap serialization/transferring

Intent-to-Deprecate-and-Remove:
https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/Z1XdYf6SjDU

Bug: 1013087
Change-Id: I34be2e59d6fa44834333e368dbd06a5e2cf1b876
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1942187Reviewed-by: default avatarMike West <mkwst@chromium.org>
Commit-Queue: Yutaka Hirano <yhirano@chromium.org>
Cr-Commit-Position: refs/heads/master@{#720130}
parent ce0b2e0b
...@@ -350,6 +350,11 @@ ScriptWrappable* V8ScriptValueDeserializer::ReadDOMObject( ...@@ -350,6 +350,11 @@ ScriptWrappable* V8ScriptValueDeserializer::ReadDOMObject(
if (!computed_byte_length.IsValid() || if (!computed_byte_length.IsValid() ||
computed_byte_length.ValueOrDie() != byte_length) computed_byte_length.ValueOrDie() != byte_length)
return nullptr; return nullptr;
if (!origin_clean) {
// Non-origin-clean ImageBitmap serialization/deserialization have
// been deprecated.
return nullptr;
}
return ImageBitmap::Create(pixels, width, height, is_premultiplied, return ImageBitmap::Create(pixels, width, height, is_premultiplied,
origin_clean, color_params); origin_clean, color_params);
} }
......
...@@ -271,8 +271,10 @@ bool V8ScriptValueSerializer::WriteDOMObject(ScriptWrappable* wrappable, ...@@ -271,8 +271,10 @@ bool V8ScriptValueSerializer::WriteDOMObject(ScriptWrappable* wrappable,
execution_context->CountUse( execution_context->CountUse(
mojom::WebFeature::kOriginCleanImageBitmapTransfer); mojom::WebFeature::kOriginCleanImageBitmapTransfer);
} else { } else {
execution_context->CountUse( exception_state.ThrowDOMException(
mojom::WebFeature::kNonOriginCleanImageBitmapTransfer); DOMExceptionCode::kDataCloneError,
"Non-origin-clean ImageBitmap cannot be transferred.");
return false;
} }
DCHECK_LE(index, std::numeric_limits<uint32_t>::max()); DCHECK_LE(index, std::numeric_limits<uint32_t>::max());
...@@ -286,8 +288,10 @@ bool V8ScriptValueSerializer::WriteDOMObject(ScriptWrappable* wrappable, ...@@ -286,8 +288,10 @@ bool V8ScriptValueSerializer::WriteDOMObject(ScriptWrappable* wrappable,
execution_context->CountUse( execution_context->CountUse(
mojom::WebFeature::kOriginCleanImageBitmapSerialization); mojom::WebFeature::kOriginCleanImageBitmapSerialization);
} else { } else {
execution_context->CountUse( exception_state.ThrowDOMException(
mojom::WebFeature::kNonOriginCleanImageBitmapSerialization); DOMExceptionCode::kDataCloneError,
"Non-origin-clean ImageBitmap cannot be cloned.");
return false;
} }
WriteTag(kImageBitmapTag); WriteTag(kImageBitmapTag);
SerializedColorParams color_params(image_bitmap->GetCanvasColorParams()); SerializedColorParams color_params(image_bitmap->GetCanvasColorParams());
......
This is a testharness.js-based test. This is a testharness.js-based test.
FAIL BroadcastChannel'ing a tainted ImageBitmap to a COOP+COEP popup assert_equals: expected "Got failure as expected." but got "Got message, expected failure." FAIL BroadcastChannel'ing a tainted ImageBitmap to a COOP+COEP popup Failed to execute 'postMessage' on 'BroadcastChannel': Non-origin-clean ImageBitmap cannot be cloned.
FAIL Messaging a tainted ImageBitMap via serialize/deserialize to a COEP shared worker assert_equals: expected "Got failure as expected." but got "Got message, expected failure." FAIL Messaging a tainted ImageBitMap via serialize/deserialize to a COEP shared worker Failed to execute 'postMessage' on 'MessagePort': Non-origin-clean ImageBitmap cannot be cloned.
FAIL Messaging a tainted ImageBitMap via transfer to a COEP shared worker assert_equals: expected "Got failure as expected." but got "Got message, expected failure." FAIL Messaging a tainted ImageBitMap via transfer to a COEP shared worker Failed to execute 'postMessage' on 'MessagePort': Non-origin-clean ImageBitmap cannot be transferred.
Harness: the test ran to completion. Harness: the test ran to completion.
The taintedness of the imagebitmap drawn to OffscrenCanvas must be transfered A tainted imagebitmap drawn to OffscrenCanvas cannot be transfered
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS ImageBitmap is tainted. Threw error: SecurityError: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data. PASS A tainted ImageBitmap cannot be transferred
PASS successfullyParsed is true PASS successfullyParsed is true
TEST COMPLETE TEST COMPLETE
......
...@@ -3,59 +3,34 @@ ...@@ -3,59 +3,34 @@
<body> <body>
<canvas id='output' width='200' height='400'></canvas> <canvas id='output' width='200' height='400'></canvas>
<script src="/js-test-resources/js-test.js"></script> <script src="/js-test-resources/js-test.js"></script>
<script id='myWorker' type='text/worker'>
self.onmessage = function(e) {
var aCanvas = new OffscreenCanvas(200, 400);
var ctx = aCanvas.getContext('2d');
// Draw a tainted imagebitmap into OffscreenCanvas 2d
ctx.drawImage(e.data, 0, 0, 10, 10);
var image = aCanvas.transferToImageBitmap();
self.postMessage(image, [image]);
};
</script>
<script> <script>
description("The taintedness of the imagebitmap drawn to OffscrenCanvas must be transfered"); description("A tainted imagebitmap drawn to OffscrenCanvas cannot be transfered");
window.jsTestIsAsync = true; window.jsTestIsAsync = true;
function shouldBeTainted(imageBitmap) {
var canvas = document.createElement("canvas");
canvas.width = 10;
canvas.height = 10;
var context = canvas.getContext("2d");
context.drawImage(imageBitmap, 0, 0, 10, 10);
try {
var imageData = context.getImageData(0, 0, 10, 10);
testFailed("ImageBitmap is not tainted.");
} catch (e) {
testPassed("ImageBitmap is tainted. Threw error: " + e);
}
}
var blob = new Blob([document.getElementById('myWorker').textContent]);
var worker = new Worker(URL.createObjectURL(blob));
worker.addEventListener('message', msg => {
shouldBeTainted(msg.data);
finishJSTest();
});
var image = document.createElement('img'); var image = document.createElement('img');
image.src = 'http://localhost:8080/security/resources/abe.png'; image.src = 'http://localhost:8080/security/resources/abe.png';
image.addEventListener('load', function() { image.addEventListener('load', function() {
createImageBitmap(image, 0, 0, 10, 10).then( createImageBitmap(image, 0, 0, 10, 10).then(
function(imagebitmap) { function(originalImagebitmap) {
// Post the tainted imagebitmap to worker const aCanvas = new OffscreenCanvas(200, 400);
worker.postMessage(imagebitmap, [imagebitmap]); const ctx = aCanvas.getContext('2d');
}, // Draw a tainted imagebitmap into OffscreenCanvas 2d
function(e) { ctx.drawImage(originalImagebitmap, 0, 0, 10, 10);
const newImageBitmap = aCanvas.transferToImageBitmap();
try {
window.postMessage(newImageBitmap, [newImageBitmap]);
testFailed("A tainted ImageBitmap cannot be transferred");
finishJSTest();
} catch (e) {
testPassed("A tainted ImageBitmap cannot be transferred");
finishJSTest();
}
}).catch((e) => {
testFailed("Image rejected unexpectedly. "); testFailed("Image rejected unexpectedly. ");
debug(e); debug(e);
} });
); });
});
</script> </script>
</body> </body>
</html> </html>
...@@ -18,21 +18,16 @@ async_test(function(t) { ...@@ -18,21 +18,16 @@ async_test(function(t) {
var returnedBitmap; var returnedBitmap;
var image = new Image(); var image = new Image();
image.onload = function() { image.onload = t.step_func(() => {
shouldBeTainted(image); shouldBeTainted(image);
createImageBitmap(image).then(imageBitmap => { createImageBitmap(image).then(t.step_func((imageBitmap) => {
worker.postMessage({data:imageBitmap}, [imageBitmap]); assert_throws('DataCloneError',
worker.onmessage = t.step_func(onMessage); () => worker.postMessage({data:imageBitmap}, [imageBitmap]));
t.done();
}));
}); });
}
image.src = 'http://localhost:8080/security/resources/abe.png'; image.src = 'http://localhost:8080/security/resources/abe.png';
function onMessage(e) {
returnedBitmap = e.data.data;
shouldBeTainted(returnedBitmap);
t.done();
}
function shouldBeTainted(imageBitmap) { function shouldBeTainted(imageBitmap) {
var canvas = document.createElement("canvas"); var canvas = document.createElement("canvas");
canvas.width = 10; canvas.width = 10;
......
...@@ -40,9 +40,7 @@ promise_test(async (t) => { ...@@ -40,9 +40,7 @@ promise_test(async (t) => {
assert_false(internals.isUseCounted(document, FEATURE)); assert_false(internals.isUseCounted(document, FEATURE));
postMessage(bitmap, '*'); assert_throws('DataCloneError', () => postMessage(bitmap, '*'));
assert_true(internals.isUseCounted(document, FEATURE));
}, 'non-origin-clean, serialization'); }, 'non-origin-clean, serialization');
promise_test(async (t) => { promise_test(async (t) => {
...@@ -64,9 +62,7 @@ promise_test(async (t) => { ...@@ -64,9 +62,7 @@ promise_test(async (t) => {
assert_false(internals.isUseCounted(document, FEATURE)); assert_false(internals.isUseCounted(document, FEATURE));
postMessage(bitmap, '*', [bitmap]); assert_throws('DataCloneError', () => postMessage(bitmap, '*', [bitmap]));
assert_true(internals.isUseCounted(document, FEATURE));
}, 'non-origin-clean, transfer'); }, 'non-origin-clean, transfer');
</script> </script>
</body> </body>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment