commit aa00f938f6b3c6a5b4502c25605666f479a22c16 Author: Russ Cox Date: Tue Mar 4 14:47:25 2025 -0500 sys/src/9k: fix noted(NCONT) losing registers If a process is interrupted by a trap and decides on the way out of the trap to handle a pending note, then when the note handler finishes and calls noted(NCONT), the kernel must return back to the original process context using IRET in order to restore all the registers to their exact state at the time of the trap. (This is the only case where a system call does not return to the code immediately after the SYSCALL instruction.) The existing system call handler code assumed that it could always exit a syscall using SYSRET; that is, it assumed that it would always return from a syscall back to immediately after the SYSCALL instruction. As a result, the noted(NCONT) case just described used SYSRET and corrupted some of the registers on its way back. It is difficult to believe the system worked as well as it did. I guess most programs do not noted(NCONT). diff --git a/sys/src/9k/k10/fns.h b/sys/src/9k/k10/fns.h index cd85923e..7a5f4c72 100755 --- a/sys/src/9k/k10/fns.h +++ b/sys/src/9k/k10/fns.h @@ -174,6 +174,7 @@ void touser(uintptr); void syscallentry(void); void syscallreturn(void); void sysrforkret(void); +void trapreturn(Ureg*); #define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) diff --git a/sys/src/9k/k10/l64idt.s b/sys/src/9k/k10/l64idt.s index 9a417a3f..4c644094 100755 --- a/sys/src/9k/k10/l64idt.s +++ b/sys/src/9k/k10/l64idt.s @@ -49,9 +49,12 @@ _intrnested: MOVQ SP, RARG PUSHQ SP CALL trap(SB) - -TEXT _intrr<>(SB), 1, $-4 /* so ktrace can pop frame */ POPQ AX + MOVQ SP, RARG + CALL trapreturn(SB) + +TEXT trapreturn(SB), 1, $-4 /* so ktrace can pop frame */ + MOVQ RARG, SP POPQ AX POPQ BX diff --git a/sys/src/9k/k10/l64syscall.s b/sys/src/9k/k10/l64syscall.s index b605b2ef..eaca1eae 100755 --- a/sys/src/9k/k10/l64syscall.s +++ b/sys/src/9k/k10/l64syscall.s @@ -35,8 +35,10 @@ TEXT syscallentry(SB), 1, $-4 PUSHQ R11 /* old flags */ PUSHQ $SSEL(SiUCS, SsRPL3) /* old code segment */ PUSHQ CX /* old ip */ + PUSHQ $-1 /* error code */ + PUSHQ $-1 /* type */ - SUBQ $(18*8), SP /* unsaved registers */ + SUBQ $(16*8), SP /* unsaved registers */ MOVW $SSEL(SiUDS, SsRPL3), (15*8+0)(SP) MOVW ES, (15*8+2)(SP) diff --git a/sys/src/9k/k10/trap.c b/sys/src/9k/k10/trap.c index 6e97ae3e..4592a143 100755 --- a/sys/src/9k/k10/trap.c +++ b/sys/src/9k/k10/trap.c @@ -19,6 +19,7 @@ static void faultamd64(Ureg*, void*); static void doublefault(Ureg*, void*); static void unexpected(Ureg*, void*); static void dumpstackwithureg(Ureg*); +static void dumpgpr(Ureg*); static Lock vctllock; static Vctl *vctl[IdtMAX+1]; @@ -258,7 +259,7 @@ intrtime(Mach*, int vno) /* go to user space */ void -kexit(Ureg*) +kexit(Ureg *ureg) { uvlong t; Tos *tos; @@ -273,6 +274,18 @@ kexit(Ureg*) tos->kcycles += t - up->kentry; tos->pcycles = up->pcycles; tos->pid = up->pid; + + /* + * system calls set type == ~(uvlong)0. + * If this Ureg is not from a system call, call trapreturn explicitly + * to make sure we use IRET and not SYSRET. + * Otherwise, if a process is interrupted by a trap and ends up + * calling a note handler, the noted(NCONT) system call will use + * SYSRET to return to the trap context, smashing some of the + * registers that were live at the time of the trap. + */ + if(ureg->type != ~(uvlong)0) + trapreturn(ureg); } /*