tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 6553440ace3ab9b90455eda769b4df80d8188855
parent f81e4cc78e3e5610d9780384ea0d769dd41ed9de
Author: Tom Ritter <tom@mozilla.com>
Date:   Tue,  9 Dec 2025 16:35:01 +0000

Bug 1873716: Add logging for Font Fingerprinting r=timhuang

Differential Revision: https://phabricator.services.mozilla.com/D275348

Diffstat:
Mdom/canvas/OffscreenCanvas.cpp | 4++++
Mdom/workers/WorkerPrivate.cpp | 2++
Mgfx/src/nsFontCache.cpp | 52++++++++++++++++++++++++++++++++++++++++------------
Mgfx/src/nsFontCache.h | 4++--
Mlayout/base/nsPresContext.cpp | 3+++
5 files changed, 51 insertions(+), 14 deletions(-)

diff --git a/dom/canvas/OffscreenCanvas.cpp b/dom/canvas/OffscreenCanvas.cpp @@ -16,6 +16,7 @@ #include "WebGLChild.h" #include "mozilla/Atomics.h" #include "mozilla/CheckedInt.h" +#include "mozilla/Logging.h" #include "mozilla/dom/BlobImpl.h" #include "mozilla/dom/DocumentInlines.h" #include "mozilla/dom/OffscreenCanvasBinding.h" @@ -33,6 +34,8 @@ namespace mozilla::dom { +static mozilla::LazyLogModule gFingerprinterDetection("FingerprinterDetection"); + OffscreenCanvasCloneData::OffscreenCanvasCloneData( OffscreenCanvasDisplayHelper* aDisplay, uint32_t aWidth, uint32_t aHeight, layers::LayersBackend aCompositorBackend, bool aNeutered, bool aIsWriteOnly, @@ -641,6 +644,7 @@ FontVisibility OffscreenCanvas::GetFontVisibility() const { } void OffscreenCanvas::ReportBlockedFontFamily(const nsCString& aMsg) const { + MOZ_LOG(gFingerprinterDetection, mozilla::LogLevel::Info, ("%s", aMsg.get())); if (Maybe<uint64_t> windowID = GetWindowID()) { nsContentUtils::ReportToConsoleByWindowID(NS_ConvertUTF8toUTF16(aMsg), nsIScriptError::warningFlag, diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp @@ -137,6 +137,7 @@ static mozilla::LazyLogModule sWorkerPrivateLog("WorkerPrivate"); static mozilla::LazyLogModule sWorkerTimeoutsLog("WorkerTimeouts"); +static mozilla::LazyLogModule gFingerprinterDetection("FingerprinterDetection"); mozilla::LogModule* WorkerLog() { return sWorkerPrivateLog; } @@ -6773,6 +6774,7 @@ FontVisibility WorkerPrivate::GetFontVisibility() const { } void WorkerPrivate::ReportBlockedFontFamily(const nsCString& aMsg) const { + MOZ_LOG(gFingerprinterDetection, mozilla::LogLevel::Info, ("%s", aMsg.get())); nsContentUtils::ReportToConsoleNonLocalized(NS_ConvertUTF8toUTF16(aMsg), nsIScriptError::warningFlag, "Security"_ns, GetDocument()); diff --git a/gfx/src/nsFontCache.cpp b/gfx/src/nsFontCache.cpp @@ -17,6 +17,8 @@ using mozilla::services::GetObserverService; NS_IMPL_ISUPPORTS(nsFontCache, nsIObserver) +static mozilla::LazyLogModule gFingerprinterDetection("FingerprinterDetection"); + // The Init and Destroy methods are necessary because it's not // safe to call AddObserver from a constructor or RemoveObserver // from a destructor. That should be fixed. @@ -117,7 +119,14 @@ void nsFontCache::DetectFontFingerprinting(const nsFont& aFont) { // number of cache misses in a short amount of time, which indicates the // usage of an unreasonable amount of different fonts by the web page. - if (mReportedProbableFingerprinting || aFont.family.families.list.IsEmpty()) { + if (aFont.family.families.list.IsEmpty()) { + return; + } + + if (!MOZ_LOG_TEST(gFingerprinterDetection, mozilla::LogLevel::Info) && + mReportedProbableFingerprinting) { + // We've already reported fingerprinting for this document, and logging + // isn't enabled, so skip the rest of the detection logic. return; } @@ -143,20 +152,39 @@ void nsFontCache::DetectFontFingerprinting(const nsFont& aFont) { uint16_t fontsMissedRecently = 0; bool clearMissedFonts = false; - for (auto iter = missedFonts->Iter(); !iter.Done(); iter.Next()) { - if (now - kFingerprintingLastNSec <= iter.Data()) { - if (++fontsMissedRecently > kFingerprintingCacheMissThreshold) { - mContext->Document()->RecordFontFingerprinting(); - mReportedProbableFingerprinting = true; - clearMissedFonts = true; - break; + if (!mReportedProbableFingerprinting) { + for (auto iter = missedFonts->Iter(); !iter.Done(); iter.Next()) { + if (now - kFingerprintingLastNSec <= iter.Data()) { + if (++fontsMissedRecently > kFingerprintingCacheMissThreshold) { + mContext->Document()->RecordFontFingerprinting(); + mReportedProbableFingerprinting = true; + clearMissedFonts = true; + break; + } + } else { + // Remove the old entries from missed cache list. + iter.Remove(); + } + } + if (mReportedProbableFingerprinting) { + // We detected a font fingerprinter, so print out all the fonts they + // queries and missed + for (auto iter = missedFonts->Iter(); !iter.Done(); iter.Next()) { + MOZ_LOG( + gFingerprinterDetection, mozilla::LogLevel::Info, + ("Font Fingerprinting Tripped | Document %p | Font Family | %s", + mContext->Document(), NS_ConvertUTF16toUTF8(iter.Key()).get())); } - } else { - // Remove the old entries from missed cache list. - iter.Remove(); } + } else { + // I've already reported the font fingerprinting, but I want to report each + // additional font + MOZ_LOG(gFingerprinterDetection, mozilla::LogLevel::Info, + ("Font Fingerprinting Tripped | Document %p | Font Family | %s", + mContext->Document(), NS_ConvertUTF16toUTF8(key).get())); } - if (clearMissedFonts) { + if (!MOZ_LOG_TEST(gFingerprinterDetection, mozilla::LogLevel::Info) && + clearMissedFonts) { missedFonts->Clear(); } } diff --git a/gfx/src/nsFontCache.h b/gfx/src/nsFontCache.h @@ -54,11 +54,11 @@ class nsFontCache final : public nsIObserver { // Number of cache misses before we assume that a font fingerprinting attempt // is being made. - static constexpr int32_t kFingerprintingCacheMissThreshold = 20; + static constexpr int32_t kFingerprintingCacheMissThreshold = 10; // We assume that fingerprinters will lookup a large number of fonts in a // short amount of time. static constexpr PRTime kFingerprintingLastNSec = - PRTime(PR_USEC_PER_SEC) * 3; // 3 seconds + PRTime(PR_USEC_PER_SEC) * 6; // 6 seconds static_assert(kFingerprintingCacheMissThreshold < kMaxCacheEntries); diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp @@ -113,6 +113,8 @@ using namespace mozilla::dom; using namespace mozilla::gfx; using namespace mozilla::layers; +static LazyLogModule gFingerprinterDetection("FingerprinterDetection"); + /** * Layer UserData for ContainerLayers that want to be notified * of local invalidations of them and their descendant layers. @@ -3081,6 +3083,7 @@ bool nsPresContext::ShouldResistFingerprinting(RFPTarget aTarget) const { } void nsPresContext::ReportBlockedFontFamily(const nsCString& aMsg) const { + MOZ_LOG(gFingerprinterDetection, LogLevel::Info, ("%s", aMsg.get())); nsContentUtils::ReportToConsoleNonLocalized(NS_ConvertUTF8toUTF16(aMsg), nsIScriptError::warningFlag, "Security"_ns, Document());