[libcxx-commits] [libcxx] [libc++] Fix shared_ptr<T[]>(Y*) rollback to use delete[] when allocation fails (PR #235691)

via libcxx-commits libcxx-commits at lists.llvm.org
Mon Oct 26 03:53:59 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Tomasz Lewandowski (tomaszlewandowskivalidas)

<details>
<summary>Changes</summary>

`shared_ptr<T[]>::shared_ptr(Y*)` held the pointer in a unique_ptr<Y> while allocating the control block. If that allocation threw, the unique_ptr called scalar delete on the array pointer. For types with a non-trivial destructor the array has a cookie, so this freed an address that was never returned by operator new[] and aborted.

Use an exception guard that calls __shared_ptr_default_delete<T, Y>, so delete[] is used for array types, as [util.smartptr.shared.const] requires.

Fixes #<!-- -->234974

---
Full diff: https://github.com/llvm/llvm-project/pull/235691.diff


2 Files Affected:

- (modified) libcxx/include/__memory/shared_ptr.h (+11-5) 
- (added) libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/shared_ptr.array.deallocation.pass.cpp (+65) 


``````````diff
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index 0db58b37a6288b..0703f3c246623c 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -300,12 +300,18 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI shared_ptr {
                               ,
                           int> = 0>
   _LIBCPP_HIDE_FROM_ABI explicit shared_ptr(_Yp* __p) : __ptr_(__p) {
-    unique_ptr<_Yp> __hold(__p);
-    typedef typename __shared_ptr_default_allocator<_Yp>::type _AllocT;
-    typedef __shared_ptr_pointer<_Yp*, __shared_ptr_default_delete<_Tp, _Yp>, _AllocT> _CntrlBlk;
-    __cntrl_ = new _CntrlBlk(__p, __shared_ptr_default_delete<_Tp, _Yp>(), _AllocT());
-    __hold.release();
+    using _Dp       = __shared_ptr_default_delete<_Tp, _Yp>;
+    using _AllocT   = typename __shared_ptr_default_allocator<_Yp>::type;
+    using _CntrlBlk = __shared_ptr_pointer<_Yp*, _Dp, _AllocT>;
+    _Dp __d{};
+    auto __guard = std::__make_exception_guard([&] { __d(__p); });
+#ifndef _LIBCPP_CXX03_LANG
+    __cntrl_ = new _CntrlBlk(__p, std::move(__d), _AllocT());
+#else
+    __cntrl_ = new _CntrlBlk(__p, __d, _AllocT());
+#endif // not _LIBCPP_CXX03_LANG
     __enable_weak_this(__p, __p);
+    __guard.__complete();
   }
 
   template <class _Yp, class _Dp, __enable_if_t<__shared_ptr_deleter_ctor_reqs_v<_Dp, _Yp, _Tp>, int> = 0>
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/shared_ptr.array.deallocation.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/shared_ptr.array.deallocation.pass.cpp
new file mode 100644
index 00000000000000..5280fa20c06890
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/shared_ptr.array.deallocation.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++1, c++14
+// UNSUPPORTED: no-exceptions
+
+// <memory>
+
+// template<class Y> explicit shared_ptr(Y* p);
+
+#include <memory>
+#include <new>
+#include <cstdlib>
+#include <cassert>
+
+#include "count_new.h"
+#include "test_macros.h"
+
+// virtual destructor forces the compiler to emit an array cookie
+struct ElemVirtualDtor {
+  int v                      = 0;
+  virtual ~ElemVirtualDtor() = default;
+};
+
+// non-trivial destructor forces the compiler to emit an array cookie
+struct ElemNonTrivialDtor {
+  int v = 0;
+  ~ElemNonTrivialDtor() { v = 42; }
+};
+
+// custom `operator delete[](void* p, std::size_t s)` forces the compiler to emit an array cookie
+struct ElemWithDeleter {
+  int v = 0;
+  // a class-specific sized operator delete[] also requires an array cookie.
+  static void operator delete[](void* p, std::size_t s) noexcept { ::operator delete[](p, s); }
+};
+
+template <typename T, std::size_t N>
+void run() {
+  globalMemCounter.reset();
+  T* arr = new T[N];
+  assert(globalMemCounter.checkOutstandingArrayNewEq(1));
+  globalMemCounter.throw_after = 0;
+
+  try {
+    std::shared_ptr<T[]> sp(arr);
+    assert(false);
+  } catch (const std::bad_alloc&) {
+    assert(globalMemCounter.checkOutstandingNewEq(0));
+    assert(globalMemCounter.outstanding_array_new == 0);
+  }
+}
+
+int main(int, char**) {
+  constexpr std::size_t N = 10;
+  run<ElemVirtualDtor, N>();
+  run<ElemNonTrivialDtor, N>();
+  run<ElemWithDeleter, N>();
+  return 0;
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/235691


More information about the libcxx-commits mailing list