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
--- 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__ */