NetBSD-Bugs archive

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

port-hppa/60514: libm/hppa: feholdexcept() does not clear floating-point status flags



>Number:         60514
>Category:       port-hppa
>Synopsis:       libm/hppa: feholdexcept() does not clear floating-point status flags
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    port-hppa-maintainer
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue Jul 28 17:35:00 +0000 2026
>Originator:     Mark H. (aka maki73)
>Release:        10.1
>Organization:
>Environment:
NetBSD  10.1 NetBSD 10.1 (GENERIC) #0: Mon Dec 16 13:08:11 UTC 2024  mkrepro%mkrepro.NetBSD.org@localhost:/usr/src/sys/arch/hppa/compile/GENERIC hppa
>Description:
feholdexcept() on PA-RISC/hppa NetBSD fails to clear floating-point status flags (exceptions) and instead only sets non-stop mode.

In the source src/lib/libm/arch/hppa/fenv.c feclearexcept() first shifts left by `FE_FLAGS_SHIFT` to clear status flags, whereas feholdexcept() simply performs
r &= ~FE_ALL_EXCEPT;
not shifting. This sets non-stop mode but does not clear status flags, contradicting its own comment right above the function.

Also, the existing shift logic in the file seems to be UB-adjacent (e.g., in feclearexcept() when excepts=FE_ALL_EXCEPT shift becomes FE_ALL_EXCEPT << FE_FLAGS_SHIFT, if FE_FLAGS_SHIFT is 27 and FE_ALL_EXCEPT is 31 then it's UB because 32-bit int cannot represent 31 * 2^27), but this is its own separate problem.


This also violates the C Standard
See:
https://man.netbsd.org/fenv.3
ISO/IEC 9899:2024 \S 7.6.6.2 \P 2 (or equivalently C99 \S 7.6.4.2 \P 2)
and ISO/IEC 9899:2024 \S 6.5.8 \P 4 for UB in the feclearexcept's shift 
>How-To-Repeat:
$ uname -a
NetBSD  10.1 NetBSD 10.1 (GENERIC) #0: Mon Dec 16 13:08:11 UTC 2024  mkrepro%mkrepro.NetBSD.org@localhost:/usr/src/sys/arch/hppa/compile/GENERIC hppa
$ cat reproducer.c
#include <stdio.h>
#include <stdlib.h>
#include <fenv.h>
#pragma STDC FENV_ACCESS ON

#ifndef FE_DIVBYZERO
#   error "reproducer requires availability of FE_DIVBYZERO!"
#endif

int main(void) {
    /* raise any floating-point exception
    */
    if (feraiseexcept(FE_DIVBYZERO) != 0) {
        goto fail;
    }

    /* ISO/IEC 9899:2024 \S 7.6.6.2 \P 2 (equivalently to C99 \S 7.6.4.2 \P 2)
      states that feholdexcept() function besides other duties does quote:
      ".. pointed to by envp, **clears the floating-point status flags**, and then ..."
      IEEE Std 1003.1-2017 (aka POSIX) defers to the ISO C standard here.
     */
    fenv_t env;
    if (feholdexcept(&env) != 0) {
        goto fail;
    }

    /* if the feholdexcept() is correct, this branch should
     not be taken
     */
    if (fetestexcept(FE_DIVBYZERO) != 0) {
        fprintf(stderr, "bug: feholdexcept() did not clear the floating-point status flags!\n");
        return EXIT_FAILURE;
    }

    printf("feholdexcept correctly cleared floating-point status flags.\n");
    return EXIT_SUCCESS;

fail:
    fprintf(stderr, "unexpected failure!\n");
    return EXIT_FAILURE;
}
$ cc reproducer.c -lm -o reproducer
$ ./reproducer
bug: feholdexcept() did not clear the floating-point status flags!
$ cc -lm -S reproducer.c
$ cat reproducer.s | grep "bl" # check that compiler preserved all calls
.globl main
	bl feraiseexcept,%r2
	bl feholdexcept,%r2
	bl fetestexcept,%r2
	bl fwrite,%r2
	bl puts,%r2
	bl fwrite,%r2
$

>Fix:
Fix can be done similar to as shown below:
@@ -30,6 +30,7 @@
 
 #include <assert.h>
 #include <fenv.h>
+#include <stdint.h>
 
 #ifdef __weak_alias
 __weak_alias(feclearexcept,_feclearexcept)
@@ -275,12 +276,15 @@
 feholdexcept(fenv_t *envp)
 {
 	fenv_t r;
+	uint32_t mask;
 
 	_DIAGASSERT(envp != NULL);
 
 	r = readfpsr();
 	*envp = r;
-	r &= ~FE_ALL_EXCEPT;
+	mask  = (uint32_t)FE_ALL_EXCEPT
+	mask |= mask << FE_FLAGS_SHIFT;
+	r &= ~mask;
 	writefpsr(r);
 
 	/* Success */

NOTE: Test this fix before applying




Home | Main Index | Thread Index | Old Index