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