Generated by Codex with GPT 5.6 Sol XHigh

The difficult part of defending a shared compute platform is rarely a single missing check. It is the interaction among mechanisms that each look reasonable in isolation: a language sandbox, restricted timers, workload limits, anomaly detection, and efficient tenant placement. Cloudflare’s latest security research shows how those pieces can still compose into an unexpected attack path—and why the repair also has to be layered.

The official Cloudflare Blog describes an internal reassessment of remote Spectre attacks against Cloudflare Workers. Researchers built an end-to-end proof of concept against Workers they controlled and leaked a planted JWT from one tenant to another at up to 12 bits per second with more than 99% accuracy. The work was conducted in 2024 and early 2025; Cloudflare says the demonstrated path has since been mitigated and that it found no evidence of active exploitation during the preceding three years.

The headline result matters, but the deeper lesson is in the construction. The attack recreated, over a noisy production network, nearly every primitive that the Workers runtime had deliberately tried to remove. It also exposed two blind spots in a detector that was looking for the right low-level event but at the wrong lifecycle boundary and with a fragile normalization rule.

Why isolate-level efficiency creates a hard security boundary

Workers runs untrusted JavaScript in V8 isolates. Each tenant gets a separate JavaScript heap, while tens of thousands of isolates can share an operating-system process. Compared with assigning every tenant a process or virtual machine, this design makes startup fast and density high. The security consequence is that isolation depends on more than ordinary page-table separation: different tenants can execute in the same address space and on the same physical CPU resources.

Cloudflare surrounds V8 with additional controls, including automated patching, Linux namespaces, seccomp filters, Cap’n Proto RPC, and the ability to move suspicious scripts into separate process sandboxes. Its Dynamic Process Isolation system, or DyPrIs, monitors hardware performance counters and isolates workloads whose behavior resembles a Spectre attack. Workers also freezes local clocks during CPU-only execution and disallows shared memory and multithreading, removing the high-resolution timers commonly used for cache side channels.

Spectre operates below those language-level guarantees. A processor predicts a branch and temporarily executes down the predicted path before the condition is resolved. If the guess is wrong, the architectural result is discarded, but changes to microarchitectural state—especially CPU caches—remain observable. An attacker can train a branch predictor, induce a transient out-of-bounds read, use the secret bit to select a cache line, and later infer the bit from access latency.

That makes an arbitrary read bug qualitatively different in a multi-tenant isolate runtime. A normal JavaScript bounds check may still be correct when the instruction retires, while speculative execution briefly crosses the boundary that the program believes it enforced.

Rebuilding the missing attack primitives

The first challenge was a usable Spectre gadget. The researchers repeatedly called a branch on objects of the expected V8 type, training the processor to predict that the type check would succeed. They then supplied an object with an attacker-controlled layout. During the resulting speculative type confusion, the CPU followed a field as though it belonged to the trained type, even though the eventual type check failed.

One gadget disclosed the compressed base address of the isolate heap. A second confused two large objects whose type field and target field occupied different cache lines. Evicting the type field delayed branch resolution while leaving the target field available, opening a speculative window in which the code followed an attacker-chosen 64-bit address. At the time of the experiment, a TypedArray backing store was one of the few V8 heap structures that still exposed a raw 64-bit pointer, turning the primitive into an arbitrary-address transient read.

Reading a cache bit locally is not enough. Cache hits and misses differ by nanoseconds, while a network timing signal varies by microseconds or milliseconds. The attack bridged that gap with a property of the L1 cache’s tree-based pseudo-least-recently-used replacement policy. A carefully selected access pattern expanded the effect of one cached or uncached line into many subsequent hits or misses. Repetition strengthened the difference further, making it large enough to classify through a noisy remote clock.

The clock itself came from a WebSocket connection to an external timestamp server. The Worker marked the start and end of a measurement remotely, then retrieved the delta. Across several timer arrangements, the researchers obtained sub-millisecond median resolution with only a handful of samples. Per-invocation calibration and statistical voting separated the zero and one distributions despite interrupts, scheduler activity, and production traffic.

Resetting the cache between samples presented another obstacle because JavaScript has no direct cache-flush instruction and constructing a precise eviction set would itself require accurate timing. The researchers instead overfilled the cache with a large pool of object pairs and chose a fresh random pair for each measurement. By the pigeonhole principle, a randomly selected line was overwhelmingly likely to be absent already. This avoided searching for an exact eviction set and produced a useful auto-eviction effect.

