RC RANDOM CHAOS

tanh now fingerprints your OS

Chromium 148 dispatches Math.tanh to the platform libm, leaking OS identity through last-bit IEEE 754 divergence. A silent, unpatchable fingerprinting signal.

· 6 min read
tanh now fingerprints your OS

Chromium 148 changed how V8 evaluates Math.tanh. The output is now a stable function of the host operating system’s math library. No CVE. No CVSS vector. This is not a memory-safety defect.

It is an observable discrepancy. CWE-203, mapped alongside CWE-200, information exposure. The affected surface is every Chromium-based browser at or above version 148 - Chrome, Edge, Brave, Opera, Vivaldi, and every Electron shell that pins a modern V8. A single floating-point call now leaks the underlying OS to any script that can run in a renderer. That is the whole of it, and it is enough.

The mechanism sits in IEEE 754. The standard mandates correct rounding for the basic arithmetic operations and square root. It does not require it for transcendental functions. tanh is not correctly rounded. Implementations are free to differ in the final unit in the last place, and they do. tanh(x) is typically computed through expm1 or a rational minimax approximation - different polynomial coefficients, different operation ordering, different last mantissa bits for the same input double.

V8 historically avoided this by shipping its own fdlibm-derived port. The Freely Distributable Math Library gave deterministic transcendental results across every platform Chromium targeted. Same input, same bits, whether the renderer ran on Windows, macOS, or Linux. That determinism was the privacy property, and almost nobody noticed it was there.

The 148 change removed it for tanh. The lowering now defers to the platform math routine on the fast path rather than the vendored implementation. glibc’s libm, Apple’s libm derived from the BSD msun tree, and the Windows UCRT each round tanh differently for a range of inputs. The high bits of the result agree. The low mantissa bits do not. Those bottom bits now encode which C runtime computed the value, which is to say they encode the OS.

The divergence is not noise. It is deterministic per tuple of OS family, libm version, and architecture. The same input on the same platform returns the same bit pattern on every call, in every tab, in every session, indefinitely. Reduce a fixed vector of inputs through tanh, read the raw doubles, and the result is a fixed token keyed to the host. It survives cookie deletion. It survives incognito. It is not stored anywhere the browser will clear because it is recomputed from the platform on demand.

The entropy is modest and that is the point. OS-family discrimination is worth roughly one and a half to two bits. Windows versus macOS versus a glibc Linux is the coarse cut. libc version granularity adds a fraction more. Alone it does not uniquely identify a machine. It does not need to. It is stable, it is orthogonal to cookie state, and it composes. Joined to a canvas hash, a WebGL vendor and renderer string, an AudioContext sample, and font metrics, the joint entropy climbs into identifier territory. Each weak signal that refuses to reset is worth more than a strong signal that does.

Reaching the primitive requires nothing. Any JavaScript context can call Math.tanh. No permission prompt. No user gesture. It runs inside a cross-origin sandboxed iframe with no allow attributes, which is the exact context an ad tag or an embedded tracker executes in. The script evaluates tanh across a fixed input set, views the results as a Float64Array aliased to a Uint8Array, and reads the little-endian byte layout where the low mantissa bytes carry the divergence. Hash the bytes. Match against a precomputed table of hash to OS class. The output is an OS label and a stable sub-identifier before the first ad even renders.

Map it to reconnaissance, not exploitation. The nearest MITRE reference is T1592.002, gather victim host information, software - the client-side analog of host profiling. There is no RCE. There is no sandbox escape. The renderer sandbox is irrelevant because nothing crosses it. The computation, the extraction, and the reduction all happen inside the renderer, and only an opaque token leaves. The sandbox contains code execution. It was never designed to contain arithmetic.

The real-world consumers already exist. Commercial fingerprinting libraries have harvested Math-function outputs for years - sin, exp, tan, and now tanh with a cleaner OS signal than before. This is not novel tradecraft. It is an incremental quality gain on an established recon surface. Bot-detection stacks of the Cloudflare class consume fingerprint entropy to link sessions across resets. Ad-tech pixels of the Meta class consume it to stitch identity across sites where third-party cookies are already dead. None of this is attributed to a named intrusion set because it is not intrusion. It is tracking infrastructure operating exactly as built.

The same primitive serves a targeted adversary without modification. An OS label narrows exploit selection. A watering-hole operator who fingerprints the visitor before staging knows whether to serve a Windows chain or a macOS chain, and serves nothing to a sandbox that returns the wrong bit pattern. Recon feeds targeting. A tracker that establishes OS with certainty in the first hundred milliseconds pre-selects the payload before the delivery stage commits. The tracking use case and the targeting use case run the same code. Only the follow-on differs.

What defenders see is nothing. That is not a figure of speech. The work is pure client-side JavaScript in the renderer. No syscall of interest is issued. No Sysmon Event ID 1 for process creation, no Event ID 10 for cross-process access, no Event ID 11 for file writes. No EDR alert category fires because no telemetry-generating action occurs - no injection, no token manipulation, no LSASS handle, no child process. The only network artifact is an HTTPS POST carrying a hash, byte-indistinguishable from a legitimate analytics beacon. There is no IOC to write. A WAF inspecting the request sees well-formed JSON. A SIEM has no event to correlate because no monitored subsystem was touched. The fingerprint never exists as anything a sensor can read until it is already reduced to a token, and the token is just data in a POST body.

Detection engineering has no hook. This is the structural gap, and it is total. Every control in a modern stack keys on an action - a process, a handle, a connection, a write. Fingerprinting produces none of them. It is computation over data the page is entitled to compute, transmitted over a channel the page is entitled to use. The absence of any anomalous action is not a monitoring failure. It is a property of the technique. The only observation point is the browser itself, and by the time the value leaves the browser it is opaque.

No patch arrives through the CVE process because there is no vulnerability to patch. The fix path is fingerprint resistance, which is a browser design decision, not a security update on a disclosure timeline. Brave injects noise into several floating-point surfaces to break exactly this determinism. Tor Browser forces deterministic math and standardizes the platform-visible values so the signal collapses. Chrome’s Privacy Sandbox targets cross-site cookies and does not address per-call transcendental determinism at all. The residual exposure post-148 is direct - any Chromium build that dispatches tanh to the platform libm carries the OS signal until V8 re-vendors a portable implementation or clamps transcendental output to a fixed precision below the divergent bits.

Under the Australian Privacy Act, a persistent cross-site identifier assembled without consent is personal information handling that APP 3 and APP 5 reach, and the collection here is covert by construction. There is no notice because there is no interaction. The mechanism is known and documented. The telemetry is silent by design. That combination - a stable identifier that generates no defensive signal - is precisely why the technique works and why a control-based response has nothing to attach to. The exposure is not in what the browser does wrong. It is in what the platform math library does differently, and what the renderer is permitted to read.

Share

Keep Reading

Stay in the loop

New writing delivered when it's ready. No schedule, no spam.