https://gcc.gnu.org/g:404fb73f95f44a2930066c7948c983d3f58572da

commit r17-5661-g404fb73f95f44a2930066c7948c983d3f58572da
Author: Tomasz KamiƄski <[email protected]>
Date:   Tue Oct 27 13:37:03 2026 +0100

    libstdc++: Fix inplace_vector capacity checks for oversized ranges.
    
    The inplace_vector's _S_distance check, was unconditionally casting
    range size into the size_type. For oversized ranges, whose difference_type
    and is larger size_t, this trims the value, and may lead to false acceptance
    of range of size that is greater than capacity.
    
    This patch, addresses the above, by replacing above with S_distance_if_fits
    function, that also checks capacity. The checks are performed by 
_S_check_size
    and _S_check_distance functions. The former is templated on integer type,
    and performs the cast only after verifying that range size fits in the
    capacity (and thus in size_type).
    
    For _S_check_distance, stops iterating the range after capacity() - size()
    elements, instead of calling std::distance. The change is also applied
    to the range overloads, via newly defined _S_distance_if_fits overload.
    
    Finally, the runtime if on _S_distance (false_type was returned in case
    of input iterators), is replaced with the if constexpr. Previously both
    branches were emitted for forward (or stronger iterator), as else branch
    was triggered for ranges of size zero. The constraints are extracted to
    new __any_sizeable_iterator concept (mirroring __any_input_iterator).
    
    libstdc++-v3/ChangeLog:
    
            * include/bits/stl_iterator_base_types.h (__any_sizeable_iterator):
            Define.
            * include/std/inplace_vector (inplace_vector::_S_reserve):
            Replace with...
            (inplace_vector::_S_check_size, inplace_vector::_S_check_distance):
            Define.
            (inplace_vector::_S_distance): Replace with..
            (inplace_vector::_S_distance_if_fits): Rework _S_distance with
            integrated bounds checking, and overloaded for rangs.
            (inplace_vector::reserve, inplace_vector::resize): Replace
            _S_reserve with _S_check_size.
            (inplace_vector::inplace_vector, inplace_vector::assign): Replace
            runtime check with if constexpr and use _S_distance_if_fits.
            (inplace_vector::insert): Likewise, and skip rotate for insertion
            at the end.
            (inplace_vector::append_range): Use _S_distance_if_fits to compute
            size.
            * 
testsuite/23_containers/inplace_vector/modifiers/capacity_check.cc:
            New test.
    
    Reviewed-by: Jonathan Wakely <[email protected]>

Diff:
---
 .../include/bits/stl_iterator_base_types.h         |   7 +
 libstdc++-v3/include/std/inplace_vector            | 123 ++++----
 .../inplace_vector/modifiers/capacity_check.cc     | 315 +++++++++++++++++++++
 3 files changed, 393 insertions(+), 52 deletions(-)

diff --git a/libstdc++-v3/include/bits/stl_iterator_base_types.h 
b/libstdc++-v3/include/bits/stl_iterator_base_types.h
index f2abd28e2417..caa930219327 100644
--- a/libstdc++-v3/include/bits/stl_iterator_base_types.h
+++ b/libstdc++-v3/include/bits/stl_iterator_base_types.h
@@ -267,6 +267,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _InIterator>
     concept __any_input_iterator
       = input_iterator<_InIterator> || __has_input_iter_cat<_InIterator>;
+
+  // Is a Cpp17ForwardIterator or satisfies std::forward_iterator or 
sized_sentinel_for
+  template<typename _InIterator>
+    concept __any_sizeable_iterator
+      = forward_iterator<_InIterator>
+       || is_convertible_v<__iter_category_t<_InIterator>, 
forward_iterator_tag>
+       || sized_sentinel_for<_InIterator, _InIterator>;
 #endif
 #endif
 
diff --git a/libstdc++-v3/include/std/inplace_vector 
b/libstdc++-v3/include/std/inplace_vector
index b16edcb98d9a..b5253f77e7b8 100644
--- a/libstdc++-v3/include/std/inplace_vector
+++ b/libstdc++-v3/include/std/inplace_vector
@@ -86,7 +86,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       inplace_vector(size_type __n)
       {
        _M_init();
-       _S_reserve(__n);
+       _S_check_size(__n);
        std::uninitialized_value_construct_n(data(), __n);
        _M_size = __n;
       }
@@ -95,7 +95,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       inplace_vector(size_type __n, const _Tp& __value)
       {
        _M_init();
-       _S_reserve(__n);
+       _S_check_size(__n);
        std::uninitialized_fill_n(data(), __n, __value);
        _M_size = __n;
       }
