[prev in list] [next in list] [prev in thread] [next in thread] 

List:       netbsd-tech-kern
Subject:    Re: Expected behavior when returning from a SIGFPE handler
From:       Jason Thorpe <thorpej () me ! com>
Date:       2021-05-27 16:14:31
Message-ID: 73EEE950-9FFF-43B0-8A32-30C358BA7525 () me ! com
[Download RAW message or body]


> On May 27, 2021, at 6:17 AM, Jason Thorpe <thorpej@me.com> wrote:
> 
> > 
> > On May 27, 2021, at 3:35 AM, Taylor R Campbell \
> > <campbell+netbsd-tech-kern@mumble.net> wrote: 
> > > Date: Wed, 26 May 2021 19:46:57 -0700
> > > From: Jason Thorpe <thorpej@me.com>
> > > 
> > > The test program sets up a SIGFPE handler, and the handler make a
> > > copy of the siginfo, sets a global flag, and returns.  The program
> > > then does "1.0 / 0.0" and prints the result.  It checks to ensure
> > > that the DZE exception is set via fpgetsticky().  It then does "1.0
> > > / 0.0" again, and then verifies that the SIGFPE handler was not
> > > called a second time (because I never cleared DZE with
> > > fpsetsticky()).
> > 
> > This strikes me as wrong.
> > 
> > The status flags (fpgetsticky, fetestexcept) don't determine whether a
> > floating-point operation `signals an exception' (in the language of
> > IEEE 754-2019); they only record whether it happened in the past.
> > 
> > It seems to me that if an operation [ieee754-]signals an exception
> > that the user has asked (with fpsetmask/feenableexcept) to be trapped,
> > then it should deliver a [unix-]signal, irrespective of whether some
> > past operation already [ieee754-]signalled an exception.
> 
> I agree.  I was describing behavior the alpha port already had.  I will write a \
> unit test for this behavior (specifically round DZE), or make sure that there is \
> one that covers it already (there may be … I'm still peeling the onion on the \
> alpha port…)

Ok, circling back on this point, the behavior I described for the alpha port is also \
how amd64 behaves, which is to say "if the an exception is enabled and the \
exception's flag is already set, then the signal handler will not be called on a \
second triggering of the exceptional condition".

Consider this test program:

#include <stdio.h>
#include <setjmp.h>
#include <signal.h>
#include <ieeefp.h>
        
volatile float f_zero = 0.0;
        
siginfo_t siginfo_copy;
jmp_buf sigfpe_env;
                
volatile float f_result;
        
static void     
sigfpe_action(int sig, siginfo_t *info, void *ctx)
{
        siginfo_copy = *info;
        longjmp(sigfpe_env, 1);
}       
                
static void __noinline
divide_by_zero(void)
{       
        f_result = 1.0 / f_zero; 
}               

int
main(int argc, char *argv[])
{
        struct sigaction sigact = {
                .sa_sigaction = sigfpe_action,
                .sa_flags = SA_SIGINFO,
        };
        (void) sigaction(SIGFPE, &sigact, NULL);

        fp_except_t mask = fpgetmask();

        printf("default MASK:\n");
        if (mask & FP_X_INV)
                printf("\tFP_X_INV\n");
        if (mask & FP_X_DZ)
                printf("\tFP_X_DZ\n");
        if (mask & FP_X_OFL)
                printf("\tFP_X_OFL\n");
        if (mask & FP_X_UFL)
                printf("\tFP_X_UFL\n");
        if (mask & FP_X_IMP)
                printf("\tFP_X_IMP\n");
#ifdef FP_X_IOV
        if (mask & FP_X_IOV)
                printf("\tFP_X_IOV\n");
#endif
 
        fpsetmask(FP_X_DZ);  
        
        if (setjmp(sigfpe_env)) {
                printf("SIGFPE 1 received: signo=%d code=%d\n",
                    siginfo_copy.si_signo,
                    siginfo_copy.si_code);
        } else {
                divide_by_zero();
                printf("1: 1.0 / f_zero -> %f\n", f_result);
        }
   
        fp_except_t sticky = fpgetsticky();

        if (sticky & FP_X_DZ) {
                printf("1: GOT FP_X_DZ!\n");
        }

        if (setjmp(sigfpe_env)) {
                printf("SIGFPE 2 received: signo=%d code=%d\n",
                    siginfo_copy.si_signo,
                    siginfo_copy.si_code);
        } else {
                divide_by_zero();
                printf("2: 1.0 / f_zero -> %f\n", f_result);
        }

        sticky = fpgetsticky();
        
        if (sticky & FP_X_DZ) {
                printf("2: GOT FP_X_DZ!\n");
        }

        return 0;
}

Running this program on amd64 results in:

the-ripe-vessel:thorpej 50$ ./fptest                     
default MASK:
SIGFPE 1 received: signo=8 code=3
2: 1.0 / f_zero -> inf
2: GOT FP_X_DZ!
the-ripe-vessel:thorpej 51$ 

…which seems very counter-intuitive to me.

I enable FP_X_DZ, I get the SIGFPE signal the first time, and fpgetsticky() does NOT \
indicate FP_X_DZ … yet I do not get the second SIGFPE, and I get FP_X_DZ from the \
second call to fpgetsticky()?

WTF is going on here?

-- thorpej


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic