Web Security & Privacy

Math.tanh OS Fingerprinting After Chromium 148: The Floating-Point Trap

Math.tanh OS Fingerprinting After Chromium 148: The Floating-Point Trap

You can get a lot of browser “identity” from the usual suspects: canvas, WebGL, fonts, audio. Those are the classics.

But sometimes the most interesting fingerprint is hiding in plain sight, inside a number that looks too innocent to matter.

Open DevTools on your machine and run:

Math.tanh(0.8)

On different operating systems, you may notice the result is the same up to the last few bits—and those tiny discrepancies can be enough for an OS-level detector.

Can one call to Math.tanh really tell you what OS is running? In Chromium versions affected by a V8 math implementation change, the answer is “surprisingly, yes”.

The last bits are where the story lives

Let’s name the moving parts.

Math.tanh is a real transcendental function

Math.tanh(x) computes the hyperbolic tangent of x—a standard math function used in statistics, physics, and machine learning. In JavaScript it’s defined as a static method on Math. (developer.mozilla.org)

When you evaluate something like 0.8, the browser has to:
1. represent 0.8 as a floating-point number,
2. call a hyperbolic tangent implementation,
3. return a final floating-point result.

Floating-point numbers aren’t infinitely precise

JavaScript numbers are IEEE 754 binary64 doubles: a fixed-size format that stores a sign, exponent, and fraction. IEEE 754 standardizes basic arithmetic very carefully, but it does not force a single, universally identical behavior for every transcendental function (sin, tanh, exp, etc.). In other words, two correct implementations can still disagree in the final representation. (eecg.toronto.edu)

A detector doesn’t care that both outputs are “about the same”. It cares about the exact returned double.

Why “almost the same” becomes “different”

Floating-point outputs differ in units of least precision. A common term here is ULP, meaning “unit in the last place” (roughly: the spacing between neighboring representable floats near the value). (developer.apple.com)

If two implementations differ by 1 ULP, that’s a microscopic difference mathematically—yet it’s a crisp, machine-checkable difference for fingerprinting.

The hidden ingredient: libm (the OS math library)

The key background idea is that browsers don’t implement every transcendental function entirely from scratch.

Many platforms provide a system math library, often referred to as libm (short for “library for math”). This library typically powers std::tanh and friends in C/C++.

Meanwhile, V8 (the JavaScript engine inside Chromium) has historically shipped its own math code for parts of JavaScript’s Math.*.

So depending on version, platform, and function, you can end up with:
- bundled math code (more deterministic across OSes), or
- host math code via libm (can vary across OS vendors and even across CPU/library variants).

That split personality is the fingerprinting opportunity.

What changed in Chromium 148 era

The core technical change behind the “Math.tanh became fingerprintable” claim is straightforward: V8 stopped using its custom IEEE754 tanh implementation and began delegating to the C++ standard library’s std::tanh.

In the V8 source, the change is literally described as: “Replace custom tanh with std::tanh”, where the old custom implementation was a fdlibm port, and the new approach “delegate[s] to std::tanh” with the stated direction of using libm “where possible”. (chromium.googlesource.com)

Why does that matter?

Because std::tanh is not a single universal algorithm. It’s an entry point whose actual implementation typically lives inside the host’s math libraries.

So now, two machines—say Linux and macOS—can legitimately produce different last bits even though both are “doing the same math”.

This is the real trick: the browser API surface (Math.tanh) is standardized, but the low-level math plumbing underneath can still differ.

“Why not just spoof it?” — because math isn’t one thing

A tempting idea is: “If browsers differ, then spoof the function output to match the OS you want to imitate.”

That fails in a subtle way: browsers aren’t always consistent about which math implementation path a given function uses.

Here are the failure modes that show up in practice.

1) Some Math.* stay bundled, others route to host

V8 doesn’t treat every math function equally. Some functions are implemented via bundled routines; others may call into the platform.

Once only one function (like tanh) starts reflecting host libm behavior, it creates an inconsistency: everything else remains OS-invariant while tanh suddenly isn’t.

A detector doesn’t need a thousand probes—one function that “breaks the pattern” is enough.

2) Even “equivalent” math can take different code paths

Modern browsers contain multiple stacks that call math routines: JavaScript Math.*, layout/CSS math, and media/audio DSP pipelines.

Those subsystems may use different implementations, different argument reductions (the math trick of turning a big input into a smaller range), and different library backends.

So reproducing one function correctly in isolation can still leave detectable mismatches elsewhere.

3) Platform libraries can disagree internally

Even on the same OS, there can be multiple math-library implementations (for example scalar vs vectorized SIMD routines). Those might not agree on the exact rounding of transcendental outputs.

If the browser calls one backend for one subsystem and another backend elsewhere, “matching OS math” becomes underspecified.

The upshot

The tricky part isn’t that floating-point math varies—it’s that real browsers vary selectively. Fingerprinting thrives on selective variation.

A practical mental model: a fingerprint is a comparison, not a guess

Think of fingerprinting like a bouncer with a small checklist.

The bouncer doesn’t need to know your whole identity. They only need to find one value where:
- genuine environment A returns pattern X,
- genuine environment B returns pattern Y,
- and the patterns differ by enough last bits to be reliably distinguishable.

When Math.tanh routes through host libm, it can become that single “one-value check”.

The most unsettling part is that this can happen without any changes to the fingerprinting surface itself. It’s just the math engine’s plumbing switching underneath.

What developers and browser users should take away

This is a security/privacy lesson wearing a math hat.

  • Standardized APIs don’t guarantee bitwise determinism across platforms for transcendental functions.
  • Floating-point minutiae (ULPs) can become observable.
  • Engine implementations matter: a relatively small internal refactor (custom tanhstd::tanh) can turn a previously uniform value into an environment-dependent signature.

As a user, it’s useful to remember that “it’s just one number” still can leak a lot.

As a developer, it’s useful to recognize that “correctness” in math libraries often prioritizes speed and typical accuracy over strict cross-platform bitwise identity.

Background reading (technical)

  • V8 change replacing custom IEEE754 tanh with std::tanh (direction toward delegating to platform math). (chromium.googlesource.com)
  • Definition/behavior notes for Math.tanh in JavaScript. (developer.mozilla.org)
  • ULP (“unit in the last place”) intuition for why tiny floating-point differences matter. (developer.apple.com)
  • IEEE 754 discussion emphasizing that transcendental functions aren’t uniformly required to be correctly rounded across platforms. (eecg.toronto.edu)

Closing thought

Math.tanh is a great example of how modern systems can accidentally turn low-level implementation choices into high-level observables. Once the browser delegates a transcendental function to the host math library, the last bits stop being “noise” and start being a signal. In fingerprinting, that’s often all it takes: one signal, reproducible across sessions, tied to OS-level details.

ahsan

ahsan

Hello! I am Mr Ahsan, the writer of the Website. I am from Netherland. I like to write about technology and the news around it.

Comments (0)

No comments yet. Be the first to respond!

Leave a Comment

Your comment will be visible after review.