Bug 1781277: Implement DiskStorageLimit RFP target. r=tjr a=pascalc

Differential Revision: https://phabricator.services.mozilla.com/D256111
This commit is contained in:
Fatih Kilic
2025-07-09 09:12:44 +00:00
committed by pchevrel@mozilla.com
parent 3392ea7ae8
commit 714d506ada
7 changed files with 106 additions and 4 deletions

View File

@@ -1298,6 +1298,12 @@ void GetJarPrefix(bool aInIsolatedMozBrowser, nsACString& aJarPrefix) {
// This method computes and returns our best guess for the temporary storage
// limit (in bytes), based on disk capacity.
Result<uint64_t, nsresult> GetTemporaryStorageLimit(nsIFile& aStorageDir) {
if (nsContentUtils::ShouldResistFingerprinting(
"The storage limit is set only once and not webpage specific.",
RFPTarget::DiskStorageLimit)) {
return nsRFPService::GetSpoofedStorageLimit();
}
// The fixed limit pref can be used to override temporary storage limit
// calculation.
if (StaticPrefs::dom_quotaManager_temporaryStorage_fixedLimit() >= 0) {
@@ -7171,17 +7177,21 @@ void QuotaManager::SetThumbnailPrivateIdentityId(
}
}
uint64_t QuotaManager::GetGroupLimit() const {
/* static */
uint64_t QuotaManager::GetGroupLimitForLimit(uint64_t aLimit) {
// To avoid one group evicting all the rest, limit the amount any one group
// can use to 20% resp. a fifth. To prevent individual sites from using
// exorbitant amounts of storage where there is a lot of free space, cap the
// group limit to 10GB.
const auto x = std::min<uint64_t>(mTemporaryStorageLimit / 5, 10 GB);
const auto x = std::min<uint64_t>(aLimit / 5, 10 GB);
// In low-storage situations, make an exception (while not exceeding the total
// storage limit).
return std::min<uint64_t>(mTemporaryStorageLimit,
std::max<uint64_t>(x, 10 MB));
return std::min<uint64_t>(aLimit, std::max<uint64_t>(x, 10 MB));
}
uint64_t QuotaManager::GetGroupLimit() const {
return GetGroupLimitForLimit(mTemporaryStorageLimit);
}
std::pair<uint64_t, uint64_t> QuotaManager::GetUsageAndLimitForEstimate(

View File

@@ -665,6 +665,7 @@ class QuotaManager final : public BackgroundThreadObject {
void SetThumbnailPrivateIdentityId(uint32_t aThumbnailPrivateIdentityId);
uint64_t GetGroupLimit() const;
static uint64_t GetGroupLimitForLimit(uint64_t aLimit);
std::pair<uint64_t, uint64_t> GetUsageAndLimitForEstimate(
const OriginMetadata& aOriginMetadata);

View File

@@ -0,0 +1,70 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
const { AppConstants } = ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
async function testSteps() {
const principal = getPrincipal("http://example.com");
const GiB = 1024 * 1024 * 1024;
// Set the limit to some random value that is less than 50 GiB.
const globalLimitBytes = 1 * GiB;
const globalLimitKib = globalLimitBytes / 1024;
setGlobalLimit(globalLimitKib);
let request = init();
await requestFinished(request);
request = initTemporaryStorage();
await requestFinished(request);
request = estimateOrigin(principal);
await requestFinished(request);
const perGroupPercentage = 0.2;
const expectedGroupLimitBytes = Math.floor(
globalLimitBytes * perGroupPercentage
);
is(expectedGroupLimitBytes, request.result.limit);
// Verify the RFP override is applied.
request = reset();
await requestFinished(request);
let spoofedLimitBytes = 50 * GiB;
if (AppConstants.platform == "android") {
spoofedLimitBytes = 32 * GiB;
}
Services.prefs.setBoolPref("privacy.resistFingerprinting", true);
request = init();
await requestFinished(request);
request = initTemporaryStorage();
await requestFinished(request);
request = estimateOrigin(principal);
await requestFinished(request);
const expectedSpoofedGroupLimitBytes = Math.floor(
spoofedLimitBytes * perGroupPercentage
);
is(
expectedSpoofedGroupLimitBytes,
request.result.limit,
"RFP limit should be applied"
);
Services.prefs.clearUserPref("privacy.resistFingerprinting");
resetGlobalLimit();
request = reset();
await requestFinished(request);
}

View File

@@ -162,6 +162,8 @@ skip-if = ["inc_origin_init"]
# a conditioned profile in the first place.
run-if = ["!condprof"]
["test_temporaryStorageRFP.js"]
["test_unaccessedOrigins.js"]
["test_unknownFiles.js"]

View File

@@ -99,6 +99,8 @@ ITEM_VALUE(WebGPUIsFallbackAdapter, 65)
ITEM_VALUE(WebGPUSubgroupSizes, 66)
ITEM_VALUE(JSLocalePrompt, 67)
ITEM_VALUE(ScreenAvailToResolution, 68)
// ITEM_VALUE(UseHardcodedFontSubstitutes, 69) // Bug 1845105 isn't uplifted to ESR yet.
ITEM_VALUE(DiskStorageLimit, 70)
// !!! Don't forget to update kDefaultFingerprintingProtections in nsRFPService.cpp

View File

@@ -52,6 +52,7 @@
#include "mozilla/dom/KeyboardEventBinding.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/dom/MediaDeviceInfoBinding.h"
#include "mozilla/dom/quota/QuotaManager.h"
#include "mozilla/fallible.h"
#include "mozilla/XorShift128PlusRNG.h"
#include "mozilla/dom/CanvasUtils.h"
@@ -2677,3 +2678,17 @@ CSSIntRect nsRFPService::GetSpoofedScreenAvailSize(const nsRect& aRect,
return CSSIntRect::FromAppUnitsRounded(
nsRect{0, 0, aRect.width, aRect.height - spoofedHeightOffset});
}
/* static */
uint64_t nsRFPService::GetSpoofedStorageLimit() {
uint64_t gib = 1024ULL * 1024ULL * 1024ULL; // 1 GiB
#ifdef ANDROID
uint64_t limit = 32ULL * gib; // 32 GiB
#else
uint64_t limit = 50ULL * gib; // 50 GiB
#endif
MOZ_ASSERT(limit / 5 ==
dom::quota::QuotaManager::GetGroupLimitForLimit(limit));
return limit;
}

View File

@@ -414,6 +414,8 @@ class nsRFPService final : public nsIObserver, public nsIRFPService {
static CSSIntRect GetSpoofedScreenAvailSize(const nsRect& aRect, float aScale,
bool aIsFullscreen);
static uint64_t GetSpoofedStorageLimit();
private:
nsresult Init();