[Bug tree-optimization/105769] [11/12/13 Regression] program segmentation fault with -ftree-vectorize and nested lambdas

jakub at gcc dot gnu.org gcc-bugzilla@gcc.gnu.org
Mon Jan 16 20:30:00 GMT 2023


https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105769

Jakub Jelinek <jakub at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |jakub at gcc dot gnu.org

--- Comment #5 from Jakub Jelinek <jakub at gcc dot gnu.org> ---
At least when using g++ 12.1.1 (20220507), the crash is because the
stack slot holding return value from jacknife is clobbered on the
bias = est(map);
line.
I see in main (well, print_cov_ratio and std::function inlined into it):
   0x00000000004016d1 <main()+274>:     lea    0x50(%rsp),%rsi
   0x00000000004016d6 <main()+279>:     lea    0x30(%rsp),%rdi
=> 0x00000000004016db <main()+284>:     call   *0x48(%rsp)
   0x00000000004016df <main()+288>:     jmp    0x401742 <main()+387>
where 0x30(%rsp) seems to be the est argument to jacknife (32 byte est_t) and
0x50(%rsp) the return value from jacknife (32 byte est_t)
(gdb) p/x $rsp+0x50
$49 = 0x7fffffffdd20
where the indirect call calls:
#0  std::_Function_handler<void (std::function<unsigned long (unsigned long)>),
jacknife<2ul, ab>(std::function<vec<2ul, ab> (std::function<unsigned long
(unsigned long)>)>, vec<2ul, vec<2ul, ab> >&, vec<2ul,
ab>&)::{lambda(std::function<unsigned long (unsigned
long)>)#1}>::_M_invoke(std::_Any_data const&, std::function<unsigned long
(unsigned long)>&&) (
    __functor=..., __args#0=...) at /usr/include/c++/12/bits/std_function.h:288
#1  0x00000000004016df in std::function<void (std::function<unsigned long
(unsigned long)>)>::operator()(std::function<unsigned long (unsigned long)>)
const (__args#0=..., 
    this=0x7fffffffdd00) at /usr/include/c++/12/bits/std_function.h:591
#2  print_cov_ratio<ab> () at /usr/src/gcc/obj/gcc/pr105769.C:85
#3  main () at /usr/src/gcc/obj/gcc/pr105769.C:121
But later in
#0  0x00000000004014f3 in jacknife<2ul, ab>(std::function<vec<2ul, ab>
(std::function<unsigned long (unsigned long)>)>, vec<2ul, vec<2ul, ab> >&,
vec<2ul, ab>&)::{lambda(std::function<unsigned long (unsigned
long)>)#1}::operator()(std::function<unsigned long (unsigned long)>) const
(map=..., __closure=0x4172c0) at /usr/src/gcc/obj/gcc/pr105769.C:59
#1  std::__invoke_impl<void, jacknife<2ul, ab>(std::function<vec<2ul, ab>
(std::function<unsigned long (unsigned long)>)>, vec<2ul, vec<2ul, ab> >&,
vec<2ul, ab>&)::{lambda(std::function<unsigned long (unsigned long)>)#1}&,
std::function<unsigned long (unsigned long)> >(std::__invoke_other,
jacknife<2ul, ab>(std::function<vec<2ul, ab> (std::function<unsigned long
(unsigned long)>)>, vec<2ul, vec<2ul, ab> >&, vec<2ul,
ab>&)::{lambda(std::function<unsigned long (unsigned long)>)#1}&,
std::function<unsigned long (unsigned long)>&&) (__f=...)
    at /usr/include/c++/12/bits/invoke.h:61
#2  std::__invoke_r<void, jacknife<2ul, ab>(std::function<vec<2ul, ab>
(std::function<unsigned long (unsigned long)>)>, vec<2ul, vec<2ul, ab> >&,
vec<2ul, ab>&)::{lambda(std::function<unsigned long (unsigned long)>)#1}&,
std::function<unsigned long (unsigned long)> >(jacknife<2ul,
ab>(std::function<vec<2ul, ab> (std::function<unsigned long (unsigned long)>)>,
vec<2ul, vec<2ul, ab> >&, vec<2ul, ab>&)::{lambda(std::function<unsigned long
(unsigned long)>)#1}&, std::function<unsigned long (unsigned long)>&&)
(__fn=...)
    at /usr/include/c++/12/bits/invoke.h:111
#3  std::_Function_handler<void (std::function<unsigned long (unsigned long)>),
jacknife<2ul, ab>(std::function<vec<2ul, ab> (std::function<unsigned long
(unsigned long)>)>, vec<2ul, vec<2ul, ab> >&, vec<2ul,
ab>&)::{lambda(std::function<unsigned long (unsigned
long)>)#1}>::_M_invoke(std::_Any_data const&, std::function<unsigned long
(unsigned long)>&&) (
    __functor=..., __args#0=...) at /usr/include/c++/12/bits/std_function.h:290
#4  0x00000000004016df in std::function<void (std::function<unsigned long
(unsigned long)>)>::operator()(std::function<unsigned long (unsigned long)>)
const (__args#0=..., 
    this=0x7fffffffdd00) at /usr/include/c++/12/bits/std_function.h:591
#5  print_cov_ratio<ab> () at /usr/src/gcc/obj/gcc/pr105769.C:85
#6  main () at /usr/src/gcc/obj/gcc/pr105769.C:121
&bias is equal to the address of the jacknife return value:
$50 = (vec<2, ab> *) 0x7fffffffdd20

To make the dumps more readable, I've patched the testcase:
--- pr105769.C~ 2023-01-16 19:05:01.000000000 +0100
+++ pr105769.C  2023-01-16 20:38:25.101524077 +0100
@@ -40,7 +40,7 @@ using sq_mat = mat<n, n, T>;
 using map_t = std::function<size_t(size_t)>;

 template<class T_v>
-using est_t = std::function<T_v(map_t map)>;
+using est_t = std::function<T_v(map_t map)>; template<class T_v> using est2_t
= std::function<T_v(map_t map)>;

 map_t id_map() {
   return [](size_t j) -> size_t {
@@ -50,7 +50,7 @@ map_t id_map() {


 template<size_t n, class T>
-est_t<void> jacknife(const est_t<vec<n, T>> est,
+est2_t<void> jacknife(const est_t<vec<n, T>> est,
     sq_mat<n, T>& cov, vec<n, T>& bias) {

   return [est, &cov, &bias](
and with that in the *.gimple dump I see:
void print_cov_ratio<ab> ()
[pr105769.C:88:1] {
  struct est2_t D.85904;
  struct est_t D.85869;
  struct ._anon_95 D.85344;
  struct map_t D.85939;
  struct sq_mat cov_jn;
  struct vec bias;
  typedef struct ._anon_95 ._anon_95;

  try
    {
      [pr105769.C:73:16] vec<2, vec<2, ab> >::vec ([pr105769.C:73:16] &cov_jn);
      [pr105769.C:74:13] vec<2, ab>::vec ([pr105769.C:74:13] &bias);
      [pr105769.C:85:23] try
        {
          try
            {
              try
                {
                  [pr105769.C:85:23] std::function<vec<2,
ab>(std::function<long unsigned int(long unsigned
int)>)>::function<print_cov_ratio<ab>()::<lambda(map_t)> > ([pr105769.C:85
                  try
                    {
                      [pr105769.C:85:23] D.85904 = jacknife<2, ab>
([pr105769.C:85:23] &D.85869, [pr105769.C:85:10] &cov_jn, [pr105769.C:85:18]
&bias); [return slot optimization]
                      try
                        {
                          try
                            {
                              [pr105769.C:85:23] D.85939 = id_map (); [return
slot optimization]
                              try
                                {
                                  [pr105769.C:85:23]
std::function<void(std::function<long unsigned int(long unsigned
int)>)>::operator() ([pr105769.C:85:23] &D.85904, [pr105769.C:85
                                }
                              finally
                                {
                                  [pr105769.C:85:23] std::function<long
unsigned int(long unsigned int)>::~function ([pr105769.C:85:23] &D.85939);
                                }
                            }
                          finally
                            {
                              [pr105769.C:85:23] D.85939 = {CLOBBER(eol)};
                            }
                        }
                      finally
                        {
                          [pr105769.C:85:23]
std::function<void(std::function<long unsigned int(long unsigned
int)>)>::~function ([pr105769.C:85:23] &D.85904);
                        }
                    }
                  finally
                    {
                      [pr105769.C:85:23] std::function<vec<2,
ab>(std::function<long unsigned int(long unsigned int)>)>::~function
([pr105769.C:85:23] &D.85869);
                    }
                }
              finally
                {
                  [pr105769.C:77:7] D.85344 = {CLOBBER(eol)};
                }
            }
          finally
            {
              [pr105769.C:85:23] D.85869 = {CLOBBER(eol)};
            }
        }
      finally
        {
          [pr105769.C:85:23] D.85904 = {CLOBBER(eol)};
        }
    }
  finally
    {
      cov_jn = {CLOBBER(eol)};
      bias = {CLOBBER(eol)};
    }
}

D.85904 above is the return value (est2_t), D.85869 is the est argument, and
bias variable is actually constructed even before this, all have 32 bytes in
size.

So, to me this looks like incorrect stack slot reuse.


More information about the Gcc-bugs mailing list