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

            Bug ID: 94540
           Summary: stack overflow populating std::vector<very large type>
           Product: gcc
           Version: 10.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: libstdc++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: redi at gcc dot gnu.org
  Target Milestone: ---

#include <vector>

struct yooj_type { char data[20*1024*1024]; };

int main() {
  std::vector<yooj_type> v(1);
}

This dies with SIGSEGV due to a stack overflow, because a yooj_type temporary
is created on the stack in <bits/stl_uninitialized.h>:

  template<>
    struct __uninitialized_default_1<true>
    {
      template<typename _ForwardIterator>
        static void
        __uninit_default(_ForwardIterator __first, _ForwardIterator __last)
        {
          typedef typename iterator_traits<_ForwardIterator>::value_type
            _ValueType;

          std::fill(__first, __last, _ValueType());
        }
    };

We should either avoid using std::fill for large objects (for some arbitrary
meaning of large) or we should construct the first object in place and then use
that as the fill argument:

--- a/libstdc++-v3/include/bits/stl_uninitialized.h
+++ b/libstdc++-v3/include/bits/stl_uninitialized.h
@@ -549,7 +549,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          typedef typename iterator_traits<_ForwardIterator>::value_type
            _ValueType;

-         std::fill(__first, __last, _ValueType());
+         if (__first == __last)
+           return;
+
+         const _ValueType& __val
+           = *std::_Construct(std::__addressof(*__first));
+         if (++__first != __last)
+           std::fill(__first, __last, __val);
        }
     };

@@ -585,7 +591,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          typedef typename iterator_traits<_ForwardIterator>::value_type
            _ValueType;

-         return std::fill_n(__first, __n, _ValueType());
+         if (__size_to_integer(__n) <= 0)
+           return __first;
+
+         const _ValueType& __val
+           = *std::_Construct(std::__addressof(*__first));
+         return std::fill_n(++__first, __size_to_integer(__n) - 1, __val);
        }
     };

I'm concerned this will confuse the optimisers, but haven't checked yet.

Reply via email to