2026-09-12
When a SYSCALL instruction fires, the CPU jumps to the kernel's entry point with every general-purpose register still holding user-controlled garbage. The kernel needs a pointer to its per-CPU data structure — the runqueue, current task, kernel stack — and it needs it in one or two instructions, before it has a stack to spill to or a way to safely load from memory. SWAPGS is the trick that makes this possible.
The mechanism: the CPU maintains two hidden 64-bit registers, GS_BASE (the current base for %gs-relative addressing) and KernelGSBase (a shadow, in MSR 0xC0000102). SWAPGS atomically exchanges them. That's the whole instruction — no memory access, no flags touched, ~2 cycles.
The convention: user space's GS_BASE holds whatever glibc stashed there for TLS (well, that's FS_BASE; GS_BASE is usually zero). The kernel pre-loads each CPU's KernelGSBase at boot with a pointer to that CPU's struct cpu_entry_area. On syscall entry, one swapgs installs the kernel pointer; the very next instruction can do mov %gs:CPU_CURRENT_TASK, %rax to fetch the current task struct. On syscall exit, another swapgs restores user state.
Concrete example — Linux's entry_SYSCALL_64 in arch/x86/entry/entry_64.S:
swapgs — install kernel GS (~2 cycles)mov %rsp, %gs:cpu_tss_rw + TSS_sp2 — save user RSP into per-CPU scratchmov %gs:cpu_current_top_of_stack, %rsp — load kernel stackThree instructions and the kernel has a stack. Without SWAPGS, you'd need a load from a fixed physical address (impossible with KASLR) or a WRMSR (~100+ cycles).
The vulnerability (CVE-2019-1125, "SWAPGS attack"): the CPU speculates past SWAPGS. On a mispredicted branch into the syscall path, the CPU can execute mov %gs:X, %rax with the wrong GS base — leaking kernel or user data across the boundary through a cache side channel. The mitigation is an lfence after swapgs, killing exactly the speculation that made it fast.
Rule of thumb: any code you see using %gs: in kernel disassembly is a per-CPU access — a single-cycle load, no lock, no atomic, no shared cache line. That's why this_cpu_read() compiles to one instruction and outperforms every other cross-core communication primitive by an order of magnitude.
SWAPGS is also why WRGSBASE (a separate lesson) is such a big deal in user space: it lets user code get the same one-instruction per-thread pointer without touching an MSR.
this_cpu_* access in Linux.