@@ -105,9 +105,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
        inplace_vector(_InputIterator __first, _InputIterator __last)
        : inplace_vector()
        {
-         if (const auto __n = _S_distance(__first, __last))
+         if constexpr (__any_sizeable_iterator<_InputIterator>)
            {
-             _S_reserve(__n);
+             const size_type __n = _S_distance_if_fits(0, __first, __last);
              std::uninitialized_copy(__first, __last, data());
              _M_size = __n;
            }
@@ -135,7 +135,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       inplace_vector(initializer_list<_Tp> __il)
       {
        _M_init();
-       _S_reserve(__il.size());
+       _S_check_size(__il.size());
        std::uninitialized_copy(__il.begin(), __il.end(), data());
        _M_size = __il.size();
       }
@@ -220,9 +220,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
        constexpr void
        assign(_InputIterator __first, _InputIterator __last)
        {
-         if (const auto __n = _S_distance(__first, __last))
+         if constexpr (__any_sizeable_iterator<_InputIterator>)
            {
-             _S_reserve(__n);
+             const size_type __n = _S_distance_if_fits(0, __first, __last);
              if (_M_size <= __n)
                {
                  for (size_t __i = 0; __i < _M_size; ++__i, (void)++__first)
@@ -257,11 +257,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
        {
          if constexpr (ranges::forward_range<_Rg> || ranges::sized_range<_Rg>)
            {
-             const auto __len = ranges::distance(__rg);
-             if (__len > _Nm)
-               __throw_bad_alloc();
-
-             const size_t __sz = size_t(__len);
+             const size_type __sz = _S_distance_if_fits(0, __rg);
              if (__sz <= size())
                {
                  ranges::copy_n(ranges::begin(__rg), __sz, data());
@@ -308,7 +304,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       constexpr void
       assign(size_type __n, const _Tp& __u)
       {
-       _S_reserve(__n);
+       _S_check_size(__n);
        if (_M_size <= __n)
          std::uninitialized_fill_n(std::fill_n(data(), _M_size, __u),
                                    __n - _M_size, __u);
@@ -399,7 +395,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       constexpr void
       resize(size_type __n)
       {
-       _S_reserve(__n);
+       _S_check_size(__n);
        if (__n > _M_size)
          std::uninitialized_value_construct_n(data() + _M_size, __n - _M_size);
        else if (__n < _M_size)
@@ -410,7 +406,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       constexpr void
       resize(size_type __n, const _Tp& __c)
       {
-       _S_reserve(__n);
+       _S_check_size(__n);
        if (__n > _M_size)
          std::uninitialized_fill_n(data() + _M_size, __n - _M_size, __c);
        else if (__n < _M_size)
@@ -420,7 +416,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 
       static constexpr void
       reserve(size_type __n)
-      { _S_reserve(__n); }
+      { _S_check_size(__n); }
 
       static constexpr void
       shrink_to_fit() { }
@@ -534,16 +530,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
        {
          if constexpr (ranges::forward_range<_Rg> || ranges::sized_range<_Rg>)
            {
-             const auto __len = ranges::distance(__rg);
-             if (__len > (_Nm - size()))
-               __throw_bad_alloc();
-
-             const size_t __sz = size_t(__len);
+             const size_t __sz = _S_distance_if_fits(_M_size, __rg);
              // Bounded on output range due PR121143
              ranges::uninitialized_copy(
                ranges::begin(__rg), unreachable_sentinel,
                data() + _M_size, data() + _M_size + __sz);
-             _M_size += size_type(__sz);
+             _M_size += __sz;
            }
          else
            {
@@ -654,10 +646,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
          __glibcxx_assert(__b <= _M_size);
          iterator __pos = begin() + __b;
          const size_t __s = _M_size;
-         if (const auto __n = _S_distance(__first, __last))
+         if constexpr (__any_sizeable_iterator<_InputIterator>)
            {
-             if ((_Nm - _M_size) < __n)
-               __throw_bad_alloc();
+             const size_type __n = _S_distance_if_fits(_M_size, __first, 
__last);
              std::uninitialized_copy(__first, __last, data() + _M_size);
              _M_size += __n;
            }
@@ -666,7 +657,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
              while (__first != __last)
                emplace_back(*__first++);
            }
-         if (__s)
+         if (__b < __s)
            std::rotate(__pos, begin() + __s, end());
          return __pos;
        }
@@ -679,20 +670,17 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
          const auto __end = end();
          if constexpr (ranges::forward_range<_Rg> || ranges::sized_range<_Rg>)
            {
-             const auto __len = ranges::distance(__rg);
-             if (__len > (_Nm - size()))
-               __throw_bad_alloc();
-             if (!__len) [[unlikely]]
+             const size_type __sz = _S_distance_if_fits(_M_size, __rg);
+             if (!__sz) [[unlikely]]
                return __pos;
 
-             const size_type __n = size_type(__len);
              const size_type __num_after = __end - __pos;
-             if (__num_after >= __n)
+             if (__num_after >= __sz)
                {
-                 ranges::uninitialized_move(__end - __n, __end,
+                 ranges::uninitialized_move(__end - __sz, __end,
                                             __end, unreachable_sentinel);
-                 _M_size += __n;
-                 ranges::move_backward(__pos, __end - __n, __end);
+                 _M_size += __sz;
+                 ranges::move_backward(__pos, __end - __sz, __end);
                  ranges::copy(__rg, __pos);
                }
              else if constexpr (ranges::forward_range<_Rg>)
@@ -700,9 +688,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
                  auto __mid = ranges::next(ranges::begin(__rg), __num_after);
                  ranges::uninitialized_copy(__mid, ranges::end(__rg),
                                             __end, unreachable_sentinel);
-                 _M_size += __n - __num_after;
+                 _M_size += __sz - __num_after;
                  ranges::uninitialized_move(__pos, __end,
-                                            __pos + __n, unreachable_sentinel);
+                                            __pos + __sz, 
unreachable_sentinel);
                  _M_size += __num_after;
                  ranges::copy(ranges::begin(__rg), __mid, __pos);
                }
@@ -711,7 +699,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
                  ranges::uninitialized_copy(
                    ranges::begin(__rg), ranges::end(__rg),
                    __end, unreachable_sentinel);
-                 _M_size += __n;
+                 _M_size += __sz;
                  std::rotate(__pos, __end, end());
                }
            }
@@ -875,25 +863,56 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 #endif
       }
 
-      static constexpr void
-      _S_reserve(size_t __n)
-      {
-       if (__n > _Nm)
+      template<typename _Integer>
+       static constexpr size_type
+       _S_check_size(_Integer __n, size_type __used = 0)
+       {
+         if constexpr (__detail::__is_signed_integer_like<_Integer>)
+           if (__n < 0)
+             __throw_bad_alloc();
+         if (__n > (_Nm - __used))
+           __throw_bad_alloc();
+         return static_cast<size_type>(__n);
+       }
+
+      template<typename _Iterator, typename _Sentinel>
+       static constexpr size_type
+       _S_check_distance(_Iterator __first, _Sentinel __last, size_type __used)
+       {
+         for (size_type __i = __used; __i < _Nm; ++__i)
+           if (__first == __last)
+             return __i - __used;
+           else
+             ++__first;
+
+         if (__first == __last)
+           return _Nm - __used;
+
          __throw_bad_alloc();
-      }
+       }
 
+      // Returns the size of the range if it will fit in the remaining
+      // capacity (_Nm - used), otherwise throws bad_alloc.
       template<typename _InputIterator>
-       constexpr static auto
-       _S_distance(_InputIterator __first, _InputIterator __last)
+       constexpr static size_t
+       _S_distance_if_fits(size_t __used, _InputIterator __first, 
_InputIterator __last)
+       {
+         if constexpr (sized_sentinel_for<_InputIterator, _InputIterator>)
+           return _S_check_size(__last - __first, __used);
+         else
+           return _S_check_distance(__first, __last, __used);
+       }
+
+      // See above.
+      template<typename _Rg>
+       constexpr static size_t
+       _S_distance_if_fits(size_t __used, _Rg& __rg)
        {
-         if constexpr (sized_sentinel_for<_InputIterator, _InputIterator>
-                         || forward_iterator<_InputIterator>)
-           return (size_type)ranges::distance(__first, __last);
-         else if constexpr (derived_from<__iter_category_t<_InputIterator>,
-                                    forward_iterator_tag>)
-           return (size_type)std::distance(__first, __last);
+         if constexpr (ranges::sized_range<_Rg>)
+           return _S_check_size(ranges::size(__rg), __used);
          else
-           return false_type{};
+           return _S_check_distance(ranges::begin(__rg), ranges::end(__rg),
+                                    __used);
        }
     };
 
diff --git 
a/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/capacity_check.cc
 
b/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/capacity_check.cc
new file mode 100644
index 000000000000..77829b6639cb
--- /dev/null
+++ 
b/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/capacity_check.cc
@@ -0,0 +1,315 @@
+// { dg-do run { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+#include <inplace_vector>
+
+#include <ranges>
+#include <testsuite_hooks.h>
+
+template<typename Category>
+struct iter_category_base
+{ using iterator_category = Category; };
+
+template<>
+struct iter_category_base<void>
+{ };
+
+template<typename Concept>
+struct iter_concept_base
+{ using iterator_concept = Concept; };
+
+template<>
+struct iter_concept_base<void>
+{ };
+
+template<typename DifferenceType, bool Sized = true,
+        typename Category = std::random_access_iterator_tag,
+        typename Concept = Category>
+struct iota_iterator
+: iter_category_base<Category>, iter_concept_base<Concept>
+{
+  using value_type = DifferenceType;
+  using difference_type = DifferenceType;
+  using pointer = const DifferenceType*;
+  using reference = DifferenceType;
+
+  static constexpr bool sized = Sized;
+  static inline std::size_t incrementCount = 0;
+  static inline std::size_t decrement_count = 0;
+
+  iota_iterator() = default;
+
+  constexpr explicit
+  iota_iterator(DifferenceType v)
+  : value(v)
+  { }
+
+  constexpr DifferenceType
+  operator*() const
+  { return value; }
+
+  constexpr DifferenceType
+  operator[](difference_type n) const
+  { return value + n; }
+
+  constexpr iota_iterator&
+  operator++()
+  { ++incrementCount; ++value; return *this; }
+
+  constexpr iota_iterator
+  operator++(int)
+  { ++incrementCount; auto tmp = *this; ++value; return tmp; }
+
+  constexpr iota_iterator&
+  operator--()
+  { ++decrement_count; --value; return *this; }
+
+  constexpr iota_iterator
+  operator--(int)
+  { ++decrement_count; auto tmp = *this; --value; return tmp; }
+
+  constexpr iota_iterator&
+  operator+=(difference_type n)
+  { incrementCount += n; value += n; return *this; }
+
+  constexpr iota_iterator&
+  operator-=(difference_type n)
+  { decrement_count += n; value -= n; return *this; }
+
+  friend constexpr iota_iterator
+  operator+(iota_iterator i, difference_type n)
+  { return iota_iterator(i.value + n); }
+
+  friend constexpr iota_iterator
+  operator+(difference_type n, iota_iterator i)
+  { return iota_iterator(i.value + n); }
+
+  friend constexpr iota_iterator
+  operator-(iota_iterator i, difference_type n)
+  { return iota_iterator(i.value - n); }
+
+  friend constexpr difference_type
+  operator-(iota_iterator a, iota_iterator b)
+    requires Sized
+  { return a.value - b.value; }
+
+  friend constexpr bool
+  operator==(iota_iterator lhs, iota_iterator rhs) noexcept
+  { return lhs.value == rhs.value; }
+
+  friend constexpr auto
+  operator<=>(iota_iterator lhs, iota_iterator rhs) noexcept
+  { return lhs.value <=> rhs.value; }
+
+private:
+  DifferenceType value = {};
+};
+
+template<size_t N, typename Iterator>
+constexpr void
+do_test(Iterator first, Iterator last)
+{
+  std::inplace_vector<int, N> v;
+  std::ranges::subrange<Iterator> range(first, last);
+
+  v.clear();
+  Iterator::incrementCount = 0;
+  try
+  {
+    v.assign(first, last);
+    VERIFY(false);
+  }
+  catch (std::bad_alloc const&)
+  {
+  }
+  if (Iterator::sized)
+    VERIFY( Iterator::incrementCount == 0 );
+  else
+    VERIFY( Iterator::incrementCount <= N+1 );
+
+  v.clear();
+  Iterator::incrementCount = 0;
+  try
+  {
+    v.assign_range(range);
+    VERIFY(false);
+  }
+  catch (std::bad_alloc const&)
+  {
+  }
+  if (Iterator::sized)
+    VERIFY( Iterator::incrementCount == 0 );
+  else
+    VERIFY( Iterator::incrementCount <= N+1 );
+
+  v.clear();
+  Iterator::incrementCount = 0;
+  try
+  {
+    v.insert(v.begin(), first, last);
+    VERIFY(false);
+  }
+  catch (std::bad_alloc const&)
+  {
+  }
+  if (Iterator::sized)
+    VERIFY( Iterator::incrementCount == 0 );
+  else
+    VERIFY( Iterator::incrementCount <= N+1 );
+
+  v.clear();
+  Iterator::incrementCount = 0;
+  try
+  {
+    v.insert_range(v.begin(), range);
+    VERIFY(false);
+  }
+  catch (std::bad_alloc const&)
+  {
+  }
+  if (Iterator::sized)
+    VERIFY( Iterator::incrementCount == 0 );
+  else
+    VERIFY( Iterator::incrementCount <= N+1 );
+
+  v.clear();
+  Iterator::incrementCount = 0;
+  try
+  {
+    v.append_range(range);
+    VERIFY(false);
+  }
+  catch (std::bad_alloc const&)
+  {
+  }
+  if (Iterator::sized)
+    VERIFY( Iterator::incrementCount == 0 );
+  else
+    VERIFY( Iterator::incrementCount <= N+1 );
+
+  if constexpr (N < 4)
+    return;
+
+  v = {1, 2, 3, 4};
+  Iterator::incrementCount = 0;
+  try
+  {
+    v.assign(first, last);
+    VERIFY(false);
+  }
+  catch (std::bad_alloc const&)
+  {
+  }
+  if (Iterator::sized)
+    VERIFY( Iterator::incrementCount == 0 );
+  else
+    VERIFY( Iterator::incrementCount <= N+1 );
+
+  v = {1, 2, 3, 4};
+  Iterator::incrementCount = 0;
+  try
+  {
+    v.assign_range(range);
+    VERIFY(false);
+  }
+  catch (std::bad_alloc const&)
+  {
+  }
+  if (Iterator::sized)
+    VERIFY( Iterator::incrementCount == 0 );
+  else
+    VERIFY( Iterator::incrementCount <= N+1 );
+
+  v = {1, 2, 3, 4};
+  Iterator::incrementCount = 0;
+  try
+  {
+    v.insert(v.begin(), first, last);
+    VERIFY(false);
+  }
+  catch (std::bad_alloc const&)
+  {
+  }
+  if (Iterator::sized)
+    VERIFY( Iterator::incrementCount == 0 );
+  else
+    VERIFY( Iterator::incrementCount <= N-3 );
+
+  v = {1, 2, 3, 4};
+  Iterator::incrementCount = 0;
+  try
+  {
+    v.insert_range(v.begin(), range);
+    VERIFY(false);
+  }
+  catch (std::bad_alloc const&)
+  {
+  }
+  if (Iterator::sized)
+    VERIFY( Iterator::incrementCount == 0 );
+  else
+    VERIFY( Iterator::incrementCount <= N-3 );
+
+  v = {1, 2, 3, 4};
+  Iterator::incrementCount = 0;
+  try
+  {
+    v.append_range(range);
+    VERIFY(false);
+  }
+  catch (std::bad_alloc const&)
+  {
+  }
+  if (Iterator::sized)
+    VERIFY( Iterator::incrementCount == 0 );
+  else
+    VERIFY( Iterator::incrementCount <= N-3 );
+}
+
+template<size_t N, typename DifferenceType, bool Sized,
+        typename Category, typename Concept>
+constexpr void
+test_iter()
+{
+  using Iterator = iota_iterator<DifferenceType, Sized, Category, Concept>;
+  using Limits = std::numeric_limits<DifferenceType>;
+  do_test<N>(Iterator(DifferenceType(0)), Iterator(Limits::max()));
+
+  DifferenceType v2 = (DifferenceType(1) << (Limits::digits - 1)) + 2;
+  do_test<N>(Iterator(DifferenceType(0)), Iterator(v2));
+}
+
+template<size_t N, bool Sized, typename Category, typename Concept>
+constexpr void
+test_difference()
+{
+  test_iter<N, int, Sized, Category, Concept>();
+  using over_size_t = 
std::ranges::range_difference_t<std::ranges::iota_view<std::ptrdiff_t>>;
+  test_iter<N, over_size_t, Sized, Category, Concept>();
+}
+
+template<size_t N, bool Sized, typename Category>
+constexpr void
+test_category()
+{
+  test_difference<N, Sized, Category, Category>();
+  test_difference<N, Sized, void, Category>();
+}
+
+template<size_t N>
+constexpr void
+test_size()
+{
+  test_category<N, true, std::random_access_iterator_tag>();
+  test_category<N, true, std::forward_iterator_tag>();
+  test_category<N, true, std::input_iterator_tag>();
+  test_category<N, false, std::forward_iterator_tag>();
+  test_category<N, false, std::input_iterator_tag>();
+}
+
+int main()
+{
+  test_size<0>();
+  test_size<4>();
+  test_size<10>();
+}

Reply via email to