x86,c_traps: avoid PC corruption in kernel lock

When we back out of the lock via IPI stall in ipiStallCoreCallback(),
the FaultIP/NextIP are not updated yet and the lock exit code will set
the wrong program counter.

Moving the FaultIP/NextIP before the lock makes sure that all exit
paths will restore to the correct program counter.

Even though this write happens outside the lock, it does not introduce
a race. The only possible interference would be a TCBWriteRegisters
syscall from another core along the lines of the following: other core
enters syscall, current core gets to lock, makes FaultIP adjustment,
other core exists syscall, current core overwrites effect. This path is
not possible, because the TCBWriteRegisters call on the other core
first leads to a stall on this core before it proceeds, which
means this core will be on idle and cannot attempt to enter the lock.

Signed-off-by: Gerwin Klein <gerwin.klein@proofcraft.systems>
This commit is contained in:
Gerwin Klein 2026-07-05 19:56:24 +10:00
parent eba3cc75f9
commit d0e0a6a5a2

View file

@ -147,6 +147,16 @@ void VISIBLE NORETURN c_handle_syscall(word_t cptr, word_t msgInfo, syscall_t sy
x86_enable_ibrs();
}
/* Must happen before NODE_LOCK_SYS so that lock exit via IPI stall restores
the correct program counter. */
if (config_set(CONFIG_SYSENTER)) {
/* increment NextIP to skip sysenter */
NODE_STATE(ksCurThread)->tcbArch.tcbContext.registers[NextIP] += 2;
} else {
/* set FaultIP */
setRegister(NODE_STATE(ksCurThread), FaultIP, getRegister(NODE_STATE(ksCurThread), NextIP) - 2);
}
NODE_LOCK_SYS;
c_entry_hook();
@ -156,14 +166,6 @@ void VISIBLE NORETURN c_handle_syscall(word_t cptr, word_t msgInfo, syscall_t sy
ksKernelEntry.is_fastpath = 1;
#endif /* TRACK_KERNEL_ENTRIES */
if (config_set(CONFIG_SYSENTER)) {
/* increment NextIP to skip sysenter */
NODE_STATE(ksCurThread)->tcbArch.tcbContext.registers[NextIP] += 2;
} else {
/* set FaultIP */
setRegister(NODE_STATE(ksCurThread), FaultIP, getRegister(NODE_STATE(ksCurThread), NextIP) - 2);
}
#ifdef CONFIG_FASTPATH
if (syscall == (syscall_t)SysCall) {
fastpath_call(cptr, msgInfo);