Bug 1424341 Add privacy.reduceTimerPrecision and privacy.reduceTimerPrecision.microseconds prefs draft
authorTom Ritter <tom@mozilla.com>
Tue, 09 Jan 2018 14:58:34 -0600
changeset 718833 70c3c08937cdc3d303256278ec1143b18de7d052
parent 718676 d97ff9ec96fc3310ff5c6043f5137bbcab7a8fe6
child 718834 8f02e33c813cb77d408649ee8c09f3ace7929654
push id95052
push userbmo:tom@mozilla.com
push dateWed, 10 Jan 2018 21:56:43 +0000
bugs1424341
milestone59.0a1
Bug 1424341 Add privacy.reduceTimerPrecision and privacy.reduceTimerPrecision.microseconds prefs This pref does not override privacy.resistFingerprinting, but when it is set (and privacy.resistFingerprinting is not) we will still adjust the precision of almost all timers. The adjustment amount is the second pref, which is defaulted to 20us but now dynamically adjustable (in the scale of microseconds.) This patch does _not_ address the performance API, which privacy.resistFingerprinting disables. We are landing this preffed on at the current value we clamp performance.now() at which is 20us. MozReview-Commit-ID: ESZlSvH9w1D
browser/app/profile/firefox.js
dom/ipc/ContentPrefs.cpp
modules/libpref/Preferences.cpp
modules/libpref/init/all.js
toolkit/components/resistfingerprinting/nsRFPService.cpp
toolkit/components/resistfingerprinting/nsRFPService.h
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1215,16 +1215,18 @@ pref("services.sync.prefs.sync.privacy.c
 pref("services.sync.prefs.sync.privacy.clearOnShutdown.offlineApps", true);
 pref("services.sync.prefs.sync.privacy.clearOnShutdown.sessions", true);
 pref("services.sync.prefs.sync.privacy.clearOnShutdown.siteSettings", true);
 pref("services.sync.prefs.sync.privacy.donottrackheader.enabled", true);
 pref("services.sync.prefs.sync.privacy.sanitize.sanitizeOnShutdown", true);
 pref("services.sync.prefs.sync.privacy.trackingprotection.enabled", true);
 pref("services.sync.prefs.sync.privacy.trackingprotection.pbmode.enabled", true);
 pref("services.sync.prefs.sync.privacy.resistFingerprinting", true);
+pref("services.sync.prefs.sync.privacy.reduceTimerPrecision", true);
+pref("services.sync.prefs.sync.privacy.resistFingerprinting.reduceTimerPrecision.microseconds", true);
 pref("services.sync.prefs.sync.security.OCSP.enabled", true);
 pref("services.sync.prefs.sync.security.OCSP.require", true);
 pref("services.sync.prefs.sync.security.default_personal_cert", true);
 pref("services.sync.prefs.sync.security.tls.version.min", true);
 pref("services.sync.prefs.sync.security.tls.version.max", true);
 pref("services.sync.prefs.sync.services.sync.syncedTabs.showRemoteIcons", true);
 pref("services.sync.prefs.sync.signon.rememberSignons", true);
 pref("services.sync.prefs.sync.spellchecker.dictionary", true);
--- a/dom/ipc/ContentPrefs.cpp
+++ b/dom/ipc/ContentPrefs.cpp
@@ -280,16 +280,18 @@ const char* mozilla::dom::ContentPrefs::
   "network.tcp.keepalive.idle_time",
   "network.tcp.keepalive.probe_count",
   "network.tcp.keepalive.retry_interval",
   "network.tcp.sendbuffer",
   "nglayout.debug.invalidation",
   "privacy.donottrackheader.enabled",
   "privacy.firstparty.isolate",
   "privacy.firstparty.isolate.restrict_opener_access",
+  "privacy.reduceTimerPrecision",
+  "privacy.reduceTimerPrecision.microseconds",
   "privacy.resistFingerprinting",
   "privacy.resistFingerprinting.target_video_res",
   "privacy.resistFingerprinting.video_dropped_ratio",
   "privacy.resistFingerprinting.video_frames_per_sec",
   "privacy.trackingprotection.lower_network_priority",
   "privacy.window.maxInnerHeight",
   "privacy.window.maxInnerWidth",
   "security.csp.enable",
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -5011,17 +5011,17 @@ Preferences::AddAtomicUintVarCache(Atomi
                                 data,
                                 Preferences::ExactMatch,
                                 /* isPriority */ true);
   return NS_OK;
 }
 
 // Since the definition of template functions is not in a header file, we
 // need to explicitly specify the instantiations that are required. Currently
