- To: bugs at gnu dot org
- Subject: libc/1739: signal+siglongjmp destroys FP control word on x86
- From: Hakon dot Bugge at scali dot com
- Date: Mon, 15 May 2000 09:57:28 -0400
>Number: 1739
>Category: libc
>Synopsis: signal+siglongjmp destroys FP control word on x86
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: libc-gnats
>State: open
>Class: sw-bug
>Submitter-Id: unknown
>Arrival-Date: Mon May 15 10:00:03 EDT 2000
>Last-Modified:
>Originator: Hakon.Bugge@scali.com
>Organization:
net
>Release: 2.1.x
>Environment:
linux 2.2.x (i.e. RH 6.x) on x86.
>Description:
A (sig)setjmp()+(sig)longjmp() does not save/restore FPU control word.
Compilers have switches for controlling the precision mode (e.g. pgicc).
If the aplication performs a (sig)longjmp() from a signal handler,
the correct FPU control word is not restored, and the program continues
execution using a different precision. Since the FPU control word cannot
be considered as "... non-volatile automatic variables in the function
calling setjmp ..." to use the ANSI C parlance, this must in my opinion
be considered a bug.
As a reference, the enclosed program work unser Solaris/x86.
A pthread application being (pthread) signalled while waiting in a
pthread_cond_timedwait() is exposed to this bug (sample program available
upon request).
This bug is very annoying if the application requires 64-bit FP precision
and is exposed to the bug, e.g., uses pthreads.
Below is a sample program demonstrating the bug.
Enjoy!
hob
---------------------------------
/*
* Author: H. Bugge, Scali AS, May 2000
*
* This program demonstrates that the Linux/x86 signal system invokes
* the signal catching routine with an incorrect FP control word.
* This is extreemly annoying if 64-bit precision is required, and the
* application uses signals, e.g. pthreads.
*
* To compile&run:
*
* gcc fpucw.c;a.out
*/
#include <assert.h>
#include <fpu_control.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
typedef void (*sighandler_t)(int);
jmp_buf env;
char *precision_mnemonic[] = {"SINGLE", "BAD VALUE", "DOUBLE", "EXTENDED" };
void print_fp_precision(char *str, int expected_prec) {
int actual_prec;
fpu_control_t cw;
_FPU_GETCW(cw);
actual_prec = cw & _FPU_EXTENDED;
printf("%-20s: %10s. %s\n",
str, precision_mnemonic[actual_prec >> 8],
(actual_prec == expected_prec ? "OK" : "ERROR")
);
}
sighandler_t catcher(int sig) {
print_fp_precision("Before siglongjmp", _FPU_DOUBLE);
siglongjmp(env, 1);
}
int main() {
fpu_control_t cw;
print_fp_precision("Beginning of main()", _FPU_EXTENDED);
/* set 64-bit FP precision */
_FPU_GETCW(cw);
cw = (cw & ~_FPU_EXTENDED) | _FPU_DOUBLE;
_FPU_SETCW(cw);
print_fp_precision("After _FP_SETCW", _FPU_DOUBLE);
signal(SIGUSR2, (sighandler_t)catcher);
print_fp_precision("Before sigsetjmp", _FPU_DOUBLE);
if (sigsetjmp(env, 1) == 0) {
print_fp_precision("After sigsetjmp", _FPU_DOUBLE);
kill(getpid(), SIGUSR2);
assert(0);
} else {
print_fp_precision("After longjmp", _FPU_DOUBLE);
}
return(0);
}
>How-To-Repeat:
gcc fpucw.c; a.ou
>Fix:
>Audit-Trail:
>Unformatted: