Bug 83860 - valarray replacement type breaks with auto and more than one operation
Summary: valarray replacement type breaks with auto and more than one operation
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: libstdc++ (show other bugs)
Version: 7.0
: P2 normal
Target Milestone: 9.0
Assignee: Jonathan Wakely
URL:
Keywords: patch, wrong-code
Depends on:
Blocks:
 
Reported: 2018-01-15 16:07 UTC by Bruno Manganelli
Modified: 2018-05-02 16:48 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail: 6.4.1, 7.2.1, 8.1.0
Last reconfirmed: 2018-01-16 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Bruno Manganelli 2018-01-15 16:07:58 UTC
The following code results in a segmentation fault:

int main()
{
    std::valarray<int> va(16), vb(16), vc(16);

    auto sum = va + vb + vc;
    
    valarray<int> vsum = sum;
}

If one of the 3 operands is removed from the sum, the code appears to work.
Comment 1 Richard Biener 2018-01-16 09:04:56 UTC
Confirmed.  Works with GCC 5, requires -O1+ to segfault.  Doesn't segfault when using clang++.

#include <valarray>
int main()
{
  std::valarray<int> va(16), vb(16), vc(16);
  auto sum = va + vb + vc;
  std::valarray<int> vsum = sum;
}
Comment 2 Bruno Manganelli 2018-01-16 15:16:11 UTC
Just to clarify, it still segfaults when using clang++ with libstdc++.
Comment 3 Richard Biener 2018-01-25 08:25:47 UTC
GCC 7.3 is being released, adjusting target milestone.
Comment 4 Jonathan Wakely 2018-03-27 16:09:39 UTC
(In reply to Richard Biener from comment #1)
> Confirmed.  Works with GCC 5

Well, it doesn't segfault, but ASan still shows the problem:



ASAN:SIGSEGV
=================================================================
==8343==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000018 (pc 0x0000004017e9 bp 0x7ffc01d53a90 sp 0x7ffc01d53a80 T0)
    #0 0x4017e8 in std::valarray<int>::operator[](unsigned long) const /home/jwakely/gcc/5.5.0/include/c++/5.5.0/valarray:571
    #1 0x401792 in std::_BinBase<std::__plus, std::valarray<int>, std::valarray<int> >::operator[](unsigned long) const /home/jwakely/gcc/5.5.0/include/c++/5.5.0/bits/valarray_before.h:534
    #2 0x4016b5 in std::_BinBase<std::__plus, std::_BinClos<std::__plus, std::_ValArray, std::_ValArray, int, int>, std::valarray<int> >::operator[](unsigned long) const /home/jwakely/gcc/5.5.0/include/c++/5.5.0/bits/valarray_before.h:534
    #3 0x4015ea in std::_Expr<std::_BinClos<std::__plus, std::_Expr, std::_ValArray, std::_BinClos<std::__plus, std::_ValArray, std::_ValArray, int, int>, int>, int>::operator[](unsigned long) const /home/jwakely/gcc/5.5.0/include/c++/5.5.0/bits/valarray_after.h:216
    #4 0x4013fd in void std::__valarray_copy_construct<int, std::_BinClos<std::__plus, std::_Expr, std::_ValArray, std::_BinClos<std::__plus, std::_ValArray, std::_ValArray, int, int>, int> >(std::_Expr<std::_BinClos<std::__plus, std::_Expr, std::_ValArray, std::_BinClos<std::__plus, std::_ValArray, std::_ValArray, int, int>, int>, int> const&, unsigned long, std::_Array<int>) /home/jwakely/gcc/5.5.0/include/c++/5.5.0/bits/valarray_array.tcc:219
    #5 0x4011a9 in std::valarray<int>::valarray<std::_BinClos<std::__plus, std::_Expr, std::_ValArray, std::_BinClos<std::__plus, std::_ValArray, std::_ValArray, int, int>, int> >(std::_Expr<std::_BinClos<std::__plus, std::_Expr, std::_ValArray, std::_BinClos<std::__plus, std::_ValArray, std::_ValArray, int, int>, int>, int> const&) /home/jwakely/gcc/5.5.0/include/c++/5.5.0/valarray:696
    #6 0x400d1c in main /tmp/val.cc:6
    #7 0x7f025472a889 in __libc_start_main (/lib64/libc.so.6+0x20889)
    #8 0x400ae9 in _start (/tmp/val5+0x400ae9)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /home/jwakely/gcc/5.5.0/include/c++/5.5.0/valarray:571 std::valarray<int>::operator[](unsigned long) const
==8343==ABORTING


The root cause is that binary operators for valarray return expression templates that have reference semantics, not value semantics. The result of va+vb+vc refers to a temporary object that goes out of scope at the end of the full expression. When that result is stored in a valarray it works OK:

  std::valarray<int> sum = va + vb + vc;

but when you use 'auto' you get an object of the expression template type, which holds a dangling reference to an expired temporary.

Solution: Don't use 'auto' with expression templates.

I'll see if there's anything the library can do to make this less error-prone.
Comment 5 Jonathan Wakely 2018-04-05 16:37:06 UTC
I have a patch.
Comment 6 Marc Glisse 2018-04-06 07:28:38 UTC
GMP's expression templates, which are based on libstdc++ valarray, have the same issue. I tried using values in GMP ( https://gmplib.org/list-archives/gmp-bugs/2014-January/003319.html ). I never committed it for lack of time, but it seemed to improve performance as well, not just safety. Somehow gcc/llvm had an easier time seeing through the expressions with (copied) values. Not sure this applies to valarray though, since the expressions have bounded depth there.

The real solution is still to avoid mixing auto with expression templates, at least until we get some version of what was once proposed as "operator auto". Maybe invent some [[warn_auto]] attribute?
Comment 7 Jonathan Wakely 2018-04-06 09:46:45 UTC
Patch: https://gcc.gnu.org/ml/gcc-patches/2018-04/msg00292.html

I'm unmarking this as a regression, since the stack-use-after-scope problem has always been there.
Comment 8 Jonathan Wakely 2018-04-06 10:49:49 UTC
N.B. compiling with -fstack-reuse=none will prevent the optimizer from reusing the stack space immediately, so the dangling references can still be used to access the temporaries after their lifetime ends.
Comment 9 Jonathan Wakely 2018-05-02 16:42:22 UTC
Author: redi
Date: Wed May  2 16:41:46 2018
New Revision: 259844

URL: https://gcc.gnu.org/viewcvs?rev=259844&root=gcc&view=rev
Log:
PR libstdc++/83860 avoid dangling references in valarray closure types

Store nested closures by value not by reference, to prevent holding
invalid references to temporaries that have been destroyed. This
changes the layout of the closure types, so change their linkage names,
but moving them to a different namespace.

	PR libstdc++/57997
	PR libstdc++/83860
	* include/bits/gslice_array.h (gslice_array): Define default
	constructor as deleted, as per C++11 standard.
	* include/bits/mask_array.h (mask_array): Likewise.
	* include/bits/slice_array.h (slice_array): Likewise.
	* include/bits/valarray_after.h (_GBase, _GClos, _IBase, _IClos): Move
	to namespace __detail.
	(_GBase::_M_expr, _IBase::_M_expr): Use _ValArrayRef for type of data
	members.
	* include/bits/valarray_before.h (_ValArrayRef): New helper for type
	of data members in closure objects.
	(_FunBase, _ValFunClos, _RefFunClos, _UnBase, _UnClos, _BinBase)
	(_BinBase2, _BinBase1, _BinClos, _SBase, _SClos): Move to namespace
	__detail.
	(_FunBase::_M_expr, _UnBase::_M_expr, _BinBase::_M_expr1)
	(_BinBase::_M_expr2, _BinBase2::_M_expr1, _BinBase1::_M_expr2)
	(_SBase::_M_expr): Use _ValArrayRef for type of data members.
	* include/std/valarray (_UnClos, _BinClos, _SClos, _GClos, _IClos)
	(_ValFunClos, _RefFunClos): Move to namespace __detail and add
	using-declarations to namespace std.
	* testsuite/26_numerics/valarray/83860.cc: New.

Added:
    trunk/libstdc++-v3/testsuite/26_numerics/valarray/83860.cc
Modified:
    trunk/libstdc++-v3/ChangeLog
    trunk/libstdc++-v3/include/bits/gslice_array.h
    trunk/libstdc++-v3/include/bits/mask_array.h
    trunk/libstdc++-v3/include/bits/slice_array.h
    trunk/libstdc++-v3/include/bits/valarray_after.h
    trunk/libstdc++-v3/include/bits/valarray_before.h
    trunk/libstdc++-v3/include/std/valarray
Comment 10 Jonathan Wakely 2018-05-02 16:48:58 UTC
The original testcase works on trunk, although it's still possible to create dangling references from valarray expressions by using `auto` (especially in functions returning `auto` or `decltype(auto)` which end up returning an expression template referring to a local valarray on the function's stack).