kernel: boot on AMD — SYSRET puts the RPL in the STAR base

A Ryzen 3 3200G triple-faulted on its first timer tick after reaching init.
Three defects in a chain, each hiding the one beneath it.

STAR's SYSRET base was 0x10, so SS came back as base+8 = 0x18 with RPL 0
while CS carried RPL 3. Intel ORs RPL 3 into SS on SYSRET; AMD only does so
for CS. Ring 3 ran fine — RPL is not checked on data access — and died the
moment an interrupt tried to IRETQ back, where SS.RPL must equal CS.RPL.
The base now carries the RPL (0x13), as Linux does.

Two fixes below it, both of which made the first one unreadable:

scheduler() read IA32_GS_BASE and dereferenced it without testing for zero,
so every fault reporter faulted in turn — a panic inside a panic, and the
machine reset before printing anything. Cast after the null test, plus a
re-entrancy guard in the panic handler.

NT is now masked in SFMASK alongside the rest, and isr.s exports
isr_return_iretq at the faulting instruction so a frame dump can say which
IRETQ died and print the CS/SS it was about to load. That dump is what
identified the RPL mismatch.
This commit is contained in:
Daniel Samson
2026-08-08 11:07:55 +01:00
parent a85f29edc9
commit 7db5fba884
3 changed files with 94 additions and 8 deletions
+10 -1
View File
@@ -518,4 +518,13 @@ isr_smap_patch:
testb $3, 8(%rsp)
jz 1f
swapgs
1: iretq
# Named so a fault reporter can recognise its own return instruction. A #GP
# here is the frame's fault, not this code's — the five words below RSP are
# what the CPU rejected, and they are the only evidence of why. Note that on
# the ring-3 path the swapgs above has already run, so a fault at this exact
# address re-enters the kernel with the *user's* GS base: per-CPU reads in
# that handler are reading user-controlled state and must not be trusted.
1:
.global isr_return_iretq
isr_return_iretq:
iretq