DF-5335 / fix.diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | --- a/sys/platform/pc64/x86_64/trap.c +++ b/sys/platform/pc64/x86_64/trap.c @@ -73,6 +73,7 @@ #include <vm/vm_kern.h> #include <vm/vm_param.h> #include <machine/cpu.h> +#include <machine/segments.h> #include <machine/pcb.h> #include <machine/smp.h> #include <machine/specialreg.h> @@ -677,8 +678,45 @@ * were in at the iretq. */ if (frame->tf_rip == (long)doreti_iret) { - frame->tf_rip = (long)doreti_iret_fault; - goto out2; + /* + * The iretq faulted returning to + * user mode (typically a corrupt + * %ss/%rsp in a context restored + * by sigreturn). Do NOT redirect + * to doreti_iret_fault: it re- + * builds the trap frame from + * stale stack/PCB data above the + * popped user frame (the frame + * tail has already been clobbered + * by KMMUENTER_TFERR's copy), and + * the garbage tf_cs/tf_rflags/ + * tf_rsp/tf_ss cascade into a + * triple fault or SMP livelock. + * + * The user context being returned + * to is unrecoverable at this + * point. Synthesize a minimal + * deterministic user frame and + * deliver SIGBUS through the + * normal user-trap path. rip=0/ + * rsp=0 guarantees that either + * the signal is delivered, or + * the process faults again at + * address 0 instead of looping + * in the kernel. + */ + userenter(td, p); + frame->tf_rip = 0; + frame->tf_rsp = 0; + frame->tf_rflags = PSL_I | 0x2; + frame->tf_cs = GSEL(GUCODE_SEL, + SEL_UPL); + frame->tf_ss = GSEL(GUDATA_SEL, + SEL_UPL); + sticks = 0; + i = SIGBUS; + ucode = BUS_OBJERR; + goto user_signal; } } break; @@ -794,6 +832,7 @@ * If the fault is directly related to a VM context managed by a * virtual kernel then let the virtual kernel handle it. */ +user_signal: if (lp->lwp_vkernel && lp->lwp_vkernel->ve) { vkernel_trap(lp, frame); goto out; --- a/sys/platform/pc64/x86_64/machdep.c +++ b/sys/platform/pc64/x86_64/machdep.c @@ -941,6 +941,8 @@ */ #define EFL_SECURE(ef, oef) ((((ef) ^ (oef)) & ~PSL_USERCHANGE) == 0) #define CS_SECURE(cs) (ISPL(cs) == SEL_UPL) +/* DF-5335: only the standard user data selector is ever valid as %ss */ +#define SS_SECURE(ss) (((ss) & 0xffff) == GSEL(GUDATA_SEL, SEL_UPL)) int sys_sigreturn(struct sysmsg *sysmsg, const struct sigreturn_args *uap) @@ -1036,6 +1038,23 @@ trapsignal(lp, SIGBUS, T_PROTFLT); return(EINVAL); } + + /* + * DF-5335: A corrupt %ss (or non-canonical %rsp) in the + * restored context makes the iretq in doreti fault after + * the CPU has already committed the (broken) SS:RSP; the + * resulting exception cascade runs below any C handler and + * resets or wedges the machine. Reject such frames here. + */ + if (!SS_SECURE(ucp->uc_mcontext.mc_ss) || + (vm_offset_t)ucp->uc_mcontext.mc_rsp >= + VM_MAX_USER_ADDRESS) { + kprintf("sigreturn: ss = 0x%lx rsp = 0x%lx\n", + (long)ucp->uc_mcontext.mc_ss, + (long)ucp->uc_mcontext.mc_rsp); + trapsignal(lp, SIGBUS, T_PROTFLT); + return(EINVAL); + } /* gcc errors out on optimized bcopy */ _bcopy(&ucp->uc_mcontext.mc_rdi, regs, sizeof(struct trapframe)); @@ -3023,7 +3042,8 @@ tp = lp->lwp_md.md_regs; if (!EFL_SECURE(regs->r_rflags, tp->tf_rflags) || - !CS_SECURE(regs->r_cs)) + !CS_SECURE(regs->r_cs) || + !SS_SECURE(regs->r_ss)) return (EINVAL); bcopy(®s->r_rdi, &tp->tf_rdi, sizeof(*regs)); clear_quickret(); |