Opened 4 years ago
Last modified 16 months ago
#13018 new defect
Math routines are OS fingerprintable
Reported by: | mikeperry | Owned by: | tbb-team |
---|---|---|---|
Priority: | Medium | Milestone: | |
Component: | Applications/Tor Browser | Version: | |
Severity: | Normal | Keywords: | tbb-fingerprinting-os-version, ff31-esr |
Cc: | Actual Points: | ||
Parent ID: | Points: | ||
Reviewer: | Sponsor: |
Description
The Math class now exposes high-precision versions of several mathematical functions. If these are OS-specific, they may be fingerprintable.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math
OS-level fingerprinting probably is not a terribly high priority. The only situation where this is high priority is if different OS versions and library versions end up producing different results for these functions.
Child Tickets
Change History (26)
comment:1 Changed 4 years ago by
Keywords: | TorBrowserTeam201409 added |
---|
comment:2 Changed 4 years ago by
Keywords: | tbb-easy added |
---|
comment:3 Changed 4 years ago by
Keywords: | TorBrowserTeam201409Easy added; TorBrowserTeam201409 removed |
---|
comment:4 Changed 4 years ago by
Keywords: | TorBrowserTeam201410 added |
---|
comment:5 Changed 4 years ago by
Keywords: | TorBrowserTeam201410Easy added; TorBrowserTeam201409Easy removed |
---|
comment:6 Changed 4 years ago by
Keywords: | TorBrowserTeam201410 removed |
---|
comment:7 Changed 4 years ago by
I did not test all functions but just some using https://people.torproject.org/~gk/misc/MathHighPrecisionAPI.html. While we have differences between OSes we have it seems differences between 32bit and 64bit architectures as well. I tested on OS X 10.6.8, 32 bit Debian testing, 32 bit Ubuntu Precise, 64 bit Ubuntu Precise, Windows 7 64 bit and Windows 8 64 bit. Small differences between Linux/OS X /Windows aside, the 64bit Ubuntu Precise gives me:
asinh(1) = 0.881373587019543 acosh(1e300) = 691.4686750787736 atanh(0.5) = 0.5493061443340548 expm1(1) = 1.718281828459045 log1p(10) = 2.3978952727983707 sinh(1) = 1.1752011936438014 cosh(10) = 11013.232920103324 tanh(1) = 0.7615941559557649
While my 32bit systems give
asinh(1) = 0.8813735870195429 acosh(1e300) = 691.4686750787737 atanh(0.5) = 0.5493061443340549 expm1(1) = 1.718281828459045 log1p(10) = 2.3978952727983707 sinh(1) = 1.1752011936438014 cosh(10) = 11013.232920103328 tanh(1) = 0.7615941559557649
comment:8 Changed 4 years ago by
The Windows values are:
asinh(1) = 0.8813735870195429 acosh(1e300) = 691.4686750787737 atanh(0.5) = 0.5493061443340549 expm1(1) = 1.718281828459045 log1p(10) = 2.3978952727983707 sinh(1) = 1.1752011936438014 cosh(10) = 11013.232920103323 tanh(1) = 0.7615941559557649
And the OS X values are only in cosh(10) different, too. I wonder whether we could get bigger differences by picking better values for these functions (I almost bet this is the case).
comment:9 Changed 4 years ago by
Values computed using standard algebraic expression:
asinh(1) = 0.8813735870195429 acosh(1e300) = NaN atanh(0.5) = 0.5493061443340549 expm1(1) = 1.718281828459045 log1p(10) = 2.3978952727983707 sinh(1) = 1.1752011936438014 cosh(10) = 11013.232920103324 tanh(1) = 0.7615941559557649
Using code:
function asinh(x) { if (x === -Infinity) { return x; } else { return Math.log(x + Math.sqrt(x * x + 1)); } } function acosh(x) { return Math.log(x + Math.sqrt(x * x - 1)); } function atanh(x) { return Math.log((1+x)/(1-x)) / 2; } function cbrt(x) { var y = Math.pow(Math.abs(x), 1/3); return x < 0 ? -y : y; } function cosh(x) { var y = Math.exp(x); return (y + 1 / y) / 2; } function expm1(x) { return Math.exp(x) - 1; } function log1p(x) { return Math.log(1 + x); } function sinh(x){ var y = Math.exp(x); return (y - 1/y) / 2; } function tanh(x) { if(x === Infinity) { return 1; } else if(x === -Infinity) { return -1; } else { var y = Math.exp(2 * x); return (y - 1) / (y + 1); } }
comment:10 Changed 4 years ago by
Just to add to my last comment: Windows7/8 and Ubuntu Precise 32/64 run on the same computer without VM. The Debian and the OS X are running on two other machines.
comment:11 Changed 4 years ago by
Keywords: | tbb-fingerprinting-os added; tbb-fingerprinting removed |
---|---|
Summary: | Determine if high-precision Math routines are fingerprintable → High-precision Math routines are OS fingerprintable |
Hrmm.. Sounds like we may need to pick a library we like and include its versions of these functions rather than calling out to the OS.
I wonder why there are 32 vs 64bit differences here, though.. I guess the Linux versions probably use 'long' instead of something standard like int64_t... Bleh.
comment:12 follow-up: 13 Changed 4 years ago by
I guess the next question is to determine if this is any worse than just an OS+arch fingerprinting vector, so we can decide how to prioritize this versus other fingerprinting issues.
comment:13 Changed 4 years ago by
Replying to mikeperry:
I guess the next question is to determine if this is any worse than just an OS+arch fingerprinting vector, so we can decide how to prioritize this versus other fingerprinting issues.
We should ask SpiderMonkey folks for a best way to fingerprint users using the Math object. They might know some really good corner cases due to the algorithms they chose. https://bugzilla.mozilla.org/show_bug.cgi?id=892671 could be relevant here, too.
comment:14 Changed 4 years ago by
32/64 difference leaks without high-precision math routines and works for ff24-esr. Math.exp(x) output is different for x >= 5.
comment:15 Changed 4 years ago by
Summary: | High-precision Math routines are OS fingerprintable → Math routines are OS fingerprintable |
---|
comment:17 Changed 4 years ago by
<html> <head> <title>https://bugzilla.mozilla.org/show_bug.cgi?id=531915</title> </head> <body> <p>Detected platform for Tor Browser (based on ff24-esr):</p> <script> var check_tan = Math.tan(-1e300); if (check_tan == -1.4214488238747245) document.write("Linux 64"); else if (check_tan == 0.8831488831618285) document.write("Linux 32"); else if (check_tan == -4.987183803371025) document.write("Windows"); else document.write("Mac OS X"); </script> </body> </html>
comment:18 Changed 4 years ago by
Detected platform for Tor Browser
For Linux it depends run-time environment (libm system library), it's possible to detect some distros such way.
Windows depends compile-time environment (mingw).
comment:19 Changed 4 years ago by
More to puzzle. Output of testing sample linked with x86 libm (not reproducible for cross compiling):
Before FIX_FPU: tan(-1e300) = -1.421449, tan(strtod('-1e300')) = -4.802497 After FIX_FPU: tan(-1e300) = -1.421449, tan(strtod('-1e300')) = 0.883149
Code of sample:
#include <stdio.h> #include <stdlib.h> #include <math.h> inline void FIX_FPU() { short control; asm("fstcw %0" : "=m" (control) : ); control &= ~0x300; // Lower bits 8 and 9 (precision control). control |= 0x2f3; // Raise bits 0-5 (exception masks) and 9 (64-bit precision). asm("fldcw %0" : : "m" (control) ); } int main () { char *end; double x; x = strtod ("-1e300", &end); // eq "x=-1e300"; printf("Before FIX_FPU: tan(-1e300) = %f, tan(strtod('-1e300')) = %f\n", tan(-1e300), tan(x)); FIX_FPU(); printf("After FIX_FPU: tan(-1e300) = %f, tan(strtod('-1e300')) = %f\n", tan(-1e300), tan(x)); return 0; }
comment:20 Changed 4 years ago by
Type: | task → defect |
---|
comment:19 was
More to puzzle. Output of testing sample linked with x86 libm (not reproducible for cross compiling):
Before FIX_FPU: tan(-1e300) = -1.421449, tan(strtod('-1e300')) = -4.802497 After FIX_FPU: tan(-1e300) = -1.421449, tan(strtod('-1e300')) = 0.883149
Code of sample:
#include <stdio.h> #include <stdlib.h> #include <math.h> inline void FIX_FPU() { short control; asm("fstcw %0" : "=m" (control) : ); control &= ~0x300; // Lower bits 8 and 9 (precision control). control |= 0x2f3; // Raise bits 0-5 (exception masks) and 9 (64-bit precision). asm("fldcw %0" : : "m" (control) ); } int main () { char *end; double x; x = strtod ("-1e300", &end); // eq "x=-1e300"; printf("Before FIX_FPU: tan(-1e300) = %f, tan(strtod('-1e300')) = %f\n", tan(-1e300), tan(x)); FIX_FPU(); printf("After FIX_FPU: tan(-1e300) = %f, tan(strtod('-1e300')) = %f\n", tan(-1e300), tan(x)); return 0; }
comment:21 Changed 4 years ago by
Keywords: | TorBrowserTeam201410Easy removed |
---|
comment:22 Changed 3 years ago by
Keywords: | tbb-fingerprinting-os-version added; tbb-fingerprinting-os removed |
---|
We may want to ship libm.so in our Linux bundles for this, at least to push back against the version fingerprinting property.
comment:23 Changed 2 years ago by
Severity: | → Normal |
---|
Here's a very interesting overview of sources of floating-point inconsistencies:
https://randomascii.wordpress.com/2013/07/16/floating-point-determinism/
comment:24 Changed 2 years ago by
This may also be the cause of the fingerprintability of the WebAudio stuff in #13017 (or at least part of that fingerprintability). See https://trac.torproject.org/projects/tor/ticket/13017#comment:27 for a useful test.
comment:25 Changed 2 years ago by
Keywords: | tbb-easy removed |
---|
comment:26 Changed 16 months ago by
A few notes:
- A quick check with the browser console gives me the impression that simple JS math expressions are evaluated with 64 bit intermediaries (as opposed to 80 bit). I am uncertain about the JS JIT behavior.
(1.0 + Number.EPSILON * 0.5) + Number.EPSILON * 0.5
)
- Assuming calls are made to libm (or equivalent) blindly, the results on each system are library version and implementation dependent. A particularly egregious example would be the output of
double sin(double x);
being flat out wrong for glibc < 2.19 for certain values. MS's VC++ runtime is less wrong for a different set of certain values, but is still wrong. This probably applies to most transcendental functions.
- Even if we fix the JS that calls into libm, higher level apis that just happen to do math are not guaranteed to give the correct results, depending on how the native code it's called into is written or built. If we can assume that x87 is never used at all, then we'd still need to check for things like
rsqrtss
.
This may be easy for someone who has a bunch of different OSes and writes some test JS to print out values from these functions. Well, easy to test for differences anyway.