[Bug c++/93934] New: Unnecessary fld of uninitialized float stack variable results in ub of valid C++ code

2020-02-25 Thread vajdaz at protonmail dot com
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93934

Bug ID: 93934
   Summary: Unnecessary fld of uninitialized float stack variable
results in ub of valid C++ code
   Product: gcc
   Version: 7.4.0
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: vajdaz at protonmail dot com
  Target Milestone: ---

Valid C++ code is compiled to assembly that seems to be buggy. Below source
code and assembly output of the compiler.

Contents of source file float.cc:

extern void foo();
extern void bar();

double func(int size, double d, bool b) {
double result;

for(int i = 0; i < size; ++i) {
if (i == 0) {
foo();
if(b) result = d;
}
else {
bar();
if(b) result = 42.0;
}
}

return b && size > 0 ? result : 7.0;
}

I assume that this is valid C++ code. On C++ level there is no way to access an
uninitialized variable. The variable result is only used as a return value if
size is greater than zero, and b is true, in which case result will be
initialized before, for sure.

Create assembly:

$ g++ -m32 -O2 -fno-pic -fno-omit-frame-pointer -S float.cc

Output is:

.file   "float.cc"
.text
.p2align 4,,15
.globl  _Z4funcidb
.type   _Z4funcidb, @function
_Z4funcidb:
.LFB0:
.cfi_startproc
pushl   %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl%esp, %ebp
.cfi_def_cfa_register 5
pushl   %edi
pushl   %esi
pushl   %ebx
subl$44, %esp
.cfi_offset 7, -12
.cfi_offset 6, -16
.cfi_offset 3, -20
movl8(%ebp), %ebx
movl20(%ebp), %eax
fldl12(%ebp)
testl   %ebx, %ebx
fstpl   -48(%ebp)
movl%eax, -36(%ebp)
jle .L2
movl%eax, %esi
xorl%edi, %edi
.p2align 4,,10
.p2align 3
.L5:
testl   %edi, %edi
je  .L16
call_Z3barv
movl%esi, %eax
testb   %al, %al
je  .L4
flds.LC0
fstpl   -32(%ebp)
.L4:
addl$1, %edi
cmpl%edi, %ebx
jne .L5
cmpb$0, -36(%ebp)
fldl-32(%ebp)
je  .L17
addl$44, %esp
popl%ebx
.cfi_remember_state
.cfi_restore 3
popl%esi
.cfi_restore 6
popl%edi
.cfi_restore 7
popl%ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.p2align 4,,10
.p2align 3
.L17:
.cfi_restore_state
fstp%st(0)
.L2:
flds.LC1
addl$44, %esp
popl%ebx
.cfi_remember_state
.cfi_restore 3
popl%esi
.cfi_restore 6
popl%edi
.cfi_restore 7
popl%ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.p2align 4,,10
.p2align 3
.L16:
.cfi_restore_state
call_Z3foov
fldl-48(%ebp)
movl%esi, %eax
testb   %al, %al
fldl-32(%ebp)
fcmovne %st(1), %st
fstp%st(1)
fstpl   -32(%ebp)
jmp .L4
.cfi_endproc
.LFE0:
.size   _Z4funcidb, .-_Z4funcidb
.section.rodata.cst4,"aM",@progbits,4
.align 4
.LC0:
.long   1109917696
.align 4
.LC1:
.long   1088421888
.ident  "GCC: (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0"
.section.note.GNU-stack,"",@progbits



Following things happen in the assembly code when size is 1 and b is true for
example.

* The value of size is stored into %ebx
* The value of b is stored into %eax and -36(%ebp), later in %esi
* The value of d is loaded into the fpu register stack and stored into
-48(%ebp)

We continue straight to the line with "je .L16" and jump to the label .L16. And
here comes the interesting part:

.L16:
.cfi_restore_state
call_Z3foov
fldl-48(%ebp)<-- loading value of d into fpu
movl%esi, %eax
testb   %al, %al <-- testing if b is true (for fcmovne below)
fldl-32(%ebp)<-- loading uninitialized stack variable result
fcmovne %st(1), %st  <-- choosing which value to put back into result
fstp%st(1)
fstpl   -32(%ebp)
jmp .L4

Why we have here a combination of two fldl instructions and a fcmovne? This
causes a possible fpu exception if the value of the uninitialized variable
named result is accidentally a SNaN value. Which means, the behavior of this
code is unpredictable.

This can be reproduced with any GCC since 4.4.7, if the -march option is set to
somethi

[Bug target/93934] Unnecessary fld of uninitialized float stack variable results in ub of valid C++ code

2020-02-26 Thread vajdaz at protonmail dot com
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93934

