How Safari Mitigated Spectre and Meltdown Attacks

In early 2018, the discovery of the Spectre and Meltdown hardware vulnerabilities forced web browser developers to urgently rethink how they handle memory and timing operations. Because modern web browsers execute untrusted JavaScript code from across the internet, they became a primary vector for exploiting these CPU-level flaws. Apple's Safari web browser, powered by the WebKit engine, responded swiftly to protect users from these speculative execution side-channel attacks. This article explains how Safari neutralized these threats by deliberately reducing the precision of the performance.now() timer and isolating the SharedArrayBuffer object.

Understanding the Browser Threat

Spectre and Meltdown exploit a hardware optimization technique called speculative execution. To make processors faster, CPUs guess which instructions will be needed next and execute them ahead of time. If the guess is wrong, the CPU discards the operation, but traces of the speculatively accessed data remain in the CPU's cache.

While Meltdown was primarily mitigated at the operating system kernel level in macOS and iOS, Spectre posed a unique threat to Safari. An attacker could use malicious JavaScript on a website to trick the CPU into speculatively accessing sensitive memory (like passwords or session tokens) belonging to other websites. To actually steal this data, the attacker needed a high-precision timer to measure how fast the CPU accessed certain memory addresses. Fast access meant the data was in the cache, allowing the attacker to deduce the contents of protected memory byte by byte.

To stop Spectre in Safari, Apple didn't need to fix the CPU; they simply needed to blind the attacker by removing the high-precision clocks available in JavaScript.

Reducing performance.now() Timer Precision

The most direct way to measure time in a browser is the performance.now() JavaScript API. Originally, this function provided timestamps with microsecond precision to help developers measure frame rates and benchmark code performance. However, this level of accuracy was precise enough for attackers to distinguish between a CPU cache hit (fast) and a cache miss (slow).

To neutralize this side-channel, the WebKit team immediately degraded the accuracy of performance.now().

  • Resolution Clamping: Safari reduced the timer's resolution to 1 millisecond (1,000 microseconds). Because CPU cache operations happen in nanoseconds, a 1-millisecond tick is far too coarse to measure the difference between cached and uncached memory.
  • Timer Jitter: In addition to rounding the time, WebKit introduced random noise, or "jitter," to the timestamps. This prevented attackers from using statistical analysis over thousands of attempts to guess the exact moment a millisecond ticked over.

Disabling and Isolating SharedArrayBuffer

Degrading performance.now() was only half the battle. Clever attackers realized they could build their own high-resolution timer using a JavaScript feature called SharedArrayBuffer.

SharedArrayBuffer allows a website to share memory simultaneously between the main browser thread and background threads (Web Workers). An attacker could set up a Web Worker whose only job was to endlessly increment a number in a shared memory space (e.g., 1, 2, 3, 4...). By reading this rapidly increasing number from the main thread before and after a memory operation, the attacker had a makeshift stopwatch capable of nanosecond-level precision, completely bypassing the crippled performance.now() timer.

Safari handled SharedArrayBuffer in two distinct phases:

Phase 1: Complete Removal

Because SharedArrayBuffer could be weaponized as a custom clock, Apple completely disabled the feature in Safari 11.1. By removing the API from the browser entirely, attackers could no longer use concurrent threads to measure the speed of speculative execution.

Phase 2: Cross-Origin Isolation

While disabling SharedArrayBuffer secured the browser, it broke legitimate high-performance web applications, such as video editors and 3D games, that relied on shared memory. To bring the feature back safely, Safari implemented a security model called Cross-Origin Isolation (introduced in Safari 15.2).

Even if a Spectre attack succeeds, it can only read memory that exists within the same operating system process. Therefore, Safari now requires websites that want to use SharedArrayBuffer to opt into strict security headers:

  • Cross-Origin-Opener-Policy (COOP)
  • Cross-Origin-Embedder-Policy (COEP)

When a website sends these headers, Safari places the webpage into its own isolated process and blocks it from loading resources from other websites unless those sites explicitly allow it. Because no sensitive cross-origin data exists in the same process anymore, an attacker has nothing to steal. Only when a website is fully isolated in this manner does Safari unlock SharedArrayBuffer and restore high-precision timers.