Bug 1432429 - Add FuzzyFox class and prefs. r=baku,froydnj
authorTom Ritter <tom@mozilla.com>
Tue, 18 Sep 2018 13:05:26 -0500
changeset 443186 920270da576f92d10e511d1f51eaf183ceb6483f
parent 443185 f1c0fa980fd1ce976b3628fe8ea445e6f337a168
child 443187 b00e84c0233b17a1daf0feccf660580aa5fe2d30
push id34943
push usercsabou@mozilla.com
push dateFri, 26 Oct 2018 21:57:01 +0000
treeherdermozilla-central@3dc7cdbb2b5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku, froydnj
bugs1432429
milestone65.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 1432429 - Add FuzzyFox class and prefs. r=baku,froydnj Creates a FuzzyFox class for implementating the core of the step/sleep algorithm. Starts it in nsLayoutStatics::Initialize() Adds the fuzzyfox prefs. Moves the ms2mt macros from TimeStamp_windows.cpp to TimeStamp_windows.h and creates a new public function GetQueryPerformanceFrequencyPerSec() to expose a static variable in the .cpp file. This is necessary to support the macros being usable anywhere. (And we use the macros in FuzzyFox.)
browser/app/profile/firefox.js
layout/build/nsLayoutStatics.cpp
mozglue/misc/TimeStamp_windows.cpp
mozglue/misc/TimeStamp_windows.h
toolkit/components/fuzzyfox/Fuzzyfox.cpp
toolkit/components/fuzzyfox/Fuzzyfox.h
toolkit/components/fuzzyfox/moz.build
toolkit/components/moz.build
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1192,16 +1192,18 @@ pref("services.sync.prefs.sync.privacy.c
 pref("services.sync.prefs.sync.privacy.clearOnShutdown.cookies", true);
 pref("services.sync.prefs.sync.privacy.clearOnShutdown.downloads", true);
 pref("services.sync.prefs.sync.privacy.clearOnShutdown.formdata", true);
 pref("services.sync.prefs.sync.privacy.clearOnShutdown.history", true);
 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.fuzzyfox.enabled", true);
+pref("services.sync.prefs.sync.privacy.fuzzyfox.clockgrainus", 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.privacy.resistFingerprinting.reduceTimerPrecision.jitter", true);
 pref("services.sync.prefs.sync.security.OCSP.enabled", true);
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -97,16 +97,17 @@
 #include "mozilla/dom/CustomElementRegistry.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "TouchManager.h"
 #include "DecoderDoctorLogger.h"
 #include "MediaDecoder.h"
 #include "mozilla/ClearSiteData.h"
+#include "mozilla/Fuzzyfox.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/StaticPresData.h"
 #include "mozilla/dom/WebIDLGlobalNameHash.h"
 #include "mozilla/dom/ipc/IPCBlobInputStreamStorage.h"
 #include "mozilla/dom/U2FTokenManager.h"
 #include "mozilla/dom/PointerEventHandler.h"
 #include "mozilla/dom/BlobURLProtocolHandler.h"
 #include "nsThreadManager.h"
@@ -279,16 +280,18 @@ nsLayoutStatics::Initialize()
   if (XRE_IsParentProcess()) {
     // On content process we initialize DOMPrefs when PContentChild is fully
     // initialized.
     mozilla::dom::DOMPrefs::Initialize();
   }
 
   nsThreadManager::InitializeShutdownObserver();
 
+  mozilla::Fuzzyfox::Start();
+
   ClearSiteData::Initialize();
 
   return NS_OK;
 }
 
 void
 nsLayoutStatics::Shutdown()
 {
--- a/mozglue/misc/TimeStamp_windows.cpp
+++ b/mozglue/misc/TimeStamp_windows.cpp
@@ -58,36 +58,30 @@ static const uint32_t kFailureThreshold 
 // If we are not able to get the value of GTC time increment, use this value
 // which is the most usual increment.
 static const DWORD kDefaultTimeIncrement = 156001;
 
 // ----------------------------------------------------------------------------
 // Global variables, not changing at runtime
 // ----------------------------------------------------------------------------
 
-/**
- * The [mt] unit:
- *
- * Many values are kept in ticks of the Performance Coutner x 1000,
- * further just referred as [mt], meaning milli-ticks.
- *
- * This is needed to preserve maximum precision of the performance frequency
- * representation.  GetTickCount64 values in milliseconds are multiplied with
- * frequency per second.  Therefor we need to multiply QPC value by 1000 to
- * have the same units to allow simple arithmentic with both QPC and GTC.
- */
-
-#define ms2mt(x) ((x) * sFrequencyPerSec)
-#define mt2ms(x) ((x) / sFrequencyPerSec)
-#define mt2ms_f(x) (double(x) / sFrequencyPerSec)
-
 // Result of QueryPerformanceFrequency
 // We use default of 1 for the case we can't use QueryPerformanceCounter
 // to make mt/ms conversions work despite that.
-static LONGLONG sFrequencyPerSec = 1;
+static uint64_t sFrequencyPerSec = 1;
+
+namespace mozilla {
+
+MFBT_API uint64_t
+GetQueryPerformanceFrequencyPerSec()
+{
+  return sFrequencyPerSec;
+}
+
+}
 
 // How much we are tolerant to GTC occasional loose of resoltion.
 // This number says how many multiples of the minimal GTC resolution
 // detected on the system are acceptable.  This number is empirical.
 static const LONGLONG kGTCTickLeapTolerance = 4;
 
 // Base tolerance (more: "inability of detection" range) threshold is calculated
 // dynamically, and kept in sGTCResolutionThreshold.
--- a/mozglue/misc/TimeStamp_windows.h
+++ b/mozglue/misc/TimeStamp_windows.h
@@ -6,16 +6,34 @@
 
 #ifndef mozilla_TimeStamp_windows_h
 #define mozilla_TimeStamp_windows_h
 
 #include "mozilla/Types.h"
 
 namespace mozilla {
 
+/**
+ * The [mt] unit:
+ *
+ * Many values are kept in ticks of the Performance Counter x 1000,
+ * further just referred as [mt], meaning milli-ticks.
+ *
+ * This is needed to preserve maximum precision of the performance frequency
+ * representation.  GetTickCount64 values in milliseconds are multiplied with
+ * frequency per second.  Therefore we need to multiply QPC value by 1000 to
+ * have the same units to allow simple arithmentic with both QPC and GTC.
+ */
+#define ms2mt(x) ((x) * mozilla::GetQueryPerformanceFrequencyPerSec())
+#define mt2ms(x) ((x) / mozilla::GetQueryPerformanceFrequencyPerSec())
+#define mt2ms_f(x) (double(x) / mozilla::GetQueryPerformanceFrequencyPerSec())
+
+MFBT_API uint64_t
+GetQueryPerformanceFrequencyPerSec();
+
 class TimeStamp;
 
 class TimeStampValue
 {
   friend struct IPC::ParamTraits<mozilla::TimeStampValue>;
   friend class TimeStamp;
 
   // Both QPC and GTC are kept in [mt] units.
new file mode 100644
--- /dev/null
+++ b/toolkit/components/fuzzyfox/Fuzzyfox.cpp
@@ -0,0 +1,382 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Fuzzyfox.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/SystemGroup.h"
+#include "mozilla/TimeStamp.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefService.h"
+#include "nsServiceManagerUtils.h"
+#include "prrng.h"
+#include "prtime.h"
+
+// For usleep/Sleep & QueryPerformanceFrequency
+#ifdef XP_WIN
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+
+using namespace mozilla;
+
+static LazyLogModule sFuzzyfoxLog("Fuzzyfox");
+
+#define US_TO_NS(x) ((x)*1000)
+#define NS_TO_US(x) ((x)/1000)
+
+#ifdef LOG
+#undef LOG
+#endif
+
+#define LOG(level, args) MOZ_LOG(sFuzzyfoxLog, mozilla::LogLevel::level, args)
+
+#define FUZZYFOX_ENABLED_PREF             "privacy.fuzzyfox.enabled"
+#define FUZZYFOX_ENABLED_PREF_DEFAULT     false
+#define FUZZYFOX_CLOCKGRAIN_PREF          "privacy.fuzzyfox.clockgrainus"
+#define FUZZYFOX_CLOCKGRAIN_PREF_DEFAULT  100
+
+Atomic<uint32_t, Relaxed> Fuzzyfox::sFuzzyfoxClockGrain;
+
+NS_IMPL_ISUPPORTS_INHERITED(Fuzzyfox, Runnable, nsIObserver)
+
+/* static */ void
+Fuzzyfox::Start()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  RefPtr<Fuzzyfox> r = new Fuzzyfox();
+  SystemGroup::Dispatch(TaskCategory::Other, r.forget());
+}
+
+Fuzzyfox::Fuzzyfox()
+  : Runnable("Fuzzyfox")
+  , mSanityCheck(false)
+  , mStartTime(0)
+  , mDuration(PickDuration())
+  , mTickType(eUptick)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // [[ I originally ran this after observing profile-after-change, but
+  // it turned out that this contructor was getting called _after_ that
+  // event had already fired. ]]
+  Preferences::AddAtomicUintVarCache(&sFuzzyfoxClockGrain,
+                                     FUZZYFOX_CLOCKGRAIN_PREF,
+                                     FUZZYFOX_CLOCKGRAIN_PREF_DEFAULT);
+
+  bool fuzzyfoxEnabled =
+    Preferences::GetBool(FUZZYFOX_ENABLED_PREF, FUZZYFOX_ENABLED_PREF_DEFAULT);
+
+  LOG(Info, ("PT(%p) Created Fuzzyfox, FuzzyFox is now %s \n",
+         this, (fuzzyfoxEnabled ? "enabled" : "disabled")));
+
+  TimeStamp::SetFuzzyfoxEnabled(fuzzyfoxEnabled);
+
+  // Should I see if these fail? And do what?
+  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+  prefs->AddObserver(FUZZYFOX_ENABLED_PREF, this, false);
+  prefs->AddObserver(FUZZYFOX_CLOCKGRAIN_PREF, this, false);
+}
+
+Fuzzyfox::~Fuzzyfox() = default;
+
+/*
+ * Fuzzyfox::Run is the core of FuzzyFox. Every so often we pop into this method, and pick
+ * a new point in the future to hold time constant until. If we have not reached the _previous_
+ * point in time we had picked, we sleep until we do so.
+ * Then we round the current time downwards to a configurable grain value, fix it in place so
+ * time does not advance, and let execution continue.
+ */
+NS_IMETHODIMP
+Fuzzyfox::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(FUZZYFOX_ENABLED_PREF)) {
+      bool fuzzyfoxEnabled =
+        Preferences::GetBool(FUZZYFOX_ENABLED_PREF, FUZZYFOX_ENABLED_PREF_DEFAULT);
+
+      LOG(Info, ("PT(%p) Observed a pref change, FuzzyFox is now %s \n",
+         this, (fuzzyfoxEnabled ? "enabled" : "disabled")));
+
+
+      TimeStamp::SetFuzzyfoxEnabled(fuzzyfoxEnabled);
+
+
+      if (TimeStamp::GetFuzzyfoxEnabled()) {
+        // Queue a runnable
+        nsCOMPtr<nsIRunnable> r = this;
+        SystemGroup::Dispatch(TaskCategory::Other, r.forget());
+      } else {
+        mStartTime = 0;
+        mTickType = eUptick;
+        mSanityCheck = false;
+      }
+    }
+  }
+  return NS_OK;
+}
+
+#define DISPATCH_AND_RETURN() \
+  nsCOMPtr<nsIRunnable> r = this; \
+  SystemGroup::Dispatch(TaskCategory::Other, r.forget()); \
+  return NS_OK
+
+NS_IMETHODIMP
+Fuzzyfox::Run()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!TimeStamp::GetFuzzyfoxEnabled()) {
+    LOG(Info, ("[FuzzyfoxEvent] PT(%p) Fuzzyfox is shut down, doing nothing \n", this));
+    return NS_OK;
+  }
+
+  if (mStartTime == 0) {
+    // This is the first time we are running afer enabling FuzzyFox. We need
+    // to prevent time from going backwards, so for the first run we round the time up
+    // to the next grain.
+    mStartTime = CeilToGrain(ActualTime());
+    MOZ_ASSERT(mStartTime != 0);
+    TimeStamp newTimeStamp = CeilToGrain(TimeStamp::NowUnfuzzed());
+    Fuzzyfox::UpdateClocks(mStartTime, newTimeStamp);
+
+    mSanityCheck = true;
+    LOG(Info, ("[FuzzyfoxEvent] PT(%p) Going to start Fuzzyfox, queuing up the job \n",
+       this));
+
+    DISPATCH_AND_RETURN();
+  }
+
+  // We need to check how long its been since we ran
+  uint64_t endTime = ActualTime();
+
+  uint64_t remaining = 0;
+
+  // Pick the amount to sleep
+  if (endTime < mStartTime) {
+    // This can only happen if we just enabled FuzzyFox, rounded up, and then re-ran the
+    // runnable before we advanced to the next grain.
+    // If that happens, then repeat the current time.
+    // We use mSanityCheck just to be sure (and will eventually remove it.)
+    MOZ_ASSERT(mSanityCheck);
+    LOG(Debug, ("[FuzzyfoxEvent] PT(%p) endTime < mStartTime mStartTime %" PRIu64 " endTime %" PRIu64 " \n",
+         this, mStartTime, endTime));
+
+    mSanityCheck = true;
+    DISPATCH_AND_RETURN();
+  }
+
+  uint64_t actualRunDuration = endTime - mStartTime;
+  if (actualRunDuration > mDuration) {
+    // We ran over our budget!
+    uint64_t over = actualRunDuration - mDuration;
+    LOG(Verbose, ("[FuzzyfoxEvent] PT(%p) Overran budget of %" PRIu32 " by %" PRIu64 " \n",
+         this, mDuration, over));
+
+    uint64_t nextDuration = PickDuration();
+    while (over > nextDuration) {
+      over -= nextDuration;
+      nextDuration = PickDuration();
+      mTickType = mTickType == eUptick ? eDowntick : eUptick;
+    }
+
+    remaining = nextDuration - over;
+  } else {
+    // Didn't go over budget
+    remaining = mDuration - actualRunDuration;
+    LOG(Verbose, ("[FuzzyfoxEvent] PT(%p) Finishing budget of %" PRIu32 " with %" PRIu64 " \n",
+        this, mDuration, remaining));
+  }
+  mSanityCheck = false;
+
+  // Sleep for now
+#ifdef XP_WIN
+  Sleep(remaining);
+#else
+  usleep(remaining);
+#endif
+
+  /*
+   * Update clocks (and fire pending events etc)
+   *
+   * Note: Anytime we round the current time to the grain, and then round the 'real'
+   * time to the grain, we are introducing the risk that we split the grain. That is,
+   * the time advances enough after the first rounding that the second rounding causes
+   * us to move to a different grain.
+   *
+   * In theory, such an occurance breaks the security of FuzzyFox, and if an
+   * attacker can influence the event to occur reliably, and then measure against it
+   * they can attack FuzzyFox. But such an attack is so difficult that it will
+   * never be acheived until you read this comment in a future Academic Publication
+   * that demonstrates it. And at that point the paper would surely never be accepted
+   * into any _respectable_ journal unless the authors had also presented a solution
+   * for the issue that was usable and incorporated into Firefox!
+   */
+  uint64_t newTime = FloorToGrain(ActualTime());
+  TimeStamp newTimeStamp = FloorToGrain(TimeStamp::NowUnfuzzed());
+  UpdateClocks(newTime, newTimeStamp);
+
+  // Reset values
+  mTickType = mTickType == eUptick ? eDowntick : eUptick;
+  mStartTime = ActualTime();
+  mDuration = PickDuration();
+
+  DISPATCH_AND_RETURN();
+}
+
+/*
+ * ActualTime returns the unfuzzed/unrounded time in microseconds since the epoch
+ */
+uint64_t
+Fuzzyfox::ActualTime()
+{
+  return PR_Now();
+}
+
+/*
+ * Calculate a duration we will wait until we allow time to advance again
+ */
+uint64_t
+Fuzzyfox::PickDuration()
+{
+  // TODO: Bug 1484298 - use a real RNG
+  long int rval = rand();
+
+  // Avoid divide by zero errors and overflow errors
+  uint32_t duration = sFuzzyfoxClockGrain <= 0 ? 1 : sFuzzyfoxClockGrain;
+  duration = duration >= (UINT32_MAX / 2) ? (UINT32_MAX / 2) : duration;
+
+  // We want uniform distribution from 1->duration*2
+  // so that the mean is duration
+  return 1 + (rval % (duration * 2));
+}
+
+/*
+ * Update the TimeStamp's class value for the current (constant) time and dispatch
+ * the new (constant) timestamp so observers can register to receive it to update
+ * their own time code.
+ */
+void
+Fuzzyfox::UpdateClocks(uint64_t aNewTime, TimeStamp aNewTimeStamp)
+{
+  // newTime is the new canonical time for this scope!
+  #ifndef XP_WIN
+  LOG(Verbose, ("[Time] New time is %" PRIu64 " (compare to %" PRIu64 ") and timestamp is %" PRIu64 " (compare to %" PRIu64 ")\n",
+    aNewTime, ActualTime(), aNewTimeStamp.mValue.mTimeStamp, TimeStamp::NowUnfuzzed().mValue.mTimeStamp));
+  #else
+  LOG(Verbose, ("[Time] New time is %" PRIu64 " (compare to %" PRIu64 ") \n", aNewTime, ActualTime()));
+  #endif
+
+  // Fire notifications
+  if (MOZ_UNLIKELY(!mObs)) {
+    mObs = services::GetObserverService();
+    if (NS_WARN_IF(!mObs)) {
+      return;
+    }
+  }
+
+  // Event firings on occur on downticks and have no data
+  if (mTickType == eDowntick) {
+    mObs->NotifyObservers(nullptr, FUZZYFOX_FIREOUTBOUND_OBSERVER_TOPIC, nullptr);
+  }
+
+  if (!mTimeUpdateWrapper) {
+    mTimeUpdateWrapper = do_CreateInstance(NS_SUPPORTS_PRINT64_CONTRACTID);
+    if (NS_WARN_IF(!mTimeUpdateWrapper)) {
+      return;
+    }
+  }
+
+  mTimeUpdateWrapper->SetData(aNewTime);
+
+  // Clocks get the new official (frozen) time. This happens on all ticks
+  mObs->NotifyObservers(mTimeUpdateWrapper, FUZZYFOX_UPDATECLOCK_OBSERVER_TOPIC, nullptr);
+
+  // Update the timestamp's canonicaltime
+  TimeStamp::UpdateFuzzyTimeStamp(aNewTimeStamp);
+}
+
+uint64_t
+Fuzzyfox::GetClockGrain()
+{
+  return sFuzzyfoxClockGrain;
+}
+
+
+/*
+ * FloorToGrain accepts a timestamp in microsecond precision
+ * and returns it in microseconds, rounded down to the nearest
+ * ClockGrain value.
+ */
+uint64_t
+Fuzzyfox::FloorToGrain(uint64_t aValue)
+{
+  return aValue - (aValue % GetClockGrain());
+}
+
+
+/*
+ * FloorToGrain accepts a timestamp and returns it, rounded down
+ * to the nearest ClockGrain value.
+ */
+TimeStamp
+Fuzzyfox::FloorToGrain(TimeStamp aValue)
+{
+#ifdef XP_WIN
+  // grain is in us
+  uint64_t grain = GetClockGrain();
+  // GTC and QPS are stored in |mt| and need to be converted to
+  uint64_t GTC = mt2ms(aValue.mValue.mGTC) * 1000;
+  uint64_t QPC = mt2ms(aValue.mValue.mQPC) * 1000;
+
+  return TimeStamp(TimeStampValue(
+    ms2mt((GTC - (GTC % grain)) / 1000),
+    ms2mt((QPC - (QPC % grain)) / 1000),
+    aValue.mValue.mHasQPC, true));
+#else
+  return TimeStamp(TimeStampValue(true, US_TO_NS(FloorToGrain(NS_TO_US(aValue.mValue.mTimeStamp)))));
+#endif
+}
+
+/*
+ * CeilToGrain accepts a timestamp in microsecond precision
+ * and returns it in microseconds, rounded up to the nearest
+ * ClockGrain value.
+ */
+uint64_t
+Fuzzyfox::CeilToGrain(uint64_t aValue)
+{
+  return (aValue / GetClockGrain()) * GetClockGrain();
+}
+
+/*
+ * CeilToGrain accepts a timestamp and returns it, rounded up
+ * to the nearest ClockGrain value.
+ */
+TimeStamp
+Fuzzyfox::CeilToGrain(TimeStamp aValue)
+{
+#ifdef XP_WIN
+  // grain is in us
+  uint64_t grain = GetClockGrain();
+  // GTC and QPS are stored in |mt| and need to be converted
+  uint64_t GTC = mt2ms(aValue.mValue.mGTC) * 1000;
+  uint64_t QPC = mt2ms(aValue.mValue.mQPC) * 1000;
+
+  return TimeStamp(TimeStampValue(
+    ms2mt(((GTC / grain) * grain) / 1000),
+    ms2mt(((QPC / grain) * grain) / 1000),
+    aValue.mValue.mHasQPC, true));
+#else
+  return TimeStamp(TimeStampValue(true, US_TO_NS(CeilToGrain(NS_TO_US(aValue.mValue.mTimeStamp)))));
+#endif
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/toolkit/components/fuzzyfox/Fuzzyfox.h
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_Fuzzyfox_h
+#define mozilla_Fuzzyfox_h
+
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsISupportsPrimitives.h"
+#include "nsThreadUtils.h"
+#include "mozilla/TimeStamp.h"
+
+
+/*
+ * This topic publishes the new canonical time according to Fuzzyfox,
+ * in microseconds since the epoch. If code needs to know the current time,
+ * it should listen for this topic and keep track of the 'current' time,
+ * so as to respect Fuzzyfox and be in sync with the rest of the browser's
+ * timekeeping.
+ */
+#define FUZZYFOX_UPDATECLOCK_OBSERVER_TOPIC   "fuzzyfox-update-clocks"
+
+/*
+ * For Fuzzyfox's security guarentees to hold, the browser must not execute
+ * actions while it should be paused. We currently only pause the main thread,
+ * so actions that occur on other threads should be queued until the browser
+ * unpaused (and moreso than unpauses: until it reaches a downtick.)
+ * This topic indicates when any queued outbound events should be delivered.
+ * TODO: Bug 1484300 and 1484299 would apply this to other communication channels
+ */
+#define FUZZYFOX_FIREOUTBOUND_OBSERVER_TOPIC  "fuzzyfox-fire-outbound"
+
+namespace mozilla {
+
+
+/*
+ * Fuzzyfox is an implementation of the Fermata concept presented in
+ * Trusted Browsers for Uncertain Times.
+ *
+ * Web Browsers expose explicit (performance.now()) and implicit
+ * (WebVTT, Video Frames) timers that, when combined with algorithmic
+ * improvements such as edge thresholding, produce extremely high
+ * resolution clocks.
+ *
+ * High Resolution clocks can be used to time network accesses, browser
+ * cache reads, web page rendering, access to the CPU cache, and other
+ * operations - and the time these operations take to perform can yield
+ * detailed information about user information we want to keep private.
+ *
+ * Fuzzyfox limits the information disclosure by limiting an attacker's
+ * ability to create a high resolution clock. It does this by introducing
+ * a concept called 'fuzzy time' that degrades all clocks (explicit and
+ * implicit). This is done through a combination of holding time constant
+ * during program execution and pausing program execution.
+ *
+ * @InProceedings{KS16,
+ *   author = {David Kohlbrenner and Hovav Shacham},
+ *   title = {Trusted Browsers for Uncertain Times},
+ *   booktitle = {Proceedings of USENIX Security 2016},
+ *   pages = {463-80},
+ *   year = 2016,
+ *   editor = {Thorsten Holz and Stefan Savage},
+ *   month = aug,
+ *   organization = {USENIX}
+ * }
+ * https://www.usenix.org/system/files/conference/usenixsecurity16/sec16_paper_kohlbrenner.pdf
+ *
+ * Fuzzyfox is an adaptation of
+ *   W.-M. Hu, “Reducing timing channels with fuzzy time,” in
+ *   Proceedings of IEEE Security and Privacy (“Oakland”)
+ *   1991, T. F. Lunt and J. McLean, Eds. IEEE Computer
+ *   Society, May 1991, pp. 8–20.
+ */
+class Fuzzyfox final : public Runnable, public nsIObserver
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIOBSERVER
+
+  static void
+  Start();
+
+  NS_IMETHOD
+  Run() override;
+
+private:
+  Fuzzyfox();
+  ~Fuzzyfox();
+
+  uint64_t
+  ActualTime();
+
+  uint64_t
+  PickDuration();
+
+  void
+  UpdateClocks(uint64_t aNewTime, TimeStamp aNewTimeStamp);
+
+  uint64_t
+  GetClockGrain();
+
+  uint64_t
+  FloorToGrain(uint64_t aValue);
+
+  TimeStamp
+  FloorToGrain(TimeStamp aValue);
+
+  uint64_t
+  CeilToGrain(uint64_t aValue);
+
+  TimeStamp
+  CeilToGrain(TimeStamp aValue);
+
+  bool mSanityCheck;
+  uint64_t mStartTime;
+  uint32_t mDuration;
+
+  enum Tick {
+    eUptick,
+    eDowntick,
+  };
+
+  Tick mTickType;
+
+  nsCOMPtr<nsIObserverService> mObs = nullptr;
+  nsCOMPtr<nsISupportsPRInt64> mTimeUpdateWrapper = nullptr;
+
+  static Atomic<bool, Relaxed> sFuzzyfoxEnabledPrefMapped;
+  static Atomic<uint32_t, Relaxed> sFuzzyfoxClockGrain;
+};
+
+} // mozilla namespace
+
+#endif /* mozilla_Fuzzyfox_h */
new file mode 100644
--- /dev/null
+++ b/toolkit/components/fuzzyfox/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+SOURCES += [
+    'Fuzzyfox.cpp',
+]
+
+EXPORTS.mozilla += [
+    'Fuzzyfox.h'
+]
+
+FINAL_LIBRARY = 'xul'
--- a/toolkit/components/moz.build
+++ b/toolkit/components/moz.build
@@ -31,16 +31,17 @@ DIRS += [
     'crashes',
     'crashmonitor',
     'downloads',
     'enterprisepolicies',
     'extensions',
     'filewatcher',
     'finalizationwitness',
     'find',
+    'fuzzyfox',
     'jsoncpp/src/lib_json',
     'lz4',
     'mediasniffer',
     'microformats',
     'mozintl',
     'mozprotocol',
     'osfile',
     'parentalcontrols',