signal+siglongjmp destroys FP control word on x86

From: Håkon Bugge (hakon.bugge@scali.no)
Date: Fri May 12 2000 - 19:32:49 EST


Hi,

A signal handler is invoked with incorrect setting of the FP control word
if precision is set different than _FPU_EXTENDED on linux 2.2.x kernel on
x86. A siglongjmp() will further *pollute* the process with the incorrect
control word.

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.

Since I don't read this mailing list too often, please cc me any reponse.

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);
}

--
Håkon Bugge; VP Product Development; Scali AS;
mailto:hob@scali.no; http://www.scali.com; fax: +47 22 62 89 51;
Voice: +47 22 62 89 50; Cellular (Europe): +47 924 84 514;
Visiting Addr: Olaf Helsets vei 6, Bogerud, N-0621 Oslo, Norway;
Mail Addr:  Scali AS, Postboks 70, Bogerud, N-0621 Oslo, Norway;

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Mon May 15 2000 - 21:00:22 EST