-// only the order=Relaxed variant is needed.
+// limited orders are needed and therefore implemented.
 template nsresult
 Preferences::AddAtomicBoolVarCache(Atomic<bool, Relaxed>*, const char*, bool);
 
 template nsresult
 Preferences::AddAtomicBoolVarCache(Atomic<bool, ReleaseAcquire>*,
                                    const char*,
                                    bool);
 
@@ -5035,16 +5035,21 @@ Preferences::AddAtomicIntVarCache(Atomic
                                   const char*,
                                   int32_t);
 
 template nsresult
 Preferences::AddAtomicUintVarCache(Atomic<uint32_t, Relaxed>*,
                                    const char*,
                                    uint32_t);
 
+template nsresult
+Preferences::AddAtomicUintVarCache(Atomic<uint32_t, ReleaseAcquire>*,
+                                   const char*,
+                                   uint32_t);
+
 static void
 FloatVarChanged(const char* aPref, void* aClosure)
 {
   CacheData* cache = static_cast<CacheData*>(aClosure);
   *static_cast<float*>(cache->mCacheLocation) =
     Preferences::GetFloat(aPref, cache->mDefaultValueFloat);
 }
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1375,16 +1375,22 @@ pref("privacy.trackingprotection.annotat
 // First Party Isolation (double keying), disabled by default
 pref("privacy.firstparty.isolate",                        false);
 // If false, two windows in the same domain with different first party domains
 // (top level URLs) can access resources through window.opener.
 // This pref is effective only when "privacy.firstparty.isolate" is true.
 pref("privacy.firstparty.isolate.restrict_opener_access", true);
 // Anti-fingerprinting, disabled by default
 pref("privacy.resistFingerprinting", false);
+// A subset of Resist Fingerprinting protections focused specifically on timers for testing
+// This affects the Animation API, the performance APIs, Date.getTime, Event.timestamp,
+//   File.lastModified, audioContext.currentTime, canvas.captureStream.currentTime
+pref("privacy.reduceTimerPrecision", true);
+// Dynamically tune the resolution of the timer reduction for both of the two above prefs
+pref("privacy.resistFingerprinting.reduceTimerPrecision.microseconds", 20);
 // Lower the priority of network loads for resources on the tracking protection list.
 // Note that this requires the privacy.trackingprotection.annotate_channels pref to be on in order to have any effect.
 #ifdef NIGHTLY_BUILD
 pref("privacy.trackingprotection.lower_network_priority", true);
 #else
 pref("privacy.trackingprotection.lower_network_priority", false);
 #endif
 
--- a/toolkit/components/resistfingerprinting/nsRFPService.cpp
+++ b/toolkit/components/resistfingerprinting/nsRFPService.cpp
@@ -30,30 +30,34 @@
 #include "prenv.h"
 
 #include "js/Date.h"
 
 using namespace mozilla;
 using namespace std;
 
 #define RESIST_FINGERPRINTING_PREF "privacy.resistFingerprinting"
+#define RFP_TIMER_PREF "privacy.reduceTimerPrecision"
+#define RFP_TIMER_VALUE_PREF "privacy.resistFingerprinting.reduceTimerPrecision.microseconds"
+#define RFP_TIMER_VALUE_DEFAULT 20
 #define RFP_SPOOFED_FRAMES_PER_SEC_PREF "privacy.resistFingerprinting.video_frames_per_sec"
 #define RFP_SPOOFED_DROPPED_RATIO_PREF  "privacy.resistFingerprinting.video_dropped_ratio"
 #define RFP_TARGET_VIDEO_RES_PREF "privacy.resistFingerprinting.target_video_res"
 #define RFP_SPOOFED_FRAMES_PER_SEC_DEFAULT 30
 #define RFP_SPOOFED_DROPPED_RATIO_DEFAULT  5
 #define RFP_TARGET_VIDEO_RES_DEFAULT 480
 #define PROFILE_INITIALIZED_TOPIC "profile-initial-state"
 
 NS_IMPL_ISUPPORTS(nsRFPService, nsIObserver)
 
 static StaticRefPtr<nsRFPService> sRFPService;
 static bool sInitialized = false;
 Atomic<bool, ReleaseAcquire> nsRFPService::sPrivacyResistFingerprinting;
-static uint32_t kResolutionUSec = 100000;
+Atomic<bool, ReleaseAcquire> nsRFPService::sPrivacyTimerPrecisionReduction;
+Atomic<uint32_t, ReleaseAcquire> sResolutionUSec;
 static uint32_t sVideoFramesPerSec;
 static uint32_t sVideoDroppedRatio;
 static uint32_t sTargetVideoRes;
 
 /* static */
 nsRFPService*
 nsRFPService::GetOrCreate()
 {
@@ -72,54 +76,54 @@ nsRFPService::GetOrCreate()
 
   return sRFPService;
 }
 
 /* static */
 double
 nsRFPService::ReduceTimePrecisionAsMSecs(double aTime)
 {
-  if (!IsResistFingerprintingEnabled()) {
+  if (!IsTimerPrecisionReductionEnabled()) {
     return aTime;
   }
-  const double resolutionMSec = kResolutionUSec / 1000.0;
+  const double resolutionMSec = sResolutionUSec / 1000.0;
   return floor(aTime / resolutionMSec) * resolutionMSec;
 }
 
 /* static */
 double
 nsRFPService::ReduceTimePrecisionAsUSecs(double aTime)
 {
-  if (!IsResistFingerprintingEnabled()) {
+  if (!IsTimerPrecisionReductionEnabled()) {
     return aTime;
   }
-  return floor(aTime / kResolutionUSec) * kResolutionUSec;
+  return floor(aTime / sResolutionUSec) * sResolutionUSec;
 }
 
 /* static */
 uint32_t
 nsRFPService::CalculateTargetVideoResolution(uint32_t aVideoQuality)
 {
   return aVideoQuality * NSToIntCeil(aVideoQuality * 16 / 9.0);
 }
 
 /* static */
 double
 nsRFPService::ReduceTimePrecisionAsSecs(double aTime)
 {
-  if (!IsResistFingerprintingEnabled()) {
+  if (!IsTimerPrecisionReductionEnabled()) {
     return aTime;
   }
-  if (kResolutionUSec < 1000000) {
+  if (sResolutionUSec < 1000000) {
     // The resolution is smaller than one sec.  Use the reciprocal to avoid
     // floating point error.
-    const double resolutionSecReciprocal = 1000000.0 / kResolutionUSec;
+    const double resolutionSecReciprocal = 1000000.0 / sResolutionUSec;
     return floor(aTime * resolutionSecReciprocal) / resolutionSecReciprocal;
   }
-  const double resolutionSec = kResolutionUSec / 1000000.0;
+  const double resolutionSec = sResolutionUSec / 1000000.0;
   return floor(aTime / resolutionSec) * resolutionSec;
 }
 
 /* static */
 uint32_t
 nsRFPService::GetSpoofedTotalFrames(double aTime)
 {
   double time = ReduceTimePrecisionAsSecs(aTime);
@@ -237,50 +241,76 @@ nsRFPService::Init()
 #endif
 
   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
   NS_ENSURE_TRUE(prefs, NS_ERROR_NOT_AVAILABLE);
 
   rv = prefs->AddObserver(RESIST_FINGERPRINTING_PREF, this, false);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  rv = prefs->AddObserver(RFP_TIMER_PREF, this, false);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = prefs->AddObserver(RFP_TIMER_VALUE_PREF, this, false);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  Preferences::AddAtomicBoolVarCache(&sPrivacyTimerPrecisionReduction,
+                                     RFP_TIMER_PREF,
+                                     true);
+
+  Preferences::AddAtomicUintVarCache(&sResolutionUSec,
+                                     RFP_TIMER_VALUE_PREF,
+                                     RFP_TIMER_VALUE_DEFAULT);
   Preferences::AddUintVarCache(&sVideoFramesPerSec,
                                RFP_SPOOFED_FRAMES_PER_SEC_PREF,
                                RFP_SPOOFED_FRAMES_PER_SEC_DEFAULT);
   Preferences::AddUintVarCache(&sVideoDroppedRatio,
                                RFP_SPOOFED_DROPPED_RATIO_PREF,
                                RFP_SPOOFED_DROPPED_RATIO_DEFAULT);
   Preferences::AddUintVarCache(&sTargetVideoRes,
                                RFP_TARGET_VIDEO_RES_PREF,
                                RFP_TARGET_VIDEO_RES_DEFAULT);
 
   // We backup the original TZ value here.
   const char* tzValue = PR_GetEnv("TZ");
   if (tzValue) {
     mInitialTZValue = nsCString(tzValue);
   }
 
-  // Call UpdatePref() here to cache the value of 'privacy.resistFingerprinting'
-  // and set the timezone.
-  UpdatePref();
+  // Call Update here to cache the values of the prefs and set the timezone.
+  UpdateRFPPref();
 
   return rv;
 }
 
+// This function updates only timing-related fingerprinting items
 void
-nsRFPService::UpdatePref()
+nsRFPService::UpdateTimers() {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (sPrivacyResistFingerprinting || sPrivacyTimerPrecisionReduction) {
+    JS::SetTimeResolutionUsec(sResolutionUSec);
+  } else if (sInitialized) {
+    JS::SetTimeResolutionUsec(0);
+  }
+}
+
+
+// This function updates every fingerprinting item necessary except timing-related
+void
+nsRFPService::UpdateRFPPref()
 {
   MOZ_ASSERT(NS_IsMainThread());
   sPrivacyResistFingerprinting = Preferences::GetBool(RESIST_FINGERPRINTING_PREF);
 
+  UpdateTimers();
+
   if (sPrivacyResistFingerprinting) {
     PR_SetEnv("TZ=UTC");
-    JS::SetTimeResolutionUsec(kResolutionUSec);
   } else if (sInitialized) {
-    JS::SetTimeResolutionUsec(0);
     // We will not touch the TZ value if 'privacy.resistFingerprinting' is false during
     // the time of initialization.
     if (!mInitialTZValue.IsEmpty()) {
       nsAutoCString tzValue = NS_LITERAL_CSTRING("TZ=") + mInitialTZValue;
       static char* tz = nullptr;
 
       // If the tz has been set before, we free it first since it will be allocated
       // a new value later.
@@ -318,29 +348,34 @@ nsRFPService::StartShutdown()
 
   if (obs) {
     obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
 
     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
 
     if (prefs) {
       prefs->RemoveObserver(RESIST_FINGERPRINTING_PREF, this);
+      prefs->RemoveObserver(RFP_TIMER_PREF, this);
+      prefs->RemoveObserver(RFP_TIMER_VALUE_PREF, this);
     }
   }
 }
 
 NS_IMETHODIMP
 nsRFPService::Observe(nsISupports* aObject, const char* aTopic,
                       const char16_t* aMessage)
 {
   if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) {
     NS_ConvertUTF16toUTF8 pref(aMessage);
 
-    if (pref.EqualsLiteral(RESIST_FINGERPRINTING_PREF)) {
-      UpdatePref();
+    if (pref.EqualsLiteral(RFP_TIMER_PREF) || pref.EqualsLiteral(RFP_TIMER_VALUE_PREF)) {
+      UpdateTimers();
+    }
+    else if (pref.EqualsLiteral(RESIST_FINGERPRINTING_PREF)) {
+      UpdateRFPPref();
 
 #if defined(XP_WIN)
       if (!XRE_IsE10sParentProcess()) {
         // Windows does not follow POSIX. Updates to the TZ environment variable
         // are not reflected immediately on that platform as they are on UNIX
         // systems without this call.
         _tzset();
       }
--- a/toolkit/components/resistfingerprinting/nsRFPService.h
+++ b/toolkit/components/resistfingerprinting/nsRFPService.h
@@ -28,16 +28,20 @@ public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
   static nsRFPService* GetOrCreate();
   static bool IsResistFingerprintingEnabled()
   {
     return sPrivacyResistFingerprinting;
   }
+  static bool IsTimerPrecisionReductionEnabled()
+  {
+    return sPrivacyTimerPrecisionReduction || IsResistFingerprintingEnabled();
+  }
 
   // The following Reduce methods can be called off main thread.
   static double ReduceTimePrecisionAsMSecs(double aTime);
   static double ReduceTimePrecisionAsUSecs(double aTime);
   static double ReduceTimePrecisionAsSecs(double aTime);
 
   // This method calculates the video resolution (i.e. height x width) based
   // on the video quality (480p, 720p, etc).
@@ -54,19 +58,21 @@ public:
 
 private:
   nsresult Init();
 
   nsRFPService() {}
 
   ~nsRFPService() {}
 
-  void UpdatePref();
+  void UpdateTimers();
+  void UpdateRFPPref();
   void StartShutdown();
 
   static Atomic<bool, ReleaseAcquire> sPrivacyResistFingerprinting;
+  static Atomic<bool, ReleaseAcquire> sPrivacyTimerPrecisionReduction;
 
   nsCString mInitialTZValue;
 };
 
 } // mozilla namespace
 
 #endif /* __nsRFPService_h__ */