Skip to content

Commit

Permalink
x86: prioritize the FPU traps for the error code
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
H. Peter Anvin committed Dec 23, 2008
1 parent 55dac3a commit adf77ba
Showing 1 changed file with 15 additions and 19 deletions.
34 changes: 15 additions & 19 deletions arch/x86/kernel/traps.c
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,7 @@ void math_error(void __user *ip)
{
struct task_struct *task;
siginfo_t info;
unsigned short cwd, swd;
unsigned short cwd, swd, err;

/*
* Save the info for the exception handler and clear the error.
Expand All @@ -675,7 +675,6 @@ void math_error(void __user *ip)
task->thread.error_code = 0;
info.si_signo = SIGFPE;
info.si_errno = 0;
info.si_code = __SI_FAULT;
info.si_addr = ip;
/*
* (~cwd & swd) will mask out exceptions that are not set to unmasked
Expand All @@ -689,34 +688,31 @@ void math_error(void __user *ip)
*/
cwd = get_fpu_cwd(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;
#endif
default: /* Multiple exceptions */
break;
case 0x001: /* Invalid Op */

if (err & 0x001) { /* Invalid op */
/*
* swd & 0x240 == 0x040: Stack Underflow
* swd & 0x240 == 0x240: Stack Overflow
* User must clear the SF bit (0x40) if set
*/
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;
break;
case 0x008: /* Overflow */
} else if (err & 0x008) { /* Overflow */
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;
break;
} else {
info.si_code = __SI_FAULT|SI_KERNEL; /* WTF? */
}
force_sig_info(SIGFPE, &info, task);
}
Expand Down

0 comments on commit adf77ba

Please sign in to comment.