Zoltan Vajda  changed:

   What|Removed |Added

 Status|RESOLVED|UNCONFIRMED
 Resolution|INVALID |---

--- Comment #3 from Zoltan Vajda  ---
(In reply to Alexander Monakov from comment #2)
> fcmov can only raise an x87 fpu exception on x87 stack underflow, which
> cannot happen here.
> 
> Even if it did raise FE_INVALID for SNaNs, note that GCC does not support
> SNaNs by default; -fsignaling-nans can be specified to request that, but
> note that documentation says the support is incomplete.
> 
> No bug here afaict.

The problem is not the fcmov instruction, but the "fldl -32(%ebp)". It loads an
uninitialized value from the variable result, which may be a SNaN.

[Bug target/93934] Unnecessary fld of uninitialized float stack variable results in ub of valid C++ code

2021-10-13 Thread vajdaz at protonmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93934

--- Comment #9 from Zoltan Vajda  ---
As I understand it, it is acknowledged, that this is a bug. However, the issue
is in state NEW for a quite long time. The issue is still present in GCC 11.2.
Do you see any chances for some progress in this case?

[Bug target/93934] Unnecessary fld of uninitialized float stack variable results in ub of valid C++ code

2021-10-13 Thread vajdaz at protonmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93934

--- Comment #12 from Zoltan Vajda  ---
Using -mfpmath=sse here does not help on a 32 bit platfrom.
https://gcc.godbolt.org/z/hs1Ef6aj4
At line 31 the assembly code performs the speculative load.

[Bug target/93934] Unnecessary fld of uninitialized float stack variable results in ub of valid C++ code

2021-10-13 Thread vajdaz at protonmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93934

--- Comment #15 from Zoltan Vajda  ---
In my special case, I have an embedded realtime application with a lot of FP
atithmetic on Intel 32 bit architecture (huge and complex legacy codebase). FPU
exceptions are enabled, so loading an SNaN results in an exception. This is
intended, and we will don't want to change this configuration. In this context
the generated ASM code does result in an fld of an uninitialized local
variable, where looking on the C++ code such an access should not be possible.
If the content of the uninitialized local variable happens to be a SNaN by
accident (chances are very small, but not zero), an FPU exception happens. And
again, based on the C++ code no FPU exception should be possible (assuming d is
not an SNaN).

Here is a synthetic example that triggers the exception by "placing a bomb" on
the stack.

https://gcc.godbolt.org/z/aooex6dcT

Function place_bomb() has an effect on what happens in func(). That should not
be the case! This is all valid C++ code.

This may now accidentally happen in our application. The behavior is
unpredictable, because it depends on what previous function calls left on the
stack.

If you change "-march=i686" to "-march=i386" in the example linked above,
everything goes fine.

[Bug target/93934] Unnecessary fld of uninitialized float stack variable results in ub of valid C++ code

2021-10-14 Thread vajdaz at protonmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93934

--- Comment #19 from Zoltan Vajda  ---
(In reply to Uroš Bizjak from comment #18)
> The following patch fixes the PR, see the comment inline:
> 
> --cut here--
> diff --git a/gcc/config/i386/i386-expand.c b/gcc/config/i386/i386-expand.c
> index 6e2b7920d2b..b87490fe544 100644
> --- a/gcc/config/i386/i386-expand.c
> +++ b/gcc/config/i386/i386-expand.c
> @@ -4094,6 +4094,15 @@ ix86_expand_fp_movcc (rtx operands[])
>   && !TARGET_64BIT))
>  return false;
>  
> +  /* Disable SFmode and DFmode x87 FCMOV with trapping math
> + to prevent speculative load using FLDL/FLDS from uninitialized
> + memory location, which can contain sNaN value.  FLDL/FLDS traps
> + on sNaN, see PR93934.  */
> +
> +  if ((mode == SFmode || mode == DFmode)
> +  && flag_trapping_math)
> +return false;
> +
>/* The floating point conditional move instructions don't directly
>   support conditions resulting from a signed integer comparison.  */
>  
> --cut here--

The problem does not only apply for conditional moves! I can turn on sse, for
example.

https://gcc.godbolt.org/z/jP3Kne8T5

Then the problematic code with the conditional move disappears, but I have a
similar speculative fld problem in another situation.

.L10:
inc esi
cmp edi, esi
jne .L11
testbl, bl<= test input variable 'b'
fld QWORD PTR [ebp-24]<= load of (maybe) uninitialized 'result'
je  .L24  <= jump based on value of 'b'
add esp, 20
pop ebx
pop esi
pop edi
pop ebp
ret
.L24:
fstpst(0)
.L8:
fld QWORD PTR .LC1
add esp, 20
pop ebx
pop esi
pop edi
pop ebp
ret

[Bug target/93934] Unnecessary fld of uninitialized float stack variable results in ub of valid C++ code

