Bug 1443943 Move LRUCache Initialization to startup rather than lazily r?baku
Before, we would initialize LRUCache on the first instance of
calling the Timer Precision Reduction functions. We would both
allocate and initialize it, and call ClearOnShutdown.
ClearOnShutdown can only be called on the Main Thread, but it
just so happened that we always did that, so there was no
problem. Now that we are not calling precision reduction for
system callers, we were initializing on a non-main-thread and
we need to avoid that.
In the future, we could reduce memory use IF we are not using
the timer precision reduction functions by figuring out how
to initialize this lazily but still on the main thread. For
now, because we are using the timer precision reduction
functions, doing so would not save us any memory.
MozReview-Commit-ID: GqkfouVSeG8
old mode 100644
new mode 100755
--- a/toolkit/components/resistfingerprinting/nsRFPService.cpp
+++ b/toolkit/components/resistfingerprinting/nsRFPService.cpp
@@ -139,24 +139,26 @@ nsRFPService::IsTimerPrecisionReductionE
* The below is a simple time-based Least Recently Used cache used to store the
* result of a cryptographic hash function. It has LRU_CACHE_SIZE slots, and will
* be used from multiple threads. It is thread-safe.
*/
#define LRU_CACHE_SIZE (45)
#define HASH_DIGEST_SIZE_BITS (256)
#define HASH_DIGEST_SIZE_BYTES (HASH_DIGEST_SIZE_BITS / 8)
-class LRUCache
+class LRUCache final
{
public:
LRUCache()
: mLock("mozilla.resistFingerprinting.LRUCache") {
this->cache.SetLength(LRU_CACHE_SIZE);
}
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LRUCache)
+
nsCString Get(long long aKey) {
for (auto & cacheEntry : this->cache) {
// Read optimistically befor locking
if (cacheEntry.key == aKey) {
MutexAutoLock lock(mLock);
// Double check after we have a lock
if (MOZ_UNLIKELY(cacheEntry.key != aKey)) {
@@ -197,16 +199,18 @@ public:
lowestKey->key = aKey;
lowestKey->data = aValue;
lowestKey->accessTime = PR_Now();
MOZ_LOG(gResistFingerprintingLog, LogLevel::Verbose, ("LRU Cache STORE with %lli", aKey));
}
private:
+ ~LRUCache() = default;
+
struct CacheEntry {
Atomic<long long, Relaxed> key;
PRTime accessTime = 0;
nsCString data;
CacheEntry() {
this->key = 0xFFFFFFFFFFFFFFFF;
this->accessTime = 0;
@@ -219,17 +223,17 @@ private:
}
};
AutoTArray<CacheEntry, LRU_CACHE_SIZE> cache;
mozilla::Mutex mLock;
};
// We make a single LRUCache
-static StaticAutoPtr<LRUCache> sCache;
+static StaticRefPtr<LRUCache> sCache;
/**
* The purpose of this function is to deterministicly generate a random midpoint
* between a lower clamped value and an upper clamped value. Assuming a clamping
* resolution of 100, here is an example:
*
* |---------------------------------------|--------------------------|
* lower clamped value (e.g. 300) | upper clamped value (400)
@@ -288,26 +292,28 @@ nsRFPService::RandomMidpoint(long long a
long long* aMidpointOut,
uint8_t * aSecretSeed /* = nullptr */)
{
nsresult rv;
const int kSeedSize = 16;
const int kClampTimesPerDigest = HASH_DIGEST_SIZE_BITS / 32;
static uint8_t * sSecretMidpointSeed = nullptr;
- if(MOZ_UNLIKELY(!sCache)) {
- StaticMutexAutoLock lock(sLock);
- if(MOZ_LIKELY(!sCache)) {
- sCache = new LRUCache();
- ClearOnShutdown(&sCache);
- }
+ if(MOZ_UNLIKELY(!aMidpointOut)) {
+ return NS_ERROR_INVALID_ARG;
}
- if(MOZ_UNLIKELY(!aMidpointOut)) {
- return NS_ERROR_INVALID_ARG;
+ RefPtr<LRUCache> cache;
+ {
+ StaticMutexAutoLock lock(sLock);
+ cache = sCache;
+ }
+
+ if(!cache) {
+ return NS_ERROR_FAILURE;
}
/*
* Below, we will call a cryptographic hash function. That's expensive. We look for ways to
* make it more efficient.
*
* We only need as much output from the hash function as the maximum resolution we will
* ever support, because we will reduce the output modulo that value. The maximum resolution
@@ -323,17 +329,17 @@ nsRFPService::RandomMidpoint(long long a
* kClampTimesPerDigest (just like we reduced the real time value to aClampedTime!)
*
* Then we hash _that_ value (assuming it's not in the cache) and index into the digest result
* the appropriate bit offset.
*/
long long reducedResolution = aResolutionUSec * kClampTimesPerDigest;
long long extraClampedTime = (aClampedTimeUSec / reducedResolution) * reducedResolution;
- nsCString hashResult = sCache->Get(extraClampedTime);
+ nsCString hashResult = cache->Get(extraClampedTime);
if(hashResult.Length() != HASH_DIGEST_SIZE_BYTES) { // Cache Miss =(
// If someone has pased in the testing-only parameter, replace our seed with it
if (aSecretSeed != nullptr) {
StaticMutexAutoLock lock(sLock);
if (sSecretMidpointSeed) {
delete[] sSecretMidpointSeed;
}
@@ -390,17 +396,17 @@ nsRFPService::RandomMidpoint(long long a
rv = hasher->Update((const uint8_t *)&extraClampedTime, sizeof(extraClampedTime));
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCStringN<HASH_DIGEST_SIZE_BYTES> derivedSecret;
rv = hasher->Finish(false, derivedSecret);
NS_ENSURE_SUCCESS(rv, rv);
// Finally, store it in the cache
- sCache->Store(extraClampedTime, derivedSecret);
+ cache->Store(extraClampedTime, derivedSecret);
hashResult = derivedSecret;
}
// Offset the appropriate index into the hash output, and then turn it into a random midpoint
// between 0 and aResolutionUSec. Sometimes out input time is negative, we ride the negative
// out to the end until we start doing pointer math. (We also triple check we're in bounds.)
int byteOffset = abs(((aClampedTimeUSec - extraClampedTime) / aResolutionUSec) * 4);
if (MOZ_UNLIKELY(byteOffset > (HASH_DIGEST_SIZE_BYTES - 4))) {
@@ -690,16 +696,22 @@ nsRFPService::Init()
const char* tzValue = PR_GetEnv("TZ");
if (tzValue) {
mInitialTZValue = nsCString(tzValue);
}
// Call Update here to cache the values of the prefs and set the timezone.
UpdateRFPPref();
+ // Create the LRU Cache when we initialize, to avoid accidently trying to
+ // create it (and call ClearOnShutdown) on a non-main-thread
+ if(!sCache) {
+ sCache = new LRUCache();
+ }
+
return rv;
}
// This function updates only timing-related fingerprinting items
void
nsRFPService::UpdateTimers() {
MOZ_ASSERT(NS_IsMainThread());
@@ -759,16 +771,21 @@ nsRFPService::UpdateRFPPref()
void
nsRFPService::StartShutdown()
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ StaticMutexAutoLock lock(sLock);
+ {
+ sCache = nullptr;
+ }
+
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);