On Mon, Sep 8, 2025 at 2:45 PM Jonathan Wakely <jwak...@redhat.com> wrote:

> This adds checks when incrementing the shared count and weak count and
> will trap if they would be
> be incremented past its maximum. The maximum value is the value at which
> incrementing it produces an invalid use_count(). So that is either the
> maximum positive value of _Atomic_word, or for targets where we now
> allow the counters to wrap around to negative values, the "maximum"
> value is -1, because that is the value at which one more increment
> overflows the usable range and resets the counter to zero.
>
> libstdc++-v3/ChangeLog:
>
>         PR libstdc++/71945
>         * include/bits/shared_ptr_base.h (_Sp_counted_base::_S_chk):
>         Trap if a reference count cannot be incremented any higher.
>         (_Sp_counted_base::_M_add_ref_copy): Use _S_chk.
>         (_Sp_counted_base::_M_add_weak_ref): Likewise.
>         (_Sp_counted_base<_S_mutex>::_M_add_ref_lock_nothrow): Likewise.
>         (_Sp_counted_base<_S_atomic>::_M_add_ref_lock_nothrow): Likewise.
>         (_Sp_counted_base<_S_single>::_M_add_ref_copy): Use _S_chk.
> ---
>
> v2: The original patch series only had two patches, but the
> implementation of trapping on counter overflow has been split out from
> PATCH 2/2 into a third patch.
>
If the use of max in _M_weak_add_ref is simple type, then this is OK for
trunk
with the change. Otherwise, I think I do not understand the patches.

>
> We should decide whether we want to do these checks and traps
> unconditionally, or only for _GLIBCXX_ASSERTIONS.
>
> Tested powerpc64le-linux.
>
>  libstdc++-v3/include/bits/shared_ptr_base.h | 51 +++++++++++++++++++--
>  1 file changed, 47 insertions(+), 4 deletions(-)
>
> diff --git a/libstdc++-v3/include/bits/shared_ptr_base.h
> b/libstdc++-v3/include/bits/shared_ptr_base.h
> index 2ea53ae15998..a636e3e30ba5 100644
> --- a/libstdc++-v3/include/bits/shared_ptr_base.h
> +++ b/libstdc++-v3/include/bits/shared_ptr_base.h
> @@ -148,7 +148,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        // Increment the use count (used when the count is greater than
> zero).
>        void
>        _M_add_ref_copy()
> -      { __gnu_cxx::__atomic_add_dispatch(&_M_use_count, 1); }
> +      { _S_chk(__gnu_cxx::__exchange_and_add_dispatch(&_M_use_count, 1));
> }
>
>        // Increment the use count if it is non-zero, throw otherwise.
>        void
> @@ -200,7 +200,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        // Increment the weak count.
>        void
>        _M_weak_add_ref() noexcept
> -      { __gnu_cxx::__atomic_add_dispatch(&_M_weak_count, 1); }
> +      {
> +       // _M_weak_count can always use negative values because it cannot
> be
> +       // observed by users (unlike _M_use_count). See _S_chk for details.
> +       constexpr _Atomic_word __max
> +         = __gnu_cxx::__int_traits<_Atomic_word>::__max;
>
Should this be -1, the comment says we can always use negative values for
_M_weak_count.

> +
> +       if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count, 1) ==
> __max)
> +         [[__unlikely__]] __builtin_trap();
> +      }
>
>        // Decrement the weak count.
>        void
> @@ -238,6 +246,35 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        _Sp_counted_base(_Sp_counted_base const&) = delete;
>        _Sp_counted_base& operator=(_Sp_counted_base const&) = delete;
>
> +      // Called when incrementing _M_use_count to cause a trap on
> overflow.
> +      // This should be passed the value of the counter before the
> increment.
> +      static void
> +      _S_chk(_Atomic_word __count)
> +      {
> +       // __max is the maximum allowed value for the shared reference
> count.
> +       // All valid reference count values need to fit into [0,LONG_MAX)
> +       // because users can observe the count via shared_ptr::use_count().
> +       //
> +       // When long is wider than _Atomic_word, _M_use_count can go
> negative
> +       // and the cast in _Sp_counted_base::use_count() will turn it into
> a
> +       // positive value suitable for returning to users. The
> implementation
> +       // only cares whether _M_use_count reaches zero after a decrement,
> +       // so negative values are not a problem internally.
> +       // So when possible, use -1 for __max (incrementing past that would
> +       // overflow _M_use_count to 0, which means an empty shared_ptr).
> +       //
> +       // When long is not wider than _Atomic_word, __max is just the
> type's
> +       // maximum positive value. We cannot use negative counts because
> they
> +       // would not fit in [0,LONG_MAX) after casting to an unsigned type,
> +       // which would cause use_count() to return bogus values.
> +       constexpr _Atomic_word __max
> +         = sizeof(long) > sizeof(_Atomic_word)
> +             ? -1 : __gnu_cxx::__int_traits<_Atomic_word>::__max;
> +
> +       if (__count == __max) [[__unlikely__]]
> +         __builtin_trap();
> +      }
> +
>        _Atomic_word  _M_use_count;     // #shared
>        _Atomic_word  _M_weak_count;    // #weak + (#shared != 0)
>      };
> @@ -248,7 +285,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>    template<>
>      inline void
>      _Sp_counted_base<_S_single>::_M_add_ref_copy()
> -    { __gnu_cxx::__atomic_add_single(&_M_use_count, 1); }
> +    {
> +      _S_chk(_M_use_count);
> +      __gnu_cxx::__atomic_add_single(&_M_use_count, 1);
> +    }
>
>    template<>
>      inline void
> @@ -284,7 +324,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>      _M_add_ref_lock_nothrow() noexcept
>      {
>        __gnu_cxx::__scoped_lock sentry(*this);
> -      if (__gnu_cxx::__exchange_and_add_dispatch(&_M_use_count, 1) == 0)
> +      if (auto __c =
> __gnu_cxx::__exchange_and_add_dispatch(&_M_use_count, 1))
> +       _S_chk(__c);
> +      else
>         {
>           // Count was zero, so we cannot lock it to get a shared_ptr.
>           // Reset to zero. This isn't racy, because there are no
> shared_ptr
> @@ -314,6 +356,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        while (!__atomic_compare_exchange_n(&_M_use_count, &__count,
> __count + 1,
>                                           true, __ATOMIC_ACQ_REL,
>                                           __ATOMIC_RELAXED));
> +      _S_chk(__count);
>        return true;
>      }
>
> --
> 2.51.0
>
>

Reply via email to