From 86568bf421919ea13c0c5e87b0a534c2e0e35f01 Mon Sep 17 00:00:00 2001 From: Tom Ritter Date: Fri, 2 Aug 2019 07:35:59 +0200 Subject: [PATCH] Bug 1387894 - Resolve timer intermittents when reduceTimerPrecision is disabled. r=smaug We unconditionally clamp all times to 20us and not just performance.now() This will consistently apply a 'safe' minimal clamping (it's not safe but I guess it's safer than ns-level precision) to all timestamps, and remove intermittents that are caused by comparing a clamped performance.now() to an unclamped [something else]. Differential Revision: https://phabricator.services.mozilla.com/D38806 --HG-- extra : rebase_source : 85f42a69cc88101c460acf784962076d39813627 --- dom/performance/Performance.cpp | 5 +- modules/libpref/init/StaticPrefList.yaml | 9 +++ .../resistfingerprinting/nsRFPService.cpp | 56 ++++++++++++++----- 3 files changed, 52 insertions(+), 18 deletions(-) diff --git a/dom/performance/Performance.cpp b/dom/performance/Performance.cpp index 1459c683b4cc..39d2bc8480f3 100644 --- a/dom/performance/Performance.cpp +++ b/dom/performance/Performance.cpp @@ -94,10 +94,7 @@ DOMHighResTimeStamp Performance::Now() { return rawTime; } - const double maxResolutionMs = 0.020; - DOMHighResTimeStamp minimallyClamped = - floor(rawTime / maxResolutionMs) * maxResolutionMs; - return nsRFPService::ReduceTimePrecisionAsMSecs(minimallyClamped, + return nsRFPService::ReduceTimePrecisionAsMSecs(rawTime, GetRandomTimelineSeed()); } diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index 0c4ca8aad8d8..8557d8a87f7c 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -5750,6 +5750,15 @@ value: true mirror: always +# If privacy.reduceTimerPrecision is false, this pref controls whether or not to +# clamp all timers at a fixed 20 microsconds. It should always be enabled, and is +# only specified as a pref to enable an emergency disabling in the event of catastrophic +# failure +- name: privacy.reduceTimerPrecision.unconditional + type: RelaxedAtomicBool + value: true + mirror: always + # Dynamically tune the resolution of the timer reduction for both of the two above prefs - name: privacy.resistFingerprinting.reduceTimerPrecision.microseconds type: RelaxedAtomicUint32 diff --git a/toolkit/components/resistfingerprinting/nsRFPService.cpp b/toolkit/components/resistfingerprinting/nsRFPService.cpp index 2a7dd88eb2ef..ba07abd09e07 100644 --- a/toolkit/components/resistfingerprinting/nsRFPService.cpp +++ b/toolkit/components/resistfingerprinting/nsRFPService.cpp @@ -49,6 +49,9 @@ static mozilla::LazyLogModule gResistFingerprintingLog( #define RESIST_FINGERPRINTING_PREF "privacy.resistFingerprinting" #define RFP_TIMER_PREF "privacy.reduceTimerPrecision" +#define RFP_TIMER_UNCONDITIONAL_PREF \ + "privacy.reduceTimerPrecision.unconditional" +#define RFP_TIMER_UNCONDITIONAL_VALUE 20 #define RFP_TIMER_VALUE_PREF \ "privacy.resistFingerprinting.reduceTimerPrecision.microseconds" #define RFP_TIMER_VALUE_DEFAULT 1000 @@ -474,7 +477,23 @@ double nsRFPService::ReduceTimePrecisionImpl(double aTime, TimeScale aTimeScale, double aResolutionUSec, int64_t aContextMixin, TimerPrecisionType aType) { - if (!IsTimerPrecisionReductionEnabled(aType) || aResolutionUSec <= 0) { + // This boolean will serve as a flag indicating we are clamping the time + // unconditionally. We do this when timer reduction preference is off; but we + // still want to apply 20us clamping to al timestamps to avoid leaking + // nano-second precision. + bool unconditionalClamping = false; + if (!IsTimerPrecisionReductionEnabled(aType)) { + if (!StaticPrefs::privacy_reduceTimerPrecision_unconditional()) { + return aTime; + } else { + unconditionalClamping = true; + aResolutionUSec = RFP_TIMER_UNCONDITIONAL_VALUE; // 20 microseconds + aContextMixin = 0; // Just clarifies our logging statement at the end, + // otherwise unused + } + } + + if (aResolutionUSec <= 0) { return aTime; } @@ -494,13 +513,14 @@ double nsRFPService::ReduceTimePrecisionImpl(double aTime, TimeScale aTimeScale, // given a relative timestamp with a mixin of 0 which is incorrect. Anyone // running a debug build _probably_ has an accurate clock, and if they don't, // they'll hopefully find this message and understand why things are crashing. - if (aContextMixin == 0 && aType == TimerPrecisionType::All && - timeAsInt < 1204233985000) { - MOZ_LOG(gResistFingerprintingLog, LogLevel::Error, - ("About to assert. aTime=%lli<1204233985000 aContextMixin=%" PRId64 - " aType=%s", - timeAsInt, aContextMixin, - (aType == TimerPrecisionType::RFPOnly ? "RFPOnly" : "All"))); + const long long kFeb282008 = 1204233985000; + if (!unconditionalClamping && aContextMixin == 0 && + aType == TimerPrecisionType::All && timeAsInt < kFeb282008) { + MOZ_LOG( + gResistFingerprintingLog, LogLevel::Error, + ("About to assert. aTime=%lli<%lli aContextMixin=%" PRId64 " aType=%s", + timeAsInt, kFeb282008, aContextMixin, + (aType == TimerPrecisionType::RFPOnly ? "RFPOnly" : "All"))); MOZ_ASSERT( false, "ReduceTimePrecisionImpl was given a relative time " @@ -526,7 +546,8 @@ double nsRFPService::ReduceTimePrecisionImpl(double aTime, TimeScale aTimeScale, floor(double(timeAsInt) / resolutionAsInt) * resolutionAsInt; long long midpoint = 0, clampedAndJittered = clamped; - if (StaticPrefs::privacy_resistFingerprinting_reduceTimerPrecision_jitter()) { + if (!unconditionalClamping && + StaticPrefs::privacy_resistFingerprinting_reduceTimerPrecision_jitter()) { if (!NS_FAILED(RandomMidpoint(clamped, resolutionAsInt, aContextMixin, &midpoint)) && timeAsInt >= clamped + midpoint) { @@ -539,13 +560,14 @@ double nsRFPService::ReduceTimePrecisionImpl(double aTime, TimeScale aTimeScale, MOZ_LOG( gResistFingerprintingLog, LogLevel::Verbose, - ("Given: (%.*f, Scaled: %.*f, Converted: %lli), Rounding with (%lli, " + ("Given: (%.*f, Scaled: %.*f, Converted: %lli), Rounding %s with (%lli, " "Originally %.*f), " "Intermediate: (%lli), Clamped: (%lli) Jitter: (%i Context: %" PRId64 " Midpoint: %lli) " "Final: (%lli Converted: %.*f)", - DBL_DIG - 1, aTime, DBL_DIG - 1, timeScaled, timeAsInt, resolutionAsInt, - DBL_DIG - 1, aResolutionUSec, + DBL_DIG - 1, aTime, DBL_DIG - 1, timeScaled, timeAsInt, + (unconditionalClamping ? "unconditionally" : "normally"), + resolutionAsInt, DBL_DIG - 1, aResolutionUSec, (long long)floor(double(timeAsInt) / resolutionAsInt), clamped, StaticPrefs::privacy_resistFingerprinting_reduceTimerPrecision_jitter(), aContextMixin, midpoint, clampedAndJittered, DBL_DIG - 1, ret)); @@ -692,8 +714,9 @@ void nsRFPService::GetSpoofedUserAgent(nsACString& userAgent, } static const char* gCallbackPrefs[] = { - RESIST_FINGERPRINTING_PREF, RFP_TIMER_PREF, RFP_TIMER_VALUE_PREF, - RFP_JITTER_VALUE_PREF, nullptr, + RESIST_FINGERPRINTING_PREF, RFP_TIMER_PREF, + RFP_TIMER_UNCONDITIONAL_PREF, RFP_TIMER_VALUE_PREF, + RFP_JITTER_VALUE_PREF, nullptr, }; nsresult nsRFPService::Init() { @@ -754,6 +777,10 @@ void nsRFPService::UpdateTimers() { privacy_resistFingerprinting_reduceTimerPrecision_jitter()); JS::SetReduceMicrosecondTimePrecisionCallback( nsRFPService::ReduceTimePrecisionAsUSecsWrapper); + } else if (StaticPrefs::privacy_reduceTimerPrecision_unconditional()) { + JS::SetTimeResolutionUsec(RFP_TIMER_UNCONDITIONAL_VALUE, false); + JS::SetReduceMicrosecondTimePrecisionCallback( + nsRFPService::ReduceTimePrecisionAsUSecsWrapper); } else if (sInitialized) { JS::SetTimeResolutionUsec(0, false); } @@ -1039,6 +1066,7 @@ void nsRFPService::PrefChanged(const char* aPref) { nsDependentCString pref(aPref); if (pref.EqualsLiteral(RFP_TIMER_PREF) || + pref.EqualsLiteral(RFP_TIMER_UNCONDITIONAL_PREF) || pref.EqualsLiteral(RFP_TIMER_VALUE_PREF) || pref.EqualsLiteral(RFP_JITTER_VALUE_PREF)) { UpdateTimers();