Bug 1431455 Fix a regression for ResistFingerprinting: use the larger of the reduceTimerPrecision pref and the constant 100ms r?bkelly
MozReview-Commit-ID: 73MpmfEKoQG
--- a/browser/components/resistfingerprinting/test/browser/browser_performanceAPI.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_performanceAPI.js
@@ -57,22 +57,26 @@ let isRounded = (x, expectedPrecision) =
" Rounded Vaue: " + rounded + " Fuzzy1: " + Math.abs(rounded - x + expectedPrecision) +
" Fuzzy 2: " + Math.abs(rounded - x));
return false;
};
let setupTest = async function(tab, resistFingerprinting, reduceTimerPrecision, expectedPrecision, runTests, workerCall) {
await SpecialPowers.pushPrefEnv({"set":
- // Run one set of tests with both true to confirm p.rP overrides p.rTP
[["privacy.resistFingerprinting", resistFingerprinting],
["privacy.reduceTimerPrecision", reduceTimerPrecision],
["privacy.resistFingerprinting.reduceTimerPrecision.microseconds", expectedPrecision * 1000]
]
});
+ // No matter what we set the precision to, if we're in ResistFingerprinting mode
+ // we use the larger of the precision pref and the constant 100ms
+ if (resistFingerprinting) {
+ expectedPrecision = expectedPrecision < 100 ? 100 : expectedPrecision;
+ }
await ContentTask.spawn(tab.linkedBrowser, {
list: PERFORMANCE_TIMINGS,
precision: expectedPrecision,
isRoundedFunc: isRounded.toString(),
workerCall
},
runTests);
};
@@ -186,17 +190,17 @@ let runWorkerTest = async function(data)
};
add_task(async function runRPTestsForWorker() {
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser, TEST_PATH + "file_dummy.html");
await setupTest(tab, true, true, 100, runWorkerTest, "runRPTests");
await setupTest(tab, true, false, 13, runWorkerTest, "runRPTests");
- await setupTest(tab, true, false, .13, runWorkerTest, "runRPTests");
+ await setupTest(tab, true, true, .13, runWorkerTest, "runRPTests");
await BrowserTestUtils.removeTab(tab);
});
add_task(async function runRTPTestsForWorker() {
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser, TEST_PATH + "file_dummy.html");
--- a/browser/components/resistfingerprinting/test/mochitest/test_animation_api.html
+++ b/browser/components/resistfingerprinting/test/mochitest/test_animation_api.html
@@ -11,47 +11,56 @@ https://bugzilla.mozilla.org/show_bug.cg
<script type="application/javascript">
/** Test for Bug 1382545 **/
SimpleTest.waitForExplicitFinish();
// Used by file_animation_api.html
var prefName = "";
var expectedPrecision = 0;
+ var resistFingerprinting = false;
+ var reduceTimerPrecision = false;
function runTest() {
+ // No matter what we set the precision to, if we're in ResistFingerprinting mode
+ // we use the larger of the precision pref and the constant 100ms
+ if (resistFingerprinting) {
+ expectedPrecision = expectedPrecision < 100000 ? 100000 : expectedPrecision;
+ }
window.open("file_animation_api.html");
}
- function setupTest(resistFingerprinting, reduceTimerPrecision, ep) {
+ function setupTest(rfp, rtp, ep) {
// Set globals
expectedPrecision = ep;
+ resistFingerprinting = rfp;
+ reduceTimerPrecision = rtp;
prefName = "";
prefName += resistFingerprinting ? "privacy.resistFingerprinting " : "";
prefName += reduceTimerPrecision ? "privacy.reduceTimerPrecision " : "";
SpecialPowers.pushPrefEnv({"set":
[
+ ["dom.animations-api.core.enabled", true],
["privacy.resistFingerprinting", resistFingerprinting],
- ["dom.animations-api.core.enabled", true],
["privacy.reduceTimerPrecision", reduceTimerPrecision],
["privacy.resistFingerprinting.reduceTimerPrecision.microseconds", expectedPrecision]
]
}, runTest);
}
var testIndx = 0;
var testSequence = [
[true, false, 100000],
[false, true, 100000],
[true, false, 50000],
[false, true, 50000],
[true, false, 100],
[false, true, 100],
- [true, false, 13],
+ [true, true, 13],
[false, true, 13],
];
window.onload = () => {
setupTest(testSequence[testIndx][0], testSequence[testIndx][1], testSequence[testIndx][2]);
};
function done() {
--- a/browser/components/resistfingerprinting/test/mochitest/test_reduce_time_precision.html
+++ b/browser/components/resistfingerprinting/test/mochitest/test_reduce_time_precision.html
@@ -83,17 +83,17 @@ https://trac.torproject.org/projects/tor
worker.removeEventListener("message", onMessage);
let timeStamps = event.data;
for (let i = 0; i < timeStampCodes.length; i++) {
let timeStamp = timeStamps[i];
ok(isRounded(timeStamp, expectedPrecision),
"pref: " + prefname + " - '" +
"'" + timeStampCodes[i] +
- "' should be rounded to nearest " + expectedPrecision + " us in workers; saw " +
+ "' should be rounded to nearest " + expectedPrecision + " ms in workers; saw " +
timeStamp);
}
resolve();
};
worker.addEventListener("message", onMessage);
});
// Send the codes to its child worker.
@@ -115,16 +115,23 @@ https://trac.torproject.org/projects/tor
// check that the resolution is updated whether or not the worker was
// already started
let worker1 = new Worker("worker_child.js");
await SpecialPowers.pushPrefEnv({
"set": [["privacy.resistFingerprinting", resistFingerprinting],
["privacy.reduceTimerPrecision", reduceTimerPrecision],
["privacy.resistFingerprinting.reduceTimerPrecision.microseconds", expectedPrecision * 1000]
]});
+
+ // No matter what we set the precision to, if we're in ResistFingerprinting mode
+ // we use the larger of the precision pref and the constant 100ms
+ if (resistFingerprinting) {
+ expectedPrecision = expectedPrecision < 100 ? 100 : expectedPrecision;
+ }
+
let worker2 = new Worker("worker_child.js");
// Allow ~550 ms to elapse, so we can get non-zero
// time values for all elements.
await new Promise(resolve => window.setTimeout(resolve, 550));
await checkWorker(worker1, prefname, expectedPrecision);
await checkWorker(worker2, prefname, expectedPrecision);
}
@@ -148,31 +155,37 @@ https://trac.torproject.org/projects/tor
prefname += reduceTimerPrecision ? "privacy.reduceTimerPrecision " : "";
await SpecialPowers.pushPrefEnv({
"set": [["privacy.resistFingerprinting", resistFingerprinting],
["privacy.reduceTimerPrecision", reduceTimerPrecision],
["privacy.resistFingerprinting.reduceTimerPrecision.microseconds", expectedPrecision * 1000]
]});
+ // No matter what we set the precision to, if we're in ResistFingerprinting mode
+ // we use the larger of the precision pref and the constant 100ms
+ if (resistFingerprinting) {
+ expectedPrecision = expectedPrecision < 100 ? 100 : expectedPrecision;
+ }
+
// Loop through each timeStampCode, evaluate it,
// and check if it is rounded
for (let timeStampCode of timeStampCodesDOM) {
let timeStamp = eval(timeStampCode);
ok(isRounded(timeStamp, expectedPrecision),
"pref: " + prefname + " - '" +
"'" + timeStampCode +
"' should be rounded to nearest " +
- expectedPrecision + " us; saw " +
+ expectedPrecision + " ms; saw " +
timeStamp);
}
}
add_task(async function testDOMRFP() {
- await testDOM(true, false, 100);
+ await testDOM(true, true, 100);
await testDOM(true, false, 13);
await testDOM(true, false, .13);
});
add_task(async function testDOMRTP() {
await testDOM(false, true, 100);
await testDOM(false, true, 13);
await testDOM(false, true, .13);
--- a/toolkit/components/resistfingerprinting/nsRFPService.cpp
+++ b/toolkit/components/resistfingerprinting/nsRFPService.cpp
@@ -56,16 +56,17 @@ static mozilla::LazyLogModule gResistFin
#define RFP_DEFAULT_SPOOFING_KEYBOARD_REGION KeyboardRegion::US
NS_IMPL_ISUPPORTS(nsRFPService, nsIObserver)
static StaticRefPtr<nsRFPService> sRFPService;
static bool sInitialized = false;
Atomic<bool, ReleaseAcquire> nsRFPService::sPrivacyResistFingerprinting;
Atomic<bool, ReleaseAcquire> nsRFPService::sPrivacyTimerPrecisionReduction;
+// Note: anytime you want to use this variable, you should probably use TimerResolution() instead
Atomic<uint32_t, ReleaseAcquire> sResolutionUSec;
static uint32_t sVideoFramesPerSec;
static uint32_t sVideoDroppedRatio;
static uint32_t sTargetVideoRes;
nsDataHashtable<KeyboardHashKey, const SpoofingKeyboardCode*>*
nsRFPService::sSpoofingKeyboardCodes = nullptr;
/* static */
@@ -83,58 +84,68 @@ nsRFPService::GetOrCreate()
ClearOnShutdown(&sRFPService);
sInitialized = true;
}
return sRFPService;
}
+inline double
+TimerResolution()
+{
+ if(nsRFPService::IsResistFingerprintingEnabled()) {
+ return max(100000.0, (double)sResolutionUSec);
+ }
+ return sResolutionUSec;
+}
+
/* static */
bool
nsRFPService::IsResistFingerprintingEnabled()
{
return sPrivacyResistFingerprinting;
}
/* static */
bool
nsRFPService::IsTimerPrecisionReductionEnabled()
{
return (sPrivacyTimerPrecisionReduction || IsResistFingerprintingEnabled()) &&
- sResolutionUSec != 0;
+ TimerResolution() != 0;
}
/* static */
double
nsRFPService::ReduceTimePrecisionAsMSecs(double aTime)
{
if (!IsTimerPrecisionReductionEnabled()) {
return aTime;
}
- const double resolutionMSec = sResolutionUSec / 1000.0;
+ const double resolutionMSec = TimerResolution() / 1000.0;
double ret = floor(aTime / resolutionMSec) * resolutionMSec;
#if defined(DEBUG)
MOZ_LOG(gResistFingerprintingLog, LogLevel::Verbose,
("Given: %.*f, Rounding with %.*f, Intermediate: %.*f, Got: %.*f",
DBL_DIG-1, aTime, DBL_DIG-1, resolutionMSec, DBL_DIG-1, floor(aTime / resolutionMSec), DBL_DIG-1, ret));
#endif
return ret;
}
/* static */
double
nsRFPService::ReduceTimePrecisionAsUSecs(double aTime)
{
if (!IsTimerPrecisionReductionEnabled()) {
return aTime;
}
- double ret = floor(aTime / sResolutionUSec) * sResolutionUSec;
+ double resolutionUSec = TimerResolution();
+ double ret = floor(aTime / resolutionUSec) * resolutionUSec;
#if defined(DEBUG)
- double tmp_sResolutionUSec = sResolutionUSec;
+ double tmp_sResolutionUSec = resolutionUSec;
MOZ_LOG(gResistFingerprintingLog, LogLevel::Verbose,
("Given: %.*f, Rounding with %.*f, Intermediate: %.*f, Got: %.*f",
DBL_DIG-1, aTime, DBL_DIG-1, tmp_sResolutionUSec, DBL_DIG-1, floor(aTime / tmp_sResolutionUSec), DBL_DIG-1, ret));
#endif
return ret;
}
/* static */
@@ -146,29 +157,30 @@ nsRFPService::CalculateTargetVideoResolu
/* static */
double
nsRFPService::ReduceTimePrecisionAsSecs(double aTime)
{
if (!IsTimerPrecisionReductionEnabled()) {
return aTime;
}
- if (sResolutionUSec < 1000000) {
+ double resolutionUSec = TimerResolution();
+ if (TimerResolution() < 1000000) {
// The resolution is smaller than one sec. Use the reciprocal to avoid
// floating point error.
- const double resolutionSecReciprocal = 1000000.0 / sResolutionUSec;
+ const double resolutionSecReciprocal = 1000000.0 / resolutionUSec;
double ret = floor(aTime * resolutionSecReciprocal) / resolutionSecReciprocal;
#if defined(DEBUG)
MOZ_LOG(gResistFingerprintingLog, LogLevel::Verbose,
("Given: %.*f, Reciprocal Rounding with %.*f, Intermediate: %.*f, Got: %.*f",
DBL_DIG-1, aTime, DBL_DIG-1, resolutionSecReciprocal, DBL_DIG-1, floor(aTime * resolutionSecReciprocal), DBL_DIG-1, ret));
#endif
return ret;
}
- const double resolutionSec = sResolutionUSec / 1000000.0;
+ const double resolutionSec = resolutionUSec / 1000000.0;
double ret = floor(aTime / resolutionSec) * resolutionSec;
#if defined(DEBUG)
MOZ_LOG(gResistFingerprintingLog, LogLevel::Verbose,
("Given: %.*f, Rounding with %.*f, Intermediate: %.*f, Got: %.*f",
DBL_DIG-1, aTime, DBL_DIG-1, resolutionSec, DBL_DIG-1, floor(aTime / resolutionSec), DBL_DIG-1, ret));
#endif
return ret;
}
@@ -333,17 +345,17 @@ nsRFPService::Init()
}
// This function updates only timing-related fingerprinting items
void
nsRFPService::UpdateTimers() {
MOZ_ASSERT(NS_IsMainThread());
if (sPrivacyResistFingerprinting || sPrivacyTimerPrecisionReduction) {
- JS::SetTimeResolutionUsec(sResolutionUSec);
+ JS::SetTimeResolutionUsec(TimerResolution());
} else if (sInitialized) {
JS::SetTimeResolutionUsec(0);
}
}
// This function updates every fingerprinting item necessary except timing-related
void