Re: Save a WRMSR GS.base?

From: Borislav Petkov

Date: Mon Jun 08 2026 - 17:21:34 EST


On Mon, Jun 08, 2026 at 01:05:28PM -0700, Borislav Petkov wrote:
> I think this is begging to be written down somewhere. Lemme point AI to it and
> see what it would generate.

Something like the below... I am thinking of sticking that somewhere under
Documentation... oh look: Documentation/arch/x86/x86_64/fsgs.rst. Looks like
there was already need to document this stuff which is clearly not really
transparent. What's there is covering the userspace side more tho.

Anyway, the below is a summary of our thread with AI, it ain't half-bad and
I think we should write it down for future reference.

Thx.

---
GS handling on context switches

SWAPGS

Swaps the base-address value in MSR_KERNEL_GS_BASE with the active GS.base in
the hidden portion of the GS selector register.

MOV <segment selector>, GS

(legacy path, non-FRED) Loads the GS selector and fetches the descriptor
attributes and base from the GDT/LDT. Writes a 32-bit base into the active
GS.base. Does not touch MSR_KERNEL_GS_BASE. Useless for task switching the
user GS base on its own without surrounding SWAPGS calls.

LKGS <selector>

(FRED path, replaces MOV GS) Like MOV GS in that it loads the selector and
descriptor attributes, but it redirects the base write — instead of updating
the active GS.base, it writes the descriptor base into IA32_KERNEL_GS_BASE
(i.e. MSR_KERNEL_GS_BASE).

Critical caveat: it only writes a zero-extended 32-bit value, because GDT/LDT
descriptors only encode 32-bit bases. This means it cannot correctly represent
a full 64-bit user-space GS base (e.g. a TLS pointer), so a full 64-bit WRMSR
is still required afterwards.

WRGSBASE <reg>

(with REX.W) Writes a full 64-bit value directly into the currently active
GS.base as FS.base and GS.base in 64-bit mode are expanded to 64-bit to cover
the full address space.

The problem in kernel context: the currently active GS.base belongs to the
kernel, not the user task. So using this during context switching would
corrupt the kernel's own GS.base, unless surrounded by SWAPGS (only safe in
IDT mode). Without REX.W, the upper 32 bits of the base are cleared instead.

WRMSR MSR_KERNEL_GS_BASE / WRMSRNS MSR_KERNEL_GS_BASE

Writes a full 64-bit value into MSR_KERNEL_GS_BASE, which holds the inactive
(user-space) GS base — the one that gets swapped into the active GS.base on
SWAPGS. This is the only instruction that can correctly set a 64-bit user GS
base during a context switch from kernel mode. WRMSRNS is the non-serializing
variant, which is preferable here since this MSR is architecturally
non-serializing anyway.

--
Regards/Gruss,
Boris.

https://people.kernel.org/tglx/notes-about-netiquette