tor-browser

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

commit a42f6c233c879dee36aae58067d0bda7e08eb9d5
parent 1c68c48f6d838d901f45c2af43db5fbadaa8e7be
Author: Tom Ritter <tom@mozilla.com>
Date:   Mon, 29 Dec 2025 20:29:00 +0000

Bug 2005866: Collect the FPU Control State r=timhuang

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

Diffstat:
Mtoolkit/components/resistfingerprinting/metrics.yaml | 24++++++++++++++++++++++++
Mtoolkit/components/resistfingerprinting/nsUserCharacteristics.cpp | 127++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mtoolkit/components/resistfingerprinting/tests/gtest/test_usercharping.cpp | 8++++++++
3 files changed, 158 insertions(+), 1 deletion(-)

diff --git a/toolkit/components/resistfingerprinting/metrics.yaml b/toolkit/components/resistfingerprinting/metrics.yaml @@ -4009,6 +4009,30 @@ characteristics: data_sensitivity: - technical + fpu_control_state: + type: string + description: > + The current FPU (Floating-Point Unit) rounding mode and precision settings. + Format depends on architecture: + - x86/x86-64: "std:X;x87:Y;sse:Z;prec:P" where X,Y,Z are rounding modes (0-3) + and P is precision (single/double/extended) + - ARM: "std:X;arm:Y" where X,Y are rounding modes (0-3) + Rounding modes: 0=nearest, 1=down/negative, 2=up/positive, 3=toward-zero + This helps identify unusual FPU configurations that may cause math variations. + lifetime: application + send_in_pings: + - user-characteristics + notification_emails: + - tom@mozilla.com + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879151 + - https://bugzilla.mozilla.org/show_bug.cgi?id=2005866 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=2005866 + expires: never + data_sensitivity: + - technical + mathml1: type: string description: > diff --git a/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp b/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp @@ -72,6 +72,13 @@ # include "mozilla/widget/GSettings.h" #endif +// For FPU control state detection +#include <cfenv> +#if defined(_MSC_VER) +# include <float.h> +# include <intrin.h> +#endif + using namespace mozilla; static LazyLogModule gUserCharacteristicsLog("UserCharacteristics"); @@ -701,8 +708,126 @@ void PopulateProcessorCount() { glean::characteristics::processor_count.Set(processorCount); } +/** + * Gets the complete FPU state including rounding mode and precision. + * Returns a string formatted as: + * - x86/x86-64: "std:X;x87:Y;sse:Z;prec:P" + * - ARM: "std:X;arm:Y" + * Where X,Y,Z are rounding mode values (0-3) and P is precision. + */ +static nsCString GetFPUControlState() { + nsCString result; + + // Get standard C rounding mode (portable) + int std_mode = std::fegetround(); + const char* mode_str = "unknown"; + switch (std_mode) { + case FE_TONEAREST: + mode_str = "0"; + break; + case FE_DOWNWARD: + mode_str = "1"; + break; + case FE_UPWARD: + mode_str = "2"; + break; + case FE_TOWARDZERO: + mode_str = "3"; + break; + } + result.AppendLiteral("std:"); + result.Append(mode_str); + +#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || \ + defined(_M_IX86) + // x86/x86-64: Read both x87 and SSE control registers + + // Read x87 control word + uint16_t x87_cw = 0; +# ifdef _MSC_VER + x87_cw = static_cast<uint16_t>(_control87(0, 0)); +# else + __asm__ __volatile__("fstcw %0" : "=m"(x87_cw)); +# endif + + // Extract rounding mode (bits 10-11) + int x87_round = (x87_cw >> 10) & 0x3; + result.AppendLiteral(";x87:"); + result.AppendInt(x87_round); + + // Extract precision control (bits 8-9) + int precision = (x87_cw >> 8) & 0x3; + const char* prec_str = "unknown"; + switch (precision) { + case 0: + prec_str = "single"; + break; + case 1: + prec_str = "reserved"; + break; + case 2: + prec_str = "double"; + break; + case 3: + prec_str = "extended"; + break; + } + + // Read SSE MXCSR register (64-bit only, or 32-bit with SSE support) +# if defined(__x86_64__) || defined(_M_X64) || defined(__SSE__) + uint32_t mxcsr = 0; +# ifdef _MSC_VER + mxcsr = _mm_getcsr(); +# else + __asm__ __volatile__("stmxcsr %0" : "=m"(mxcsr)); +# endif + + // Extract rounding mode (bits 13-14) + int sse_round = (mxcsr >> 13) & 0x3; + result.AppendLiteral(";sse:"); + result.AppendInt(sse_round); +# else + // 32-bit without SSE + result.AppendLiteral(";sse:na"); +# endif + + result.AppendLiteral(";prec:"); + result.Append(prec_str); + +#elif defined(__aarch64__) + // ARM64: Read FPCR register + uint64_t fpcr = 0; + __asm__ __volatile__("mrs %0, fpcr" : "=r"(fpcr)); + + // Extract rounding mode (bits 22-23) + int arm_round = (fpcr >> 22) & 0x3; + result.AppendLiteral(";arm:"); + result.AppendInt(arm_round); + +#elif defined(__arm__) + // ARM32: Read FPSCR register + uint32_t fpscr = 0; + __asm__ __volatile__("vmrs %0, fpscr" : "=r"(fpscr)); + + // Extract rounding mode (bits 22-23) + int arm_round = (fpscr >> 22) & 0x3; + result.AppendLiteral(";arm:"); + result.AppendInt(arm_round); + +#else + // Other architectures: only report standard mode + result.AppendLiteral(";arch:other"); +#endif + + return result; +} + void PopulateMisc(bool worksInGtest) { if (worksInGtest) { + // Collect FPU control state + nsCString fpuState = GetFPUControlState(); + glean::characteristics::fpu_control_state.Set(fpuState); + glean::characteristics::max_touch_points.Set(testing::MaxTouchPoints()); nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service(); if (gfxInfo) { @@ -801,7 +926,7 @@ const RefPtr<PopulatePromise>& TimoutPromise( // metric is set, this variable should be incremented. It'll be a lot. It's // okay. We're going to need it to know (including during development) what is // the source of the data we are looking at. -const int kSubmissionSchema = 29; +const int kSubmissionSchema = 30; const auto* const kUUIDPref = "toolkit.telemetry.user_characteristics_ping.uuid"; diff --git a/toolkit/components/resistfingerprinting/tests/gtest/test_usercharping.cpp b/toolkit/components/resistfingerprinting/tests/gtest/test_usercharping.cpp @@ -60,6 +60,14 @@ TEST(ResistFingerprinting, UserCharacteristics_Complex) mozilla::glean::characteristics::max_touch_points.TestGetValue() .unwrap() .ref()); + + // Test FPU control state metric to ensure code runs and doesn't crash + auto fpuState = + mozilla::glean::characteristics::fpu_control_state.TestGetValue() + .unwrap() + .value(); + ASSERT_STRNE("", fpuState.get()); + ASSERT_TRUE(strstr(fpuState.get(), "std:") != nullptr); }, []() { nsUserCharacteristics::SubmitPing(); })); }