2021-10-14 Thread vajdaz at protonmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93934

--- Comment #23 from Zoltan Vajda  ---
(In reply to Uroš Bizjak from comment #20)
> (In reply to jos...@codesourcery.com from comment #16)
> > I don't think this bug is anything to do with -fsignaling-nans, for the 
> > same reason as applies to bug 58416 and bug 71460.
> 
> The situation is hopeless from the beginning. Please consider this testcase:
> 
> --cut here--
> #include 
> #include 
> 
> double
> __attribute__((noinline,noipa))
> foo (double a, double b, char c)
> {
>   return c ? a : b;
> }
> 
> int main ()
> {
>   double a = __builtin_nans ("");
>   double b = 42.0;
> 
>   feclearexcept (FE_INVALID);
>   foo (a, b, 0);
>   if (fetestexcept (FE_INVALID))
> __builtin_abort ();
> 
>   return 0;
> }
> --cut here--
> 
> $ gcc -O2 -m32 -march=i686 -lm fcmov.c
> $ ./a.out 
> Aborted (core dumped)
> $ gcc -O2 -m32 -march=i386 -lm fcmov.c
> $ ./a.out 
> Aborted (core dumped)
> 
> Because the compiler generates:
> 
> foo:
> cmpb$0, 20(%esp)
> fldl12(%esp)
> fldl4(%esp)
> fcmove  %st(1), %st
> fstp%st(1)
> ret
> 
> in the former case and:
> 
> foo:
> fldl4(%esp)
> fldl12(%esp)
> cmpb$0, 20(%esp)
> jne .L4
> fstp%st(1)
> jmp .L2
> .L4:
> fstp%st(0)
> .L2:
> ret
> 
> in the later.
> 
> Since the ABI specifies the operand size on the stack, the above code will
> always trap.

There is a small but very important difference between this example and my
example.

In this example the compiler may assume that objects 'a' and 'b' both are
initialized at the beginning of the function execution (calling a function with
uninitialized input by value would be UB). Therefore you may argue that
accessing both of them is fine. If you feed foo() with SNaN through 'a', it
will always abort, independent of 'c'.

In my example there is an uninitialized object when the function execution
starts (local variable 'result') , and at C++ level there is no execution path
that would result in accessing this object before initialization. In spite of
this, at ASM level, the object is accessed before initialization. I don't see
any argument that makes this access valid. My example function aborts randomly,
independent of the input. The behavior depends on some random data on the
stack.

[Bug c++/119120] New: Unnecessary fld when assigning real or imaginary part of a complex variable

2025-03-04 Thread vajdaz at protonmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119120

Bug ID: 119120
   Summary: Unnecessary fld when assigning real or imaginary part
of a complex variable
   Product: gcc
   Version: 14.2.1
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: vajdaz at protonmail dot com
  Target Milestone: ---

Following C++ code generates the assembly underneath when compiling with -O0
for x86 architecture (32 bit).

C++ source:

#include 

void foo(const std::complex& z) {
_Complex double __t;
__real__ __t = z.real();
__imag__ __t = z.imag();
}

Generated asm code:

foo(std::complex const&):
pushl   %ebp
movl%esp, %ebp
subl$40, %esp
subl$12, %esp
pushl   8(%ebp)
callstd::complex::real[abi:cxx11]() const
addl$16, %esp
fstpl   -16(%ebp)
fldl-24(%ebp)  ; <= here an uninitialized load happens
fldl-16(%ebp)
fstpl   -40(%ebp)
fstpl   -32(%ebp)
subl$12, %esp
pushl   8(%ebp)
callstd::complex::imag[abi:cxx11]() const
addl$16, %esp
fstpl   -24(%ebp)
fldl-24(%ebp)
fldl-16(%ebp)
fstpl   -40(%ebp)
fstpl   -32(%ebp)
nop
leave
ret

At the marked position loading of an uninitialized floating point value is done
unnecessarily. This may cause an unintended FPU exception if that value
accidentally is a signaling NaN.

The expected code here would be this one (no unnecessary load/store pairs):

foo(std::complex const&):
pushl   %ebp
movl%esp, %ebp
subl$40, %esp
movl8(%ebp), %eax
movl%eax, (%esp)
callstd::complex::real() const
fldl(%eax)
fstpl   -24(%ebp)
movl8(%ebp), %eax
movl%eax, (%esp)
callstd::complex::imag() const
fldl(%eax)
fstpl   -16(%ebp)
leave
ret

And this is the code which is generated by GCC 4.5.3 and earlier. Later GCCs
generate the (in my opinion) wrong code.

This is not an academic problem. The implementation of operator /= of
std::complex has this behavior until GCC 8.5 (inclusive).