Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 13 Jun 2018 21:10:23 +0000 (UTC)
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r335089 - head/sys/i386/i386
Message-ID:  <201806132110.w5DLANi8071518@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kib
Date: Wed Jun 13 21:10:23 2018
New Revision: 335089
URL: https://svnweb.freebsd.org/changeset/base/335089

Log:
  Enable eager FPU context switch by default on i386 too, based on
  amd64 r335072.
  
  Security:	CVE-2018-3665
  Sponsored by:	The FreeBSD Foundation

Modified:
  head/sys/i386/i386/npx.c
  head/sys/i386/i386/swtch.s

Modified: head/sys/i386/i386/npx.c
==============================================================================
--- head/sys/i386/i386/npx.c	Wed Jun 13 20:35:56 2018	(r335088)
+++ head/sys/i386/i386/npx.c	Wed Jun 13 21:10:23 2018	(r335089)
@@ -191,6 +191,11 @@ int	hw_float;
 SYSCTL_INT(_hw, HW_FLOATINGPT, floatingpoint, CTLFLAG_RD,
     &hw_float, 0, "Floating point instructions executed in hardware");
 
+int lazy_fpu_switch = 0;
+SYSCTL_INT(_hw, OID_AUTO, lazy_fpu_switch, CTLFLAG_RWTUN | CTLFLAG_NOFETCH,
+    &lazy_fpu_switch, 0,
+    "Lazily load FPU context after context switch");
+
 int use_xsave;
 uint64_t xsave_mask;
 static	uma_zone_t fpu_save_area_zone;
@@ -319,6 +324,7 @@ npxinit_bsp1(void)
 	u_int cp[4];
 	uint64_t xsave_mask_user;
 
+	TUNABLE_INT_FETCH("hw.lazy_fpu_switch", &lazy_fpu_switch);
 	if (cpu_fxsr && (cpu_feature2 & CPUID2_XSAVE) != 0) {
 		use_xsave = 1;
 		TUNABLE_INT_FETCH("hw.use_xsave", &use_xsave);
@@ -777,6 +783,42 @@ npxtrap_sse(void)
 	return (fpetable[(mxcsr & (~mxcsr >> 7)) & 0x3f]);
 }
 
+static void
+restore_npx_curthread(struct thread *td, struct pcb *pcb)
+{
+
+	/*
+	 * Record new context early in case frstor causes a trap.
+	 */
+	PCPU_SET(fpcurthread, td);
+
+	stop_emulating();
+	if (cpu_fxsr)
+		fpu_clean_state();
+
+	if ((pcb->pcb_flags & PCB_NPXINITDONE) == 0) {
+		/*
+		 * This is the first time this thread has used the FPU or
+		 * the PCB doesn't contain a clean FPU state.  Explicitly
+		 * load an initial state.
+		 *
+		 * We prefer to restore the state from the actual save
+		 * area in PCB instead of directly loading from
+		 * npx_initialstate, to ignite the XSAVEOPT
+		 * tracking engine.
+		 */
+		bcopy(npx_initialstate, pcb->pcb_save, cpu_max_ext_state_size);
+		fpurstor(pcb->pcb_save);
+		if (pcb->pcb_initial_npxcw != __INITIAL_NPXCW__)
+			fldcw(pcb->pcb_initial_npxcw);
+		pcb->pcb_flags |= PCB_NPXINITDONE;
+		if (PCB_USER_FPU(pcb))
+			pcb->pcb_flags |= PCB_NPXUSERINITDONE;
+	} else {
+		fpurstor(pcb->pcb_save);
+	}
+}
+
 /*
  * Implement device not available (DNA) exception
  *
@@ -790,11 +832,13 @@ static int err_count = 0;
 int
 npxdna(void)
 {
+	struct thread *td;
 
 	if (!hw_float)
 		return (0);
+	td = curthread;
 	critical_enter();
-	if (PCPU_GET(fpcurthread) == curthread) {
+	if (PCPU_GET(fpcurthread) == td) {
 		printf("npxdna: fpcurthread == curthread %d times\n",
 		    ++err_count);
 		stop_emulating();
@@ -805,39 +849,10 @@ npxdna(void)
 		printf("npxdna: fpcurthread = %p (%d), curthread = %p (%d)\n",
 		       PCPU_GET(fpcurthread),
 		       PCPU_GET(fpcurthread)->td_proc->p_pid,
-		       curthread, curthread->td_proc->p_pid);
+		       td, td->td_proc->p_pid);
 		panic("npxdna");
 	}
-	stop_emulating();
-	/*
-	 * Record new context early in case frstor causes a trap.
-	 */
-	PCPU_SET(fpcurthread, curthread);
-
-	if (cpu_fxsr)
-		fpu_clean_state();
-
-	if ((curpcb->pcb_flags & PCB_NPXINITDONE) == 0) {
-		/*
-		 * This is the first time this thread has used the FPU or
-		 * the PCB doesn't contain a clean FPU state.  Explicitly
-		 * load an initial state.
-		 *
-		 * We prefer to restore the state from the actual save
-		 * area in PCB instead of directly loading from
-		 * npx_initialstate, to ignite the XSAVEOPT
-		 * tracking engine.
-		 */
-		bcopy(npx_initialstate, curpcb->pcb_save, cpu_max_ext_state_size);
-		fpurstor(curpcb->pcb_save);
-		if (curpcb->pcb_initial_npxcw != __INITIAL_NPXCW__)
-			fldcw(curpcb->pcb_initial_npxcw);
-		curpcb->pcb_flags |= PCB_NPXINITDONE;
-		if (PCB_USER_FPU(curpcb))
-			curpcb->pcb_flags |= PCB_NPXUSERINITDONE;
-	} else {
-		fpurstor(curpcb->pcb_save);
-	}
+	restore_npx_curthread(td, td->td_pcb);
 	critical_exit();
 
 	return (1);
@@ -861,8 +876,20 @@ npxsave(addr)
 		xsaveopt((char *)addr, xsave_mask);
 	else
 		fpusave(addr);
-	start_emulating();
-	PCPU_SET(fpcurthread, NULL);
+}
+
+void npxswitch(struct thread *td, struct pcb *pcb);
+void
+npxswitch(struct thread *td, struct pcb *pcb)
+{
+
+	if (lazy_fpu_switch || (td->td_pflags & TDP_KTHREAD) != 0 ||
+	    !PCB_USER_FPU(pcb)) {
+		start_emulating();
+		PCPU_SET(fpcurthread, NULL);
+	} else if (PCPU_GET(fpcurthread) != td) {
+		restore_npx_curthread(td, pcb);
+	}
 }
 
 /*

Modified: head/sys/i386/i386/swtch.s
==============================================================================
--- head/sys/i386/i386/swtch.s	Wed Jun 13 20:35:56 2018	(r335088)
+++ head/sys/i386/i386/swtch.s	Wed Jun 13 21:10:23 2018	(r335089)
@@ -283,6 +283,12 @@ sw1:
 cpu_switch_load_gs:
 	mov	PCB_GS(%edx),%gs
 
+	pushl	%edx
+	pushl	PCPU(CURTHREAD)
+	call	npxswitch
+	popl	%edx
+	popl	%edx
+
 	/* Test if debug registers should be restored. */
 	testl	$PCB_DBREGS,PCB_FLAGS(%edx)
 	jz      1f



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201806132110.w5DLANi8071518>