Closed xry111 closed 7 months ago
Edited the issue title for you (it's named after a different scientist in English expression, interestingly ;-)
The coredump contains FCSR = 0x10000, indicating an inaccurate exception while this test is designed to abort on any inaccurate exception. I'm wondering if the kernel fails to restore FCSR during context switch with some rare condition...
There seems some general stability issue on the Gentoo dev machine. Today I've seen another non-reproducible test failure in libstdc++ which should be completely unrelated to FP, and an non-reproducible build failure where the assembler fails to assemble the generated .s file in /tmp (the file seems truncated for some reason).
There seems some general stability issue on the Gentoo dev machine. Today I've seen another non-reproducible test failure in libstdc++ which should be completely unrelated to FP, and an non-reproducible build failure where the assembler fails to assemble the generated .s file in /tmp (the file seems truncated for some reason).
@xen0n could we try a latest stable release of the kernel? Currently we have "6.6.0-rc3-next-20230928" which is both some sort of outdated and an unstable reelase.
Still happening with 6.6.7-gentoo-dist.
Happy new year folks.
I can reproduce the builtin-fp-int-inexact-c2x.c failure with a high probability by running a loop
while true; do echo xx; ./builtin-fp-int-inexact-c2x.exe || break; done
when "some parts" (I assume they perform some FP rounding operations) of the GCC test suite are running in the background.
The loop will be broken in seconds (otherwise it means the background processes are not performing enough FP rounding operations, I guess). Sometimes there are only about ten lines of "xx" outputted.
FWIW the kernel is 6.7.0-rc7+.
How to reproduce:
$ cat noise.c
int main()
{
while (1) {
volatile float x = 114.514;
volatile int y = x;
}
}
$ cc noise.c -O2 -o noise
$ cat measure.c
#define _GNU_SOURCE
#include <fenv.h>
#include <stdio.h>
int main()
{
return fetestexcept(FE_INEXACT) || fetestexcept(FE_INEXACT);
}
$ cc measure.c -O2 -o measure -lm
$ ./noise & while ./measure; do echo ok; done
The while
loop will finally stop, indicating FE_INEXACT has came out from nowhere. After that remember to run killall noise
!
An interesting aspect: if I only call fetestexcept(FE_INEXACT)
once in noise.c I cannot reproduce the issue at all.
How to reproduce:
$ cat noise.c int main() { while (1) { volatile float x = 114.514; volatile int y = x; } } $ cc noise.c -O2 -o noise $ cat measure.c #define _GNU_SOURCE #include <fenv.h> #include <stdio.h> int main() { return fetestexcept(FE_INEXACT) || fetestexcept(FE_INEXACT); } $ cc measure.c -O2 -o measure -lm $ ./noise & while ./measure; do echo ok; done
The
while
loop will finally stop, indicating FE_INEXACT has came out from nowhere. After that remember to runkillall noise
!An interesting aspect: if I only call
fetestexcept(FE_INEXACT)
once in noise.c I cannot reproduce the issue at all.
And phew, it still reproduces even w/o "noise" running with some good (or bad?) luck.
To rule out Glibc I've rewritten measure
with assembly:
.globl _start
_start:
.align 4
movfcsr2gr $a0, $fcsr0
bstrpick.w $a0, $a0, 16, 16
li.w $a7, 93
syscall 0
and the issue still reproduces.
Whoa. This seems happening when execve'ing an executable from a process where FCSR is already dirty. When I run the loop
while ./measure; do; done
(measure assembled from the assembly above) in a "fresh" shell with FCSR = 0 (proven via gdb attach) the issue does not reproduce. But when I run the loop in a shell with FCSR=0x10000 (i.e. an INE already happened in this shell; again proven via gdb attach) it reproduces easily. From a clean shell it also reproduces with a wrapper messing up the FCSR first then execve the "measure" program:
#include <unistd.h>
int main()
{
volatile double x = 114.514;
volatile int y = x;
execl("./measure", "measure", NULL);
__builtin_abort();
}
It seems we are relying on SET_PERSONALITY2
to clear FCSR on execve. I'd say this is highly suspicious and it seems no other architectures (even MIPS) clears only MIPS and LoongArch clear FCSR on execve this way.
I've made this:
diff --git a/arch/loongarch/kernel/process.c b/arch/loongarch/kernel/process.c
index 767d94cce0de..caed58770650 100644
--- a/arch/loongarch/kernel/process.c
+++ b/arch/loongarch/kernel/process.c
@@ -92,6 +92,7 @@ void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp)
clear_used_math();
regs->csr_era = pc;
regs->regs[3] = sp;
+ current->thread.fpu.fcsr = 0;
}
void flush_thread(void)
With this line echo $((1.0/3)); while ./measure; do ; done
survived five minutes, but without this line the command just stopped in seconds.
I'm not sure if this is a proper fix, or just reducing the probability of a dirty FCSR after execve, or just papering over the real issue. But to me we have enough evidence to say this is a kernel bug.
It seems we are relying on
SET_PERSONALITY2
to clear FCSR on execve. I'd say this is highly suspicious and it seems ~no other architectures (even MIPS) clears~ only MIPS and LoongArch clear FCSR on execve this way.
As I've guessed, MIPS is buggy too :(.
Fixed for mainline kernel.
When bootstrapping and regtesting GCC on Gentoo, the tests builtin-fp-int-inexact.c and builtin-fp-int-inexact-c2x.c intermittently fail.
The log is not very helpful:
I don't believe this is a GCC bug (with 99% confidence), so creating an issue here instead of via GCC Bugzilla.