Zenbleed (CVE-2023-20593) is a speculative execution bug in various AMD Ryzen processors that can leak secrets from any process running on the same CPU as an attacker. It was recently discovered by Tavis Ormandy, a security researcher at Google. In contrast to Spectre/Meltdown, it is not a side channel leak, but a bug in the microcode. The bug itself can be triggered by an attacker with unprivileged code execution capabilities, and results in leaking register contents from victim processes. Specifically vulnerable are string operations such as strlen, memcpy and strcmp, as they make heavy use of the AVX2 capabilities that contain the bug. The researcher created proof-of-concept code that demonstrates dumping strings, including secrets, from any process on the system.
This is specifically a risk in cloud environments, where CPUs are shared amongst tenants. It means that one tenant could spy on the processes of another tenant and dumping secrets such as passwords and keys. Major cloud providers have since patched their CPU microcode to avoid this vulnerability.
How does it work?
The Zenbleed vulnerability is triggered by a specific combination of the XMM Register Merge Optimization, a register rename, and a mispredicted vzeroupper instruction. The blogpost by Tavis Ormandy describes this in detail. To provide an intuition: there is a microcode bug that when a speculatively executed vzeroupper instruction is rolled back, the pointer table and zero flag of YMM registers are not properly set. The effect is that they point to data that should not be accessible.
To provide some background on the vulnerability, consider the following. The AVX2 instruction set allows usage of XMM and YMM registers, which are respectively 128 and 256 bits wide. However, they overlap: the lower 128 bits of e.g. the YMM0 register are aliased to XMM0. To allow speculative and superscalar execution, the registers are not directly stored. They are stored indirectly, as pointers into a large register file. When a program accesses e.g. XMM0, the CPU looks up where XMM0 is currently stored through the pointer, and then returns its contents from the register file. This is architecturally invisible to programs. For the YMM registers, the upper and lower 128 bits have separate pointers. The final important piece to know is that when a register (or for YMM, the upper or lower half register) is zero, this will be indicated in the pointer area with a zero flag, and the zeros will not be physically stored in the register file. So, when the vzeroupper instruction is applied to a YMM register, it only sets the zero flag, freeing up space in the register file. When the vzeroupper is speculatively executed, but needs to be rolled back, there is a bug in doing that, essentially leading to an incorrect pointer for YMM that points to the data of another process.
How was it found?
Interestingly, this bug was found by CPU fuzzing. This was done by creating a fuzzer that creates code sequences. With “regular” code-level fuzzing, feedback to the fuzzer is typically provided by code coverage information, allowing it to discover new code paths. With this CPU fuzzing, there is no such concept as code coverage. Instead, the fuzzer uses the CPUs performance counters to discover new and interesting code sequences. Another difference with “regular” fuzzing is that there are no program crashes that can be used to detect vulnerable behavior. Instead, differential fuzzing is used: after each fuzz attempt, the resulting state is compared to a “ground truth” state. If the state differs, some bug has been found. The CPU fuzzing used was specifically focused on speculative execution bugs. To get a ground truth state, the researcher first ran a fuzz attempt without speculative execution (using fence instructions), then ran an attempt with speculative execution, and compared the differences. This approach eventually resulted in differences in YMM register state, and thereby discovering Zenbleed.
AMD has released microcode updates. While writing this article, two other bugs (on Intel and AMD) were published: Downfall and Inception. Spectre and Meltdown caused a significant (academic) interest in CPU bugs, which is now leading to ever increasing publications. In general, it is good to realize that microcode is code, and code has bugs. The upside in this case is that this also means the CPU bugs can be patched, though often with a performance penalty.