Bug 1431455 Fix a regression for ResistFingerprinting: use the larger of the reduceTimerPrecision pref and the constant 100ms r=bkelly
authorTom Ritter <tom@mozilla.com>
Thu, 18 Jan 2018 11:25:59 -0600
changeset 454303 a88543d4c4ac6910973b578ef7c4b97419a52108
parent 454302 6803a23209cdc4ed5039a28be59ff2a471129afb
child 454304 d4c110c146afff418fdf865b2efe4d7026751007
push id1648
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 12:45:47 +0000
treeherdermozilla-release@cbb9688c2eeb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbkelly
bugs1431455
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1431455 Fix a regression for ResistFingerprinting: use the larger of the reduceTimerPrecision pref and the constant 100ms r=bkelly MozReview-Commit-ID: 73MpmfEKoQG
browser/components/resistfingerprinting/test/browser/browser_performanceAPI.js
browser/components/resistfingerprinting/test/mochitest/test_animation_api.html
browser/components/resistfingerprinting/test/mochitest/test_reduce_time_precision.html
toolkit/components/resistfingerprinting/nsRFPService.cpp
--- 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