From: Chuck Ebbert <76306.1226@compuserve.com> i386 floating-point exception handling has a bug that can cause error code 0 to be sent instead of the proper code during signal delivery. This is caused by unconditionally checking the IS and c1 bits from the FPU status word when they are not always relevant. The IS bit tells whether an exception is a stack fault and is only relevant when the exception is IE (invalid operation.) The C1 bit determines whether a stack fault is overflow or underflow and is only relevant when IS and IE are set. This bug also exists in the 2.4 kernel and appears to be in the 2.6 x86_64 code as well. Before patch: $ ./fpsig handler: signum = 8, errno = 0, code = 0 handler: fpu cwd = 0xb40, fpu swd = 0xbaa0 After: $ ./fpsig handler: signum = 8, errno = 0, code = 6 handler: fpu cwd = 0xb40, fpu swd = 0xbaa0 2.4 also has this problem; the patch applies with offsets on 2.4.31 but I didn't test it beyond that. Patch also applies to 2.6.13-rc6-mm1 with offsets. x86-64 also looks to be affected but I have no way to test it Test program: /* i387 fp signal test */ #define _GNU_SOURCE #include #include #include #include #include __attribute__ ((aligned(4096))) unsigned char altstack[4096]; unsigned short cw = 0x0b40; /* unmask all exceptions, round up */ struct sigaction sa; stack_t ss = { .ss_sp = &altstack[2047], .ss_size = sizeof(altstack)/2, }; static void handler(int nr, siginfo_t *si, void *uc) { printf("handler: signum = %d, errno = %d, code = %d\n", si->si_signo, si->si_errno, si->si_code); printf("handler: fpu cwd = 0x%hx, fpu swd = 0x%hx\n", *(unsigned short *)&altstack[0xd84], *(unsigned short *)&altstack[0xd88]); exit(1); } int main(int argc, char * const argv[]) { sa.sa_sigaction = handler; sa.sa_flags = SA_ONSTACK | SA_SIGINFO; if (sigaltstack(&ss, 0)) perror("sigaltstack"); if (sigaction(SIGFPE, &sa, NULL)) perror("sigaction"); asm volatile ("fnclex ; fldcw %0" : : "m" (cw)); asm volatile ( /* st(1) = 3.0, st = 1.0 */ "fld1 ; fld1 ; faddp ; fld1 ; faddp ; fld1"); asm volatile ( "fdivp ; fwait"); /* 1.0 / 3.0 */ return 0; } Signed-off-by: Chuck Ebbert <76306.1226@compuserve.com> Signed-off-by: Andrew Morton --- arch/i386/kernel/traps.c | 18 ++++++++++++++++-- 1 files changed, 16 insertions(+), 2 deletions(-) diff -puN arch/i386/kernel/traps.c~i386-fix-incorrect-fp-signal-delivery arch/i386/kernel/traps.c --- devel/arch/i386/kernel/traps.c~i386-fix-incorrect-fp-signal-delivery 2005-08-22 20:26:53.000000000 -0700 +++ devel-akpm/arch/i386/kernel/traps.c 2005-08-22 20:26:53.000000000 -0700 @@ -778,7 +778,7 @@ void math_error(void __user *eip) { struct task_struct * task; siginfo_t info; - unsigned short cwd, swd; + unsigned short cwd, swd, wd; /* * Save the info for the exception handler and clear the error. @@ -803,7 +803,21 @@ void math_error(void __user *eip) */ cwd = get_fpu_cwd(task); swd = get_fpu_swd(task); - switch (((~cwd) & swd & 0x3f) | (swd & 0x240)) { + wd = swd & 0x3f & ~cwd; + /* + * If the exception is invalid operation, the IS bit is needed + * to see if it's a stack fault. + */ + if (wd & 1) + wd |= swd & 0x40; + /* + * If it's a stack fault, C1 is needed to see if it's overflow or + * underflow. + */ + if (wd & 0x40) + wd |= swd & 0x200; + + switch (wd) { case 0x000: default: break; _