Platform features completed the chain

An attacker and victim also had to land in the same process on the same edge server. The Workers placement model made this easier than its global scale might suggest. When one Worker fetched another, the scheduler would commonly start the target on the same machine and in the same process. Repeated subrequests kept the victim present, and the researchers could favor an off-peak data center where lower CPU contention improved the signal.

Resource limits appeared to constrain the long measurement loop. At the time, an invocation had 30 seconds of CPU time and 1,000 subrequests. Durable Objects changed the lifecycle. A persistent WebSocket sent keep-alive messages, and each message reset the relevant invocation budgets when the single-threaded isolate yielded to the event loop. By alternating synchronous amplification bursts with yields, the proof of concept kept an isolate alive for five to more than 20 hours.

The completed chain combined co-location, branch-predictor mistraining, transient pointer reads, probabilistic cache reset, PLRU amplification, remote timing, and per-run calibration. It first recovered an isolate root, then confirmed 64-bit leakage by reading recognizable strings from the virtual dynamic shared object region. Finally, it extracted the planted JWT from the controlled victim Worker bit by bit. The result was far faster than Cloudflare’s 2021 proof of concept, which had leaked roughly 120 bits per hour.

Detection failed at the lifecycle boundary

DyPrIs was designed for this class of behavior, but two implementation choices let the proof of concept pass. First, the system acted only after an invocation finished. A WebSocket-backed Durable Object could keep one invocation alive long enough to complete the leak before post-execution isolation occurred. A detector can identify the correct behavior and still respond too late if its unit of observation does not match the workload’s actual lifetime.

Second, DyPrIs normalized branch mispredictions by instruction-TLB accesses. The attack’s large remote-timing I/O loop increased those iTLB events, pushing the normalized ratio below the alert threshold. Activity that was necessary to exfiltrate the cache signal simultaneously diluted the metric intended to expose its speculative component.

This is a general reliability and security warning about ratios. A normalized signal is only robust when its denominator cannot be manipulated by an adversary—or changed drastically by a legitimate new execution model. Long-lived, I/O-heavy workloads were not edge cases for Durable Objects; the detector had treated them as though they were.

The fix narrows the risk in several layers

Cloudflare responded in three places. The V8 memory sandbox removes raw 64-bit pointers from large parts of the JavaScript heap, so the TypedArray structure exploited by the proof of concept no longer provides the same path to arbitrary addresses. This blocks the demonstrated gadget but is not a complete defense against every possible speculative-execution technique.

Cloudflare also deployed hardware-assisted in-process isolation using Memory Protection Keys. MPK assigns isolate heaps to hardware-enforced protection domains and changes access rights cheaply as execution moves between them. A cross-isolate access using the wrong key is denied, blocking the straightforward victim-heap read on which this attack depended. MPK still has operational constraints, including a limited number of protection domains and the need to manage key state correctly, so it reduces the leakage surface rather than abolishing Spectre.

Finally, DyPrIs was changed to treat long-lived invocations and I/O-heavy workloads as first-class security cases instead of waiting for completion. Cloudflare is also investigating remote timing patterns as a behavioral dimension: repeated timestamp-like I/O surrounding compute-heavy sections may be part of an exfiltration channel, not harmless background traffic.

Production isolation must be tested as a system

The broader engineering takeaway is that sandbox security cannot be proven one control at a time. Freezing local clocks did not eliminate timing because the network supplied a clock. Resource budgets did not cap the experiment because a stateful API renewed them. A detector saw branch behavior but deferred its response until an invocation boundary the workload could postpone. A global scheduler that improved locality also made adversarial co-location practical.

None of those features is inherently a mistake. Together, however, they changed the feasible attack surface. The effective security model lives in their composition, including the timing and lifecycle semantics that are easy to omit from a threat diagram.

Cloudflare’s most transferable choice was to test the full chain under production conditions and then harden independent layers: object representation, hardware memory access, and behavioral detection. That approach accepts that speculative execution remains difficult to eliminate completely. The practical goal is to remove useful gadgets, place hard boundaries behind soft ones, detect evolving behavior soon enough to act, and keep reassessing the assumptions as the runtime gains new capabilities.