A vulnerability identified as CVE-2025-21632 was discovered and recently patched in the Linux kernel's x86 Floating Point Unit (FPU) codebase. This bug involves how the kernel deals with the shadow stack—a security feature meant to protect the return addresses of functions on the stack. Because of this issue, under certain conditions, the kernel could incorrectly handle shadow stack state, triggering warnings and potential instability.
In this article, we’ll break down how this bug worked, what the impact was, how it was fixed, and show code snippets to clarify the details. No prior kernel experience is required—we’ll keep it simple and clear.
Background: What is the Shadow Stack?
The shadow stack is part of Intel’s Control-flow Enforcement Technology (CET), which helps defend against some types of control-flow hijacking attacks, like Return-Oriented Programming (ROP).
The kernel maintains special state for the shadow stack in per-thread data, but this state is managed differently than traditional Floating Point (FP) or SIMD registers.
How Do Userspace and ptrace Interact With Shadow Stacks?
To inspect or modify thread or process states, Linux debuggers and other tools use ptrace. For standard FPU and extended state, ptrace interfaces with routines that know how to save and restore this state safely.
- XSAVE/XRSTOR are instructions to save/restore various CPU states, but shadow stack state (a *"supervisor state component"*) isn’t accessible to normal userspace or through the old XSAVE ptrace API.
CVE-2025-21632: The Core of the Bug
Each thread keeps a flag (ARCH_SHSTK_SHSTK) to track whether the shadow stack is enabled and ready.
When the kernel gets or sets state via this regset handler, it’s supposed to check if shadow stack is actually enabled. There is an ->active() handler that is *supposed* to make this check—but the way ptrace calls the handler, this ->active() check is sometimes skipped!
That means, in rare situations, the kernel tries to get the shadow stack registers when they haven’t been initialized or enabled. When that happens, the following call returns NULL:
get_xsave_addr(xfpstate, XFEATURE_CET_USER)
And if that pointer is NULL, later code triggers a WARN_ON()
void *addr = get_xsave_addr(xfpstate, XFEATURE_CET_USER);
WARN_ON(!addr);
A real example from Christina Schimpe (see lkml.org thread)
WARNING: CPU: 5 PID: 1773 at arch/x86/kernel/fpu/regset.c:198 ssp_get+x89/xa
...
Call Trace:
...
ssp_get+x89/xa
...
This doesn’t immediately let attackers take over the kernel, but it can cause kernel panics or disrupt debugging.
The Patch: How Did Linux Fix It?
The fix is simple: ALWAYS check that the shadow stack is active before accessing its state in the XSAVE buffer, regardless of what’s calling.
Here’s a simplified pseudo-diff of the kernel patch
- void *addr = get_xsave_addr(xfpstate, XFEATURE_CET_USER);
- WARN_ON(!addr);
+ if (!ssp_active(t)) // <-- NEW CHECK
+ return -EINVAL;
+ void *addr = get_xsave_addr(xfpstate, XFEATURE_CET_USER);
+ if (!addr)
+ return -EINVAL;
The important thing: ssp_active() is called before touching the buffer, so we KNOW shadow stack state is actually there.
Proof of Concept: How Could This Be Triggered?
You could trigger this bug by using ptrace to read shadow stack state from a thread before it has had CET enabled. This is hard to do accidentally, but a simple scenario is:
Start a traced process on an x86 system that supports shadow stack.
2. Before enabling CET/shadow stack, use ptrace to get the shadow stack regset.
Here’s a *sketch* in C pseudocode
#include <sys/ptrace.h>
// Platform-specific headers omitted
// 'pid' is running process
struct iovec iov = { .iov_base = buf, .iov_len = length }; // buf/length = CET_USER struct size
// This regset number is for illustration only
int cet_user_regset_num = ...; // defined in kernel sources
int ret = ptrace(PTRACE_GETREGSET, pid, cet_user_regset_num, &iov);
// On buggy kernels, this could trigger a kernel WARN
Note: This won’t allow privilege escalation or kernel compromise directly, but it can destabilize systems and confuse debuggers.
Who Is Affected?
- Linux users with Intel CET/Shadow Stack Hardware Support: Most users aren’t vulnerable, unless their hardware and kernel have CET enabled.
- System administrators & kernel developers: Debugging tools or custom software using ptrace might hit this issue.
Original Patch (LKML):
x86/fpu: Ensure shadow stack is active before "getting" registers (lkml.org)
Upstream commit:
kernel.org git commit
- Shadow Stack/CET Documentation:
kernel.org CET Shadow Stack Documentation
Conclusion
CVE-2025-21632 shows how new security features can sometimes open up new paths for complex bugs if kernel paths aren’t audited carefully. While this particular bug is not a direct security hole, it could cause warnings, kernel instability, or confusion for tools analyzing thread state.
Update your kernel if you run on hardware and kernels supporting x86 shadow stack (CET).
- If writing kernel patches: carefully check regset handlers and all entrypoints, not just the common code paths!
Stay safe, and keep your kernels patched.
*Written exclusively for this post. For more kernel deep dives, keep following!*
Timeline
Published on: 01/19/2025 11:15:08 UTC
Last modified on: 05/04/2025 07:17:52 UTC