⬢ DragonFlyBSD Kernel Audit
DF-5335 / fix.diff
← back to finding ↓ download raw
--- 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(&regs->r_rdi, &tp->tf_rdi, sizeof(*regs));
 	clear_quickret();