Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 01 Sep 2025 13:15:14 +0000
From:      bugzilla-noreply@freebsd.org
To:        bugs@FreeBSD.org
Subject:   [Bug 289232] i386 (x87) signal incorrectly reports FPE_FLTRES over FPE_FLTUND when both are present
Message-ID:  <bug-289232-227@https.bugs.freebsd.org/bugzilla/>

index | next in thread | raw e-mail

https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=289232

            Bug ID: 289232
           Summary: i386 (x87) signal incorrectly reports FPE_FLTRES over
                    FPE_FLTUND when both are present
           Product: Base System
           Version: 13.5-RELEASE
          Hardware: i386
                OS: Any
            Status: New
          Severity: Affects Many People
          Priority: ---
         Component: kern
          Assignee: bugs@FreeBSD.org
          Reporter: riverstdr@gmail.com

When an x87 floating-point operation underflows, the result is often also
inexact; so both the #U and #P flags are set in the x87 status register.

IEEE 754 prioritizes FP exception reporting with a hierarchy.  When both
UNDERFLOW and INEXACT are present, UNDERFLOW should be reported (as UNDERFLOW
implies INEXACT.)

Unfortunately, the signal handling code appears to prefer INEXACT in this case,
so that the 'si_code' handed to the signal handler indicates FPE_FLTRES instead
of FPE_FLTUND.

This makes detecting underflow in a signal handler impossible when FE_INEXACT
is allowed in conjunction with FE_UNDERFLOW. If you set the floating-pt mask
with fpeenableexcept() to be feenableexcept(FE_UNDERFLOW | FE_INEXACT); the x87
floating-pt status register reported in the machine context only seems to 
have #P set (something has cleared out #U).   But, if you set it to only
feenableexcept(FE_UNDERFLOW), the same operation correctly reports FPE_FLTUND
in si_code, and the x87 status register only has #U.

I've tried several FreeBSD versions - the problem appears to be a very old one
all the back to FreeBSD 4.5 a least.

Interestingly, when using SSE floating-point, the FPE_FLTUND is properly
reported.

Here's an example program - compile this with -m64 (using SSE regs) and the
proper FPE_FLTUND is reported, compile it with -m32 (which uses x87 operations)
and the incorrect FPE_FLTRES is reported.


#include <fenv.h>
#include <stdio.h>
#include <signal.h>
#include <math.h>
#include <unistd.h>

void sigfpe_handler(int signum, siginfo_t *info, void *context) {

    ucontext_t *uc = (ucontext_t *)context;
    int si_code = info->si_code;

    printf("Caught SIGFPE (Floating-Point Exception)!\n");
    printf("Signal info (si_code): %d", info->si_code);
    if(si_code == FPE_FLTUND) printf(": FPE_FLTUND\n");
    else if (si_code == FPE_FLTRES) printf(": FPE_FLTRES\n");
    else printf("\n");

    /* The FP state pointer is within the machine-specific context */
    /* This is the structure containing both x87 and SSE state */
    struct _fpstate *fpregs = (struct _fpstate *)uc->uc_mcontext.mc_fpstate;

    if (fpregs) {
        // The x87 status word is at the beginning of the _fpstate structure
        // The fxsave format places the FPU status word (fsw) at offset 2
        //
        // On x86-64, the mc_fpstate points to the FXSAVE area, where the
        // x87 FPU Status Word is at offset 2.
        unsigned short x87_status_word = *(unsigned short *)((char *)fpregs +
2);

        printf("x87 FPU Status Word (FSW): 0x%04x\n", x87_status_word);
        if(x87_status_word & 0x01) printf("  INVALID OP\n");
        if(x87_status_word & 0x02) printf("  DENORMAL OPERAND\n");
        if(x87_status_word & 0x04) printf("  ZERO DIVIDE\n");
        if(x87_status_word & 0x08) printf("  OVERFLOW\n");
        if(x87_status_word & 0x10) printf("  UNDERFLOW\n");
        if(x87_status_word & 0x20) printf("  INEXACT\n");
    }
    _exit(1);
}

int main() {
    struct sigaction sa;
    sa.sa_sigaction = sigfpe_handler;
    sa.sa_flags = SA_SIGINFO;
    sigaction(SIGFPE, &sa, NULL);

    // Enable traps for floating-point exceptions
    // The following code is architecture-specific.
    // It enables traps for underflow and inexact exceptions.
    // The underflow trap should have priority over the inexact trap.
    fedisableexcept(FE_INEXACT);
    feenableexcept(FE_UNDERFLOW );
    feenableexcept(FE_UNDERFLOW | FE_INEXACT); 

    double small_num = 1.0E-300;
    double large_num = 1.0E+300;

    // This operation will cause both an underflow and an inexact result.
    double result = small_num / large_num;

    printf("Result is: %e\n", result);
    return 0;
}

-- 
You are receiving this mail because:
You are the assignee for the bug.

home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?bug-289232-227>