From 4bb1a50963b49ff7f2c93a5459e7584259b75b90 Mon Sep 17 00:00:00 2001 From: Csoregi Natalia Date: Fri, 8 May 2020 09:39:25 +0300 Subject: [PATCH] Backed out changeset 8a48a3a488ab (bug 1621433) for hazard failures on CanvasRenderingContext2D.cpp CLOSED TREE --- ...rowser_canvas_fingerprinting_resistance.js | 187 +++++------------- dom/base/ImageEncoder.cpp | 6 +- dom/canvas/CanvasRenderingContext2D.cpp | 4 +- dom/canvas/GeneratePlaceholderCanvasData.h | 65 ------ dom/canvas/moz.build | 1 - dom/html/HTMLCanvasElement.cpp | 6 +- modules/libpref/init/StaticPrefList.yaml | 7 - 7 files changed, 52 insertions(+), 224 deletions(-) delete mode 100644 dom/canvas/GeneratePlaceholderCanvasData.h diff --git a/browser/base/content/test/permissions/browser_canvas_fingerprinting_resistance.js b/browser/base/content/test/permissions/browser_canvas_fingerprinting_resistance.js index a6cf97d31c48..4fc0c25c6577 100644 --- a/browser/base/content/test/permissions/browser_canvas_fingerprinting_resistance.js +++ b/browser/base/content/test/permissions/browser_canvas_fingerprinting_resistance.js @@ -3,9 +3,6 @@ * required for canvas data extraction. * This tests whether the site permission prompt for canvas data extraction * works properly. - * When "privacy.resistFingerprinting.randomDataOnCanvasExtract" is true, - * canvas data extraction results in random data, and when it is false, canvas - * data extraction results in all-white data. */ "use strict"; @@ -51,12 +48,6 @@ function initTab() { let placeholder = drawCanvas("white"); contentWindow.kPlaceholderData = placeholder.toDataURL(); let canvas = drawCanvas("cyan", "canvas-id-canvas"); - contentWindow.kPlacedData = canvas.toDataURL(); - is( - canvas.toDataURL(), - contentWindow.kPlacedData, - "privacy.resistFingerprinting = false, canvas data == placed data" - ); isnot( canvas.toDataURL(), contentWindow.kPlaceholderData, @@ -64,17 +55,10 @@ function initTab() { ); } -function enableResistFingerprinting( - randomDataOnCanvasExtract, - autoDeclineNoInput -) { +function enableResistFingerprinting(autoDeclineNoInput) { return SpecialPowers.pushPrefEnv({ set: [ ["privacy.resistFingerprinting", true], - [ - "privacy.resistFingerprinting.randomDataOnCanvasExtract", - randomDataOnCanvasExtract, - ], [ "privacy.resistFingerprinting.autoDeclineNoUserInputCanvasPrompts", autoDeclineNoInput, @@ -91,49 +75,28 @@ function promisePopupHidden() { return BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popuphidden"); } -function extractCanvasData(randomDataOnCanvasExtract, grantPermission) { +function extractCanvasData(grantPermission) { let contentWindow = content.wrappedJSObject; let canvas = contentWindow.document.getElementById("canvas-id-canvas"); let canvasData = canvas.toDataURL(); if (grantPermission) { + isnot( + canvasData, + contentWindow.kPlaceholderData, + "privacy.resistFingerprinting = true, permission granted, canvas data != placeholderdata" + ); + } else if (grantPermission === false) { is( canvasData, - contentWindow.kPlacedData, - "privacy.resistFingerprinting = true, permission granted, canvas data == placed data" + contentWindow.kPlaceholderData, + "privacy.resistFingerprinting = true, permission denied, canvas data == placeholderdata" ); - if (!randomDataOnCanvasExtract) { - isnot( - canvasData, - contentWindow.kPlaceholderData, - "privacy.resistFingerprinting = true and randomDataOnCanvasExtract = false, permission granted, canvas data != placeholderdata" - ); - } - } else if (grantPermission === false) { - isnot( - canvasData, - contentWindow.kPlacedData, - "privacy.resistFingerprinting = true, permission denied, canvas data != placed data" - ); - if (!randomDataOnCanvasExtract) { - is( - canvasData, - contentWindow.kPlaceholderData, - "privacy.resistFingerprinting = true and randomDataOnCanvasExtract = false, permission denied, canvas data == placeholderdata" - ); - } } else { - isnot( + is( canvasData, - contentWindow.kPlacedData, - "privacy.resistFingerprinting = true, requesting permission, canvas data != placed data" + contentWindow.kPlaceholderData, + "privacy.resistFingerprinting = true, requesting permission, canvas data == placeholderdata" ); - if (!randomDataOnCanvasExtract) { - is( - canvasData, - contentWindow.kPlaceholderData, - "privacy.resistFingerprinting = true and randomDataOnCanvasExtract = false, requesting permission, canvas data == placeholderdata" - ); - } } } @@ -155,19 +118,11 @@ function testPermission() { return Services.perms.testPermissionFromPrincipal(kPrincipal, kPermission); } -async function withNewTabNoInput( - randomDataOnCanvasExtract, - grantPermission, - browser -) { +async function withNewTabNoInput(grantPermission, browser) { await SpecialPowers.spawn(browser, [], initTab); - await enableResistFingerprinting(randomDataOnCanvasExtract, false); + await enableResistFingerprinting(false); let popupShown = promisePopupShown(); - await SpecialPowers.spawn( - browser, - [randomDataOnCanvasExtract], - extractCanvasData - ); + await SpecialPowers.spawn(browser, [], extractCanvasData); await popupShown; let popupHidden = promisePopupHidden(); if (grantPermission) { @@ -179,34 +134,28 @@ async function withNewTabNoInput( await popupHidden; is(testPermission(), Services.perms.DENY_ACTION, "permission denied"); } - await SpecialPowers.spawn( - browser, - [randomDataOnCanvasExtract, grantPermission], - extractCanvasData - ); + await SpecialPowers.spawn(browser, [grantPermission], extractCanvasData); await SpecialPowers.popPrefEnv(); } -async function doTestNoInput(randomDataOnCanvasExtract, grantPermission) { +async function doTestNoInput(grantPermission) { await BrowserTestUtils.withNewTab( kUrl, - withNewTabNoInput.bind(null, randomDataOnCanvasExtract, grantPermission) + withNewTabNoInput.bind(null, grantPermission) ); Services.perms.removeFromPrincipal(kPrincipal, kPermission); } // With auto-declining disabled (not the default) // Tests clicking "Don't Allow" button of the permission prompt. -add_task(doTestNoInput.bind(null, true, false)); -add_task(doTestNoInput.bind(null, false, false)); +add_task(doTestNoInput.bind(null, false)); // Tests clicking "Allow" button of the permission prompt. -add_task(doTestNoInput.bind(null, true, true)); -add_task(doTestNoInput.bind(null, false, true)); +add_task(doTestNoInput.bind(null, true)); -async function withNewTabAutoBlockNoInput(randomDataOnCanvasExtract, browser) { +async function withNewTabAutoBlockNoInput(browser) { await SpecialPowers.spawn(browser, [], initTab); - await enableResistFingerprinting(randomDataOnCanvasExtract, true); + await enableResistFingerprinting(true); let noShowHandler = () => { ok(false, "The popup notification should not show in this case."); @@ -220,11 +169,7 @@ async function withNewTabAutoBlockNoInput(randomDataOnCanvasExtract, browser) { ); // Try to extract canvas data without user inputs. - await SpecialPowers.spawn( - browser, - [randomDataOnCanvasExtract], - extractCanvasData - ); + await SpecialPowers.spawn(browser, [], extractCanvasData); await promisePopupObserver; info("There should be no popup shown on the panel."); @@ -245,72 +190,38 @@ async function withNewTabAutoBlockNoInput(randomDataOnCanvasExtract, browser) { await SpecialPowers.popPrefEnv(); } -async function doTestAutoBlockNoInput(randomDataOnCanvasExtract) { - await BrowserTestUtils.withNewTab( - kUrl, - withNewTabAutoBlockNoInput.bind(null, randomDataOnCanvasExtract) - ); -} +add_task(async function doTestAutoBlockNoInput() { + await BrowserTestUtils.withNewTab(kUrl, withNewTabAutoBlockNoInput); +}); -add_task(doTestAutoBlockNoInput.bind(null, true)); -add_task(doTestAutoBlockNoInput.bind(null, false)); - -function extractCanvasDataUserInput( - randomDataOnCanvasExtract, - grantPermission -) { +function extractCanvasDataUserInput(grantPermission) { let contentWindow = content.wrappedJSObject; let canvas = contentWindow.document.getElementById("canvas-id-canvas"); let canvasData = canvas.toDataURL(); if (grantPermission) { + isnot( + canvasData, + contentWindow.kPlaceholderData, + "privacy.resistFingerprinting = true, permission granted, canvas data != placeholderdata" + ); + } else if (grantPermission === false) { is( canvasData, - contentWindow.kPlacedData, - "privacy.resistFingerprinting = true, permission granted, canvas data == placed data" + contentWindow.kPlaceholderData, + "privacy.resistFingerprinting = true, permission denied, canvas data == placeholderdata" ); - if (!randomDataOnCanvasExtract) { - isnot( - canvasData, - contentWindow.kPlaceholderData, - "privacy.resistFingerprinting = true and randomDataOnCanvasExtract = false, permission granted, canvas data != placeholderdata" - ); - } - } else if (grantPermission === false) { - isnot( - canvasData, - contentWindow.kPlacedData, - "privacy.resistFingerprinting = true, permission denied, canvas data != placed data" - ); - if (!randomDataOnCanvasExtract) { - is( - canvasData, - contentWindow.kPlaceholderData, - "privacy.resistFingerprinting = true and randomDataOnCanvasExtract = false, permission denied, canvas data == placeholderdata" - ); - } } else { - isnot( + is( canvasData, - contentWindow.kPlacedData, - "privacy.resistFingerprinting = true, requesting permission, canvas data != placed data" + contentWindow.kPlaceholderData, + "privacy.resistFingerprinting = true, requesting permission, canvas data == placeholderdata" ); - if (!randomDataOnCanvasExtract) { - is( - canvasData, - contentWindow.kPlaceholderData, - "privacy.resistFingerprinting = true and randomDataOnCanvasExtract = false, requesting permission, canvas data == placeholderdata" - ); - } } } -async function withNewTabInput( - randomDataOnCanvasExtract, - grantPermission, - browser -) { +async function withNewTabInput(grantPermission, browser) { await SpecialPowers.spawn(browser, [], initTab); - await enableResistFingerprinting(randomDataOnCanvasExtract, true); + await enableResistFingerprinting(true); let popupShown = promisePopupShown(); await SpecialPowers.spawn(browser, [], function(host) { E10SUtils.wrapHandlingUserInput(content, true, function() { @@ -331,29 +242,23 @@ async function withNewTabInput( } await SpecialPowers.spawn( browser, - [randomDataOnCanvasExtract, grantPermission], + [grantPermission], extractCanvasDataUserInput ); await SpecialPowers.popPrefEnv(); } -async function doTestInput( - randomDataOnCanvasExtract, - grantPermission, - autoDeclineNoInput -) { +async function doTestInput(grantPermission, autoDeclineNoInput) { await BrowserTestUtils.withNewTab( kUrl, - withNewTabInput.bind(null, randomDataOnCanvasExtract, grantPermission) + withNewTabInput.bind(null, grantPermission) ); Services.perms.removeFromPrincipal(kPrincipal, kPermission); } // With auto-declining enabled (the default) // Tests clicking "Don't Allow" button of the permission prompt. -add_task(doTestInput.bind(null, true, false)); -add_task(doTestInput.bind(null, false, false)); +add_task(doTestInput.bind(null, false)); // Tests clicking "Allow" button of the permission prompt. -add_task(doTestInput.bind(null, true, true)); -add_task(doTestInput.bind(null, false, true)); +add_task(doTestInput.bind(null, true)); diff --git a/dom/base/ImageEncoder.cpp b/dom/base/ImageEncoder.cpp index da0c855941f6..988279ccbca0 100644 --- a/dom/base/ImageEncoder.cpp +++ b/dom/base/ImageEncoder.cpp @@ -6,7 +6,6 @@ #include "ImageEncoder.h" #include "mozilla/dom/CanvasRenderingContext2D.h" -#include "mozilla/dom/GeneratePlaceholderCanvasData.h" #include "mozilla/dom/MemoryBlobImpl.h" #include "mozilla/dom/WorkerPrivate.h" #include "mozilla/gfx/2D.h" @@ -384,9 +383,8 @@ nsresult ImageEncoder::ExtractDataInternal( return NS_ERROR_INVALID_ARG; } if (aUsePlaceholder) { - auto size = 4 * aSize.width * aSize.height; - auto* data = map.mData; - GeneratePlaceholderCanvasData(size, &data); + // If placeholder data was requested, return all-white, opaque image data. + memset(map.mData, 0xFF, 4 * aSize.width * aSize.height); } rv = aEncoder->InitFromData(map.mData, aSize.width * aSize.height * 4, aSize.width, aSize.height, aSize.width * 4, diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index cc20dfff022e..54fe0d1db539 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -17,7 +17,6 @@ #include "mozilla/PresShellInlines.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/HTMLCanvasElement.h" -#include "mozilla/dom/GeneratePlaceholderCanvasData.h" #include "SVGObserverUtils.h" #include "nsPresContext.h" @@ -5105,8 +5104,9 @@ nsresult CanvasRenderingContext2D::GetImageDataArray( uint8_t* src = rawData.mData + srcReadRect.y * srcStride + srcReadRect.x * 4; + // Return all-white, opaque pixel data if no permission. if (usePlaceholder) { - GeneratePlaceholderCanvasData(len.value(), &data); + memset(data, 0xFF, len.value()); break; } diff --git a/dom/canvas/GeneratePlaceholderCanvasData.h b/dom/canvas/GeneratePlaceholderCanvasData.h deleted file mode 100644 index bd3639be9d40..000000000000 --- a/dom/canvas/GeneratePlaceholderCanvasData.h +++ /dev/null @@ -1,65 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_GeneratePlaceholderCanvasData_h -#define mozilla_dom_GeneratePlaceholderCanvasData_h - -#include "nsCOMPtr.h" -#include "nsIRandomGenerator.h" -#include "nsServiceManagerUtils.h" -#include "mozilla/StaticPrefs_privacy.h" - -#define RANDOM_BYTES_TO_SAMPLE 32 - -namespace mozilla { -namespace dom { - -/** - * When privacy.resistFingerprinting.randomDataOnCanvasExtract is true, tries - * to generate random canvas data by sampling RANDOM_BYTES_TO_SAMPLE bytes and - * then repeating those bytes many times to fill the buffer. If this fails or - * if privacy.resistFingerprinting.randomDataOnCanvasExtract is false, returns - * all-white, opaque pixel data. - * - * @param[in] size Size of output buffer - * @param[out] buffer Output buffer - */ -inline void GeneratePlaceholderCanvasData(uint32_t size, uint8_t** buffer) { - if (!StaticPrefs::privacy_resistFingerprinting_randomDataOnCanvasExtract()) { - memset(*buffer, 0xFF, size); - return; - } - nsresult rv; - nsCOMPtr rg = - do_GetService("@mozilla.org/security/random-generator;1", &rv); - if (NS_FAILED(rv)) { - memset(*buffer, 0xFF, size); - } else { - uint8_t* tmp_buffer; - rv = rg->GenerateRandomBytes(RANDOM_BYTES_TO_SAMPLE, &tmp_buffer); - if (NS_FAILED(rv)) { - memset(*buffer, 0xFF, size); - } else { - auto remaining_to_fill = size; - auto index = 0; - while (remaining_to_fill > 0) { - auto bytes_to_write = (remaining_to_fill > RANDOM_BYTES_TO_SAMPLE) - ? RANDOM_BYTES_TO_SAMPLE - : remaining_to_fill; - memcpy((*buffer) + (index * RANDOM_BYTES_TO_SAMPLE), tmp_buffer, - bytes_to_write); - remaining_to_fill -= bytes_to_write; - index++; - } - free(tmp_buffer); - } - } -} - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_GeneratePlaceholderCanvasData_h diff --git a/dom/canvas/moz.build b/dom/canvas/moz.build index c2a3f25043cc..65e41a003bee 100644 --- a/dom/canvas/moz.build +++ b/dom/canvas/moz.build @@ -53,7 +53,6 @@ EXPORTS.mozilla.dom += [ 'CanvasRenderingContext2D.h', 'CanvasRenderingContextHelper.h', 'CanvasUtils.h', - 'GeneratePlaceholderCanvasData.h', 'ImageBitmap.h', 'ImageBitmapRenderingContext.h', 'ImageBitmapSource.h', diff --git a/dom/html/HTMLCanvasElement.cpp b/dom/html/HTMLCanvasElement.cpp index b713ebd8aaa5..7e64b2e0d3b6 100644 --- a/dom/html/HTMLCanvasElement.cpp +++ b/dom/html/HTMLCanvasElement.cpp @@ -18,7 +18,6 @@ #include "mozilla/CheckedInt.h" #include "mozilla/dom/CanvasCaptureMediaStream.h" #include "mozilla/dom/CanvasRenderingContext2D.h" -#include "mozilla/dom/GeneratePlaceholderCanvasData.h" #include "mozilla/dom/Event.h" #include "mozilla/dom/File.h" #include "mozilla/dom/HTMLCanvasElementBinding.h" @@ -101,9 +100,8 @@ class RequestedFrameRefreshObserver : public nsARefreshObserver { MOZ_ASSERT(data->GetFormat() == copy->GetFormat()); if (aReturnPlaceholderData) { - auto size = write.GetStride() * copy->GetSize().height; - auto* data = write.GetData(); - GeneratePlaceholderCanvasData(size, &data); + // If returning placeholder data, fill the frame copy with white pixels. + memset(write.GetData(), 0xFF, write.GetStride() * copy->GetSize().height); } else { memcpy(write.GetData(), read.GetData(), write.GetStride() * copy->GetSize().height); diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index 5628351fb891..6d05f5201ff4 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -8155,13 +8155,6 @@ value: true mirror: always -# Whether canvas extraction should result in random data. If false, canvas -# extraction results in all-white, opaque pixel data. -- name: privacy.resistFingerprinting.randomDataOnCanvasExtract - type: bool - value: true - mirror: always - # The log level for browser console messages logged in RFPHelper.jsm. Change to # 'All' and restart to see the messages. - name: privacy.resistFingerprinting.jsmloglevel