Spectre on RISC-V Silicon | Lukas Gerlach
Back to homeJul 3, 2026<br>7 min read<br>Spectre on RISC-V Silicon<br>Our paper Spectre on RISC-V Silicon: Attacks and Defenses on Commercial Out-of-Order Processors has been accepted at USENIX Security ‘26 .<br>Paper<br>Artifact
This post is the short version of what we found.<br>RISC-V is an open, modular instruction set architecture that anyone can implement without a license fee.<br>Many independent vendors build their own cores from it, ranging from tiny in-order microcontrollers to superscalar out-of-order designs.<br>For a long time, RISC-V had a reputation for being out of Spectre’s reach.<br>That reputation was reasonable in 2018.<br>Because the RISC-V silicon you could actually buy back then was in-order.<br>Both the RISC-V Foundation and SiFive said so publicly at the time, and they were right about the hardware that existed.<br>The problem is that the myth that RISC-V is somehow not affected by Spectre is quite persistent.<br>Commercial out-of-order RISC-V cores now use the same kind of speculation that made Spectre relevant on x86 and ARM, while the software stack was never revisited.<br>We wanted to know what that means for the RISC-V ecosystem.<br>1. Spectre Works on RISC-V Silicon<br>We evaluated the T-Head Xuantie C910/C920 and the SiFive P550.<br>These were the only commercially available out-of-order RISC-V processors when we did the study.<br>But there are many different out-of-order RISC-V processors coming out, and our work should make it easy to test them all.<br>The takeaway from these experiments was that all Spectre variants that worked on x86 before also work on RISC-V, but sometimes with a few extra tricks.<br>A notable exception is Spectre-BTB (v2), which does not work out of the box everywhere because whether the indirect branch predictor is enabled depends on the firmware.<br>The C910 and P550 ship with it disabled, while our C920 had it enabled.<br>Enabling it made the attack work but sometimes also crashed the machine, which hints at a hardware bug.<br>2. From BPF Gadget to File Contents<br>We also built an exploit for the paper that uses a Spectre gadget to read arbitrary files.<br>We used BPF because it was the only feasible exploit path on the machines we had.<br>Typically, kernel Spectre exploits use Spectre-BTB because it gives a lot of flexibility in gadget selection.<br>The RISC-V cores we tested split the BTB between userspace and kernel, which makes these exploits hard.<br>The basic gadget comes from a missing fence in the RISC-V BPF JIT.<br>Recent Linux kernels insert BPF_NOSPEC barriers when verifier-approved programs need a speculation barrier.1<br>The RISC-V JIT emits nothing for BPF_NOSPEC, so the verifier considers the program protected while the generated code has no barrier at all.<br>The gadget itself is a speculative type confusion.<br>We write BPF code where a branch decides what ends up in a register that gets dereferenced right after.<br>One path puts a valid kernel pointer there, the other puts a scalar we control.<br>Train the branch on the pointer path, then take the scalar path.<br>Architecturally nothing happens, the scalar is never dereferenced.<br>Transiently the processor mispredicts and loads from whatever address we picked.<br>The loaded value then indexes a BPF array map, which is where the covert channel comes in.<br>Array maps can be mapped into userspace with BPF_F_MMAPABLE, so Flush+Reload recovers the index.<br>BPF path used for the Spectre-PHT exploit.This gives an attacker an arbitrary speculative read into the kernel.<br>To read something useful, previous exploits swept the direct physical map of the kernel until they arrived at the information they wanted to leak.<br>This is a bad choice for the CPUs we tested, because while the leakage is very precise it is also kind of slow.<br>Instead we use the fact that Linux keeps recently read files in the page cache.<br>We can get the inode number of the target file through the public stat interface.<br>Then we leak a filesystem pointer, which we recover by walking pointers from init_task.<br>After we have both the inode number and the filesystem pointer we can compute the page-cache hash, find the cached page, and read the target file contents directly.<br>Translating a file name to the location of its contents with our exploit.3. Which Instruction Stops Speculation?<br>The missing fence raises an obvious question.<br>Which instruction should the JIT emit instead?<br>RISC-V does not really have an answer.<br>x86 has lfence and ARM has CSDB, but the RISC-V fence only orders memory operations.<br>It says nothing about speculation.<br>Linux assumes such an instruction exists anyway, so barrier_nospec() just compiles to a no-op.<br>So we tested 12 candidate instructions on both cores, and went back to the vendors with what we found.<br>fence.i stops speculation on both cores, in user code and in kernel code.<br>CSR reads are the cheaper option, and T-Head confirmed that they terminate speculation on the C910/C920.<br>On the P550 we measured the same for rdtime, though that one is our own result rather...