Spectre Exploit and JavaScript Timers

The Spectre vulnerability revealed a fundamental security flaw in modern CPU architectures, allowing attackers to read sensitive memory data through speculative execution side-channels. In response, web browser vendors modified JavaScript runtimes by intentionally reducing the precision of high-resolution timers like performance.now() and restricting features like SharedArrayBuffer. These changes directly disrupted the microsecond-level timing measurements attackers relied upon to infer private data from browser memory.

Understanding the Spectre Vulnerability

Spectre targets speculative execution, a hardware optimization technique used by modern processors. To maximize throughput, CPUs predict the path of conditional branches and execute instructions before the branch condition is officially resolved. If the CPU predicts correctly, execution proceeds without delay; if it mispredicts, the architectural changes are discarded.

However, discarded speculative operations still leave microarchitectural side effects, notably in the CPU data cache. An attacker can construct code that speculatively accesses unauthorized memory and loads it into the cache. By measuring the time it takes to access that data later, the attacker can determine whether the data resides in the cache (a cache hit) or in main memory (a cache miss), effectively reconstructing secret information bit by bit.

The Role of High-Resolution Timers in Attacks

For a side-channel attack to succeed in a web browser, untrusted JavaScript code must measure execution intervals with extreme precision. The difference between reading from a CPU cache versus main system memory is typically a matter of tens of nanoseconds.

Before Spectre was disclosed, JavaScript provided access to fine-grained clocks through the High Resolution Time API (performance.now()), which offered sub-millisecond precision down to five microseconds or better. Additionally, developers could create custom, high-precision clocks by spawning background Web Workers that continuously incremented values in a SharedArrayBuffer. These fine-grained clocks gave malicious scripts the exact fidelity required to distinguish cache hits from misses.

Browser Countermeasures

Because modern hardware could not be instantly patched, browser vendors mitigated the risk by eliminating the attacker’s ability to measure time accurately.

  1. Clamping Timer Precision: Browser engines (including V8, SpiderMonkey, and JavaScriptCore) reduced the resolution of performance.now() and Date.now(). Timer precision was coarsened from microseconds to tens or hundreds of microseconds (e.g., 20µs, 50µs, or 100µs depending on the engine and platform).
  2. Adding Jitter: Engines introduced deliberate random variations (jitter) to timer output, making it difficult for an attacker to average out multiple measurements to calculate exact access times.
  3. Disabling SharedArrayBuffer: Browsers initially disabled SharedArrayBuffer entirely to prevent attackers from creating shared-memory counter threads that acted as substitute high-resolution clocks.

Long-Term Impact on Web Development

While these changes successfully neutralized timing-based side-channel attacks in web browsers, they created performance-tracking trade-offs for developers building high-performance web applications, audio tools, and 3D graphics engines.

To restore advanced capabilities without sacrificing security, browsers developed architectural mitigations like Site Isolation, which enforces that pages from different origins run in separate operating system processes. By pairing process isolation with opt-in security headers—specifically Cross-Origin Opener Policy (COOP) and Cross-Origin Embedder Policy (COEP)—modern browsers have safely re-enabled features like SharedArrayBuffer within isolated environments, while high-resolution timer limits remain in place for non-isolated contexts.