# HG changeset patch # User Taylor R Campbell # Date 1745590724 0 # Fri Apr 25 14:18:44 2025 +0000 # Branch trunk # Node ID 3ffac25e9902c52a2fc030f5860ecbbbb9ca44d8 # Parent fa66a8de28195fb9d8985a1c997f6a4b3fd8da4d # EXP-Topic riastradh-pr56820-softfloatfpexcsig libc softfloat: Make SIGFPE for trapped fp exceptions non-ignorable. In hardfloat, when the kernel delivers SIGFPE for a floating-point exception trap, if SIGFPE is masked or ignored, it is handled _as if_ SIGFPE were unmasked and had the default signal disposition: 964 /* 965 * If the signal is masked or ignored, then unmask it and 966 * reset it to the default action so that the process or 967 * its tracer will be notified. 968 */ 969 const bool ignored = action == SIG_IGN; 970 if (masked || ignored) { 971 mutex_enter(&ps->sa_mutex); 972 sigdelset(mask, signo); 973 sigdelset(&p->p_sigctx.ps_sigcatch, signo); 974 sigdelset(&p->p_sigctx.ps_sigignore, signo); 975 sigdelset(&SIGACTION_PS(ps, signo).sa_mask, signo); 976 SIGACTION_PS(ps, signo).sa_handler = SIG_DFL; 977 mutex_exit(&ps->sa_mutex); 978 } https://nxr.netbsd.org/xref/src/sys/kern/kern_sig.c?r=1.410#964 In other words, if you have asked for floating-point exception traps via fpsetmask(3) or feeenableexcept(3), then you can't simply defer or ignore them at the signal level any more than you can defer or ignore SIGBUS or SIGSEGV. And if the signal handler returns, it will restart the instruction and -- if nothing has changed in the set of trapped floating-point exceptions -- trap again, delivering the same signal again. Since we can't lock access to signal dispositions, I don't think we can guarantee the same effect from userland atomically, but we can get a close approximation (should be good enough for single-threaded programs, at least) by mimicking this logic and calling sigqueueinfo in a loop. PR misc/56820: Many FPE related tests fail on softfloat machines diff -r fa66a8de2819 -r 3ffac25e9902 lib/libc/softfloat/softfloat-specialize --- a/lib/libc/softfloat/softfloat-specialize Thu Apr 24 18:37:59 2025 +0000 +++ b/lib/libc/softfloat/softfloat-specialize Fri Apr 25 14:18:44 2025 +0000 @@ -36,6 +36,8 @@ this code that are retained. #include #include +#include "reentrant.h" + /* ------------------------------------------------------------------------------- Underflow tininess-detection mode, statically initialized to default value. @@ -64,20 +66,75 @@ should be simply `float_exception_flags fp_except float_exception_mask = 0; #endif void -float_raise( fp_except flags ) +float_raise( fp_except newflags ) { siginfo_t info; - fp_except mask = float_exception_mask; + struct sigaction sa; + sigset_t sigmask, osigmask; + fp_except flags; + for (;;) { #ifdef set_float_exception_mask - flags |= set_float_exception_flags(flags, 0); + flags = newflags | set_float_exception_flags(newflags, 0); #else - float_exception_flags |= flags; - flags = float_exception_flags; + float_exception_flags |= newflags; + flags = float_exception_flags; #endif - flags &= mask; - if ( flags ) { + /* + * If none of the sticky flags are trapped (i.e., enabled in + * float_exception_mask), we're done. + */ + flags &= float_exception_mask; + if (flags == 0) + break; + + /* + * Block all signals while we figure out how to deliver + * a non-maskable (as a signal), non-ignorable SIGFPE, and + * obtain the current signal mask. + */ + sigfillset(&sigmask); + thr_sigsetmask(SIG_BLOCK, &sigmask, &osigmask); + + /* + * Find the current signal disposition of SIGFPE. + */ + sigaction(SIGFPE, NULL, &sa); + + /* + * If SIGFPE is masked or ignored, unmask it and reset + * it to the default disposition to deliver the signal. + */ + if (sigismember(&osigmask, SIGFPE) || + ((sa.sa_flags & SA_SIGINFO) == 0 && + sa.sa_handler == SIG_IGN)) { + /* + * Prepare to unmask SIGFPE. This will take + * effect when we use thr_sigsetmask(SIG_SETMASK, + * ...) below, once the signal has been queued, + * so that it happens atomically with respect + * to other signal delivery. + */ + sigdelset(&osigmask, SIGFPE); + + /* + * Reset SIGFPE to the default disposition, + * which is to terminate the process. + */ + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(SIGFPE, &sa, NULL); + } + + /* + * Queue the signal for delivery. It won't trigger the + * signal handler yet, because it's still masked, but + * as soon as we unmask it either the process will + * terminate or the signal handler will be called. + */ memset(&info, 0, sizeof info); info.si_signo = SIGFPE; info.si_pid = getpid(); @@ -93,6 +150,17 @@ float_raise( fp_except flags ) else if (flags & float_flag_inexact) info.si_code = FPE_FLTRES; sigqueueinfo(getpid(), &info); + + /* + * Restore the old signal mask, except with SIGFPE + * unmasked even if it was masked before. + * + * At this point, either the process will terminate (if + * SIGFPE had or now has the default disposition) or + * the signal handler will be called (if SIGFPE had a + * non-default, non-ignored disposition). + */ + thr_sigsetmask(SIG_SETMASK, &osigmask, NULL); } } #undef float_exception_mask