tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: Expected behavior when returning from a SIGFPE handler



> On May 27, 2021, at 6:17 AM, Jason Thorpe <thorpej%me.com@localhost> wrote:
> 
>> 
>> On May 27, 2021, at 3:35 AM, Taylor R Campbell <campbell+netbsd-tech-kern%mumble.net@localhost> wrote:
>> 
>>> Date: Wed, 26 May 2021 19:46:57 -0700
>>> From: Jason Thorpe <thorpej%me.com@localhost>
>>> 
>>> 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



Home | Main Index | Thread Index | Old Index