Преглед изворни кода

x86: prioritize the FPU traps for the error code

In the case of multiple FPU errors, prioritize the error codes,
instead of returning __SI_FAULT, which ends up pushing a 0 as the
error code to userspace, a POSIX violation.

For i386, we will simply return if there are no errors at all; for
x86-64 this is probably a "can't happen" (and the code should be
unified), but for this patch, return __SI_FAULT|SI_KERNEL if this ever
happens.

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
H. Peter Anvin пре 16 година
родитељ
комит
adf77bac05
1 измењених фајлова са 15 додато и 19 уклоњено
  1. 15 19
      arch/x86/kernel/traps.c

+ 15 - 19
arch/x86/kernel/traps.c

@@ -664,7 +664,7 @@ void math_error(void __user *ip)
 {
 {
 	struct task_struct *task;
 	struct task_struct *task;
 	siginfo_t info;
 	siginfo_t info;
-	unsigned short cwd, swd;
+	unsigned short cwd, swd, err;
 
 
 	/*
 	/*
 	 * Save the info for the exception handler and clear the error.
 	 * Save the info for the exception handler and clear the error.
@@ -675,7 +675,6 @@ void math_error(void __user *ip)
 	task->thread.error_code = 0;
 	task->thread.error_code = 0;
 	info.si_signo = SIGFPE;
 	info.si_signo = SIGFPE;
 	info.si_errno = 0;
 	info.si_errno = 0;
-	info.si_code = __SI_FAULT;
 	info.si_addr = ip;
 	info.si_addr = ip;
 	/*
 	/*
 	 * (~cwd & swd) will mask out exceptions that are not set to unmasked
 	 * (~cwd & swd) will mask out exceptions that are not set to unmasked
@@ -689,34 +688,31 @@ void math_error(void __user *ip)
 	 */
 	 */
 	cwd = get_fpu_cwd(task);
 	cwd = get_fpu_cwd(task);
 	swd = get_fpu_swd(task);
 	swd = get_fpu_swd(task);
-	switch (swd & ~cwd & 0x3f) {
-	case 0x000: /* No unmasked exception */
-#ifdef CONFIG_X86_32
+
+	err = swd & ~cwd & 0x3f;
+
+#if CONFIG_X86_32
+	if (!err)
 		return;
 		return;
 #endif
 #endif
-	default: /* Multiple exceptions */
-		break;
-	case 0x001: /* Invalid Op */
+
+	if (err & 0x001) {	/* Invalid op */
 		/*
 		/*
 		 * swd & 0x240 == 0x040: Stack Underflow
 		 * swd & 0x240 == 0x040: Stack Underflow
 		 * swd & 0x240 == 0x240: Stack Overflow
 		 * swd & 0x240 == 0x240: Stack Overflow
 		 * User must clear the SF bit (0x40) if set
 		 * User must clear the SF bit (0x40) if set
 		 */
 		 */
 		info.si_code = FPE_FLTINV;
 		info.si_code = FPE_FLTINV;
-		break;
-	case 0x002: /* Denormalize */
-	case 0x010: /* Underflow */
-		info.si_code = FPE_FLTUND;
-		break;
-	case 0x004: /* Zero Divide */
+	} else if (err & 0x004) { /* Divide by Zero */
 		info.si_code = FPE_FLTDIV;
 		info.si_code = FPE_FLTDIV;
-		break;
-	case 0x008: /* Overflow */
+	} else if (err & 0x008) { /* Overflow */
 		info.si_code = FPE_FLTOVF;
 		info.si_code = FPE_FLTOVF;
-		break;
-	case 0x020: /* Precision */
+	} else if (err & 0x012) { /* Denormal, Underflow */
+		info.si_code = FPE_FLTUND;
+	} else if (err & 0x020) { /* Precision */
 		info.si_code = FPE_FLTRES;
 		info.si_code = FPE_FLTRES;
-		break;
+	} else {
+		info.si_code = __SI_FAULT|SI_KERNEL; /* WTF? */
 	}
 	}
 	force_sig_info(SIGFPE, &info, task);
 	force_sig_info(SIGFPE, &info, task);
 }
 }