Artikel von Scrapfly Engineering (12. Juli 2026) über einen Fingerprinting-Kanal, der in den letzten Bits einer Zahl sitzt.

Der Mechanismus. IEEE 754 legt fest, wie ein double gespeichert wird, verlangt aber nicht die korrekte Rundung transzendenter Funktionen. Da korrekte Rundung teuer ist, liefert jede Plattform eine eigene libm mit eigenen Minimax-Koeffizienten, Tabellen und Konstanten. Dadurch liefert Math.tanh(0.8) drei unterschiedliche Werte, je nach glibc, libsystem_m und UCRT. Linux und macOS weichen bei rund einem Viertel der Eingabewerte voneinander ab, typischerweise um 1 ULP. „Ein Detektor braucht keine Mathematik, nur eine Tabelle.“ Und die Inkonsistenz ist unmittelbar ausnutzbar: macOS zu behaupten, aber Linux-Bits zurückzugeben, widerspricht dem eigenen User-Agent.

Das Erkennungsmerkmal ist neu und datiert. Bis Chrome 147 berechnete V8 tanh mit einem eingebetteten fdlibm, überall identisch. Der Commit c1486295ae5 ersetzte ihn durch std::tanh, das die libm des Hosts liest, ausgeliefert mit Chrome 148.

Drei Oberflächen lecken. Math.tanh ist die einzige betroffene Math.*-Funktion — V8 bettet alle übrigen ein und verlinkt sie statisch. Alle sieben CSS-Trigonometriefunktionen lecken, wobei Blink die libm des Hosts nach einer gradbasierten Winkelreduktion aufruft, die keinen Code mit Math.sin teilt. Und Web Audio berührt drei Bibliotheken innerhalb eines einzigen Graphen: Accelerate für FFT- und Vektorstufen, skalare libsystem_m für die transzendenten Funktionen des Kompressors. WASM dagegen leckt nicht das Betriebssystem — nur die Architektur.

Vier Fallen erschweren die Gegenmaßnahme: Nur manche Funktionen lecken, sodass das Fälschen der übrigen eine erkennbare Asymmetrie erzeugt; JavaScript und CSS sind getrennte Codepfade; macOS enthält zwei Mathematikbibliotheken, die voneinander abweichen, je nach Funktion um 10 bis 89 %, sodass „Apples Mathematik nachbilden“ erst dann Sinn ergibt, wenn klar ist, welche an welcher Stelle aufgerufen wird; und ARM und x86 unterscheiden sich bei fused multiply-add und der NaN-Weitergabe.

Rauschen funktioniert nicht: Es erzeugt einen Wert, der mit keinem realen Betriebssystem übereinstimmt, und sein fehlender Determinismus ist selbst ein Signal. Der einzige Weg ist die bitgenaue Reproduktion — Koeffizienten aus der Ziel-libm extrahiert und hexadezimal transkribiert, jede Fusion als explizites fma() geschrieben, kompiliert mit -ffp-contract=off.

Der Herausgeber gibt an, dass seine Beiträge mit KI-Unterstützung verfasst werden, während Mechanismen, Zahlen und Code aus eigener Feder stammen.