[libcxx-commits] [libcxx] [libc++] Add an ABI setting to harden unique_ptr<T[]>::operator[] (PR #91798)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Fri Aug 30 06:25:06 PDT 2024


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/91798

>From ab4a062ab96332e1ed0df6d0f4f80508777d88df Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 10 May 2024 15:40:18 -0400
Subject: [PATCH 1/6] [libc++] Harden unique_ptr<T[]>::operator[] when we can

This patch adds an ABI configuration that allows bounds-checking in
unique_ptr<T[]>::operator[] when it has been constructed with bounds
information in the API.

The patch also adds support for bounds-checking when an array cookie
is known to exist, which allows validating bounds without even changing
the ABI.

Drive-by changes:
- Improve the tests for `operator[]`
- Improve the tests for `.get()`
- Add a test for incomplete types support
---
 ...-hardening-mode-fast-with-abi-breaks.cmake |   2 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__configuration/abi.h          |   7 +
 libcxx/include/__memory/array_cookie.h        |  55 ++++++++
 libcxx/include/__memory/unique_ptr.h          | 123 +++++++++++++++-
 libcxx/include/module.modulemap               |   1 +
 .../unique.ptr.class/incomplete.sh.cpp        |  93 ++++++++++++
 .../assert.subscript.pass.cpp                 | 133 ++++++++++++++++++
 .../unique.ptr.observers/get.pass.cpp         | 117 ++++++++++++---
 .../op_subscript.runtime.pass.cpp             | 124 ++++++++++++----
 10 files changed, 597 insertions(+), 59 deletions(-)
 create mode 100644 libcxx/include/__memory/array_cookie.h
 create mode 100644 libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/incomplete.sh.cpp
 create mode 100644 libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/assert.subscript.pass.cpp

diff --git a/libcxx/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake b/libcxx/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake
index c0f2bad1c95af0..f63436c7679478 100644
--- a/libcxx/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake
+++ b/libcxx/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake
@@ -1,2 +1,2 @@
 set(LIBCXX_HARDENING_MODE "fast" CACHE STRING "")
-set(LIBCXX_ABI_DEFINES "_LIBCPP_ABI_BOUNDED_ITERATORS;_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STRING;_LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR" CACHE STRING "")
+set(LIBCXX_ABI_DEFINES "_LIBCPP_ABI_BOUNDED_ITERATORS;_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STRING;_LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR;_LIBCPP_ABI_BOUNDED_UNIQUE_PTR" CACHE STRING "")
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 32579272858a8e..910501249c6786 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -528,6 +528,7 @@ set(files
   __memory/allocator_arg_t.h
   __memory/allocator_destructor.h
   __memory/allocator_traits.h
+  __memory/array_cookie.h
   __memory/assume_aligned.h
   __memory/auto_ptr.h
   __memory/builtin_new_allocator.h
diff --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h
index 8efbb42d1d8470..5078eaca97a79d 100644
--- a/libcxx/include/__configuration/abi.h
+++ b/libcxx/include/__configuration/abi.h
@@ -150,6 +150,13 @@
 // ABI impact: changes the iterator type of `vector` (except `vector<bool>`).
 // #define _LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR
 
+// Tracks the bounds of the array owned by std::unique_ptr<T[]>, allowing it to trap when accessed out-of-bounds.
+// Note that limited bounds checking is also available outside of this ABI configuration, but only some categories
+// of types can be checked.
+//
+// ABI impact: This causes the layout of std::unique_ptr<T[]> to change and its size to increase.
+// #define _LIBCPP_ABI_BOUNDED_UNIQUE_PTR
+
 #if defined(_LIBCPP_COMPILER_CLANG_BASED)
 #  if defined(__APPLE__)
 #    if defined(__i386__) || defined(__x86_64__)
diff --git a/libcxx/include/__memory/array_cookie.h b/libcxx/include/__memory/array_cookie.h
new file mode 100644
index 00000000000000..29f80df9e7c97a
--- /dev/null
+++ b/libcxx/include/__memory/array_cookie.h
@@ -0,0 +1,55 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___MEMORY_ARRAY_COOKIE_H
+#define _LIBCPP___MEMORY_ARRAY_COOKIE_H
+
+#include <__config>
+
+#include <__configuration/abi.h>
+#include <__type_traits/integral_constant.h>
+#include <__type_traits/is_trivially_destructible.h>
+#include <__type_traits/negation.h>
+#include <cstddef>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// Trait representing whether a type requires an array cookie at the start of its allocation when
+// allocated as `new T[n]` and deallocated as `delete array`.
+//
+// Under the Itanium C++ ABI [1], we know that an array cookie is available unless `T` is trivially
+// destructible and the call to `operator delete[]` is not a sized operator delete. Under ABIs other
+// than the Itanium ABI, we assume there are no array cookies.
+//
+// [1]: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#array-cookies
+#ifdef _LIBCPP_ABI_ITANIUM
+// TODO: Use a builtin instead
+// TODO: We should factor in the choice of the usual deallocation function in this determination.
+template <class _Tp>
+struct __has_array_cookie : _Not<is_trivially_destructible<_Tp> > {};
+#else
+template <class _Tp>
+struct __has_array_cookie : false_type {};
+#endif
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI size_t __get_array_cookie(_Tp const* __ptr) {
+  static_assert(
+      __has_array_cookie<_Tp>::value, "Trying to access the array cookie of a type that is not guaranteed to have one");
+  size_t const* __cookie = reinterpret_cast<size_t const*>(__ptr) - 1; // TODO: Use a builtin instead
+  return *__cookie;
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___MEMORY_ARRAY_COOKIE_H
diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h
index 7f5e0ea243c956..ea2a38eab6f8f5 100644
--- a/libcxx/include/__memory/unique_ptr.h
+++ b/libcxx/include/__memory/unique_ptr.h
@@ -17,8 +17,10 @@
 #include <__functional/hash.h>
 #include <__functional/operations.h>
 #include <__memory/allocator_traits.h> // __pointer
+#include <__memory/array_cookie.h>
 #include <__memory/auto_ptr.h>
 #include <__memory/compressed_pair.h>
+#include <__memory/pointer_traits.h>
 #include <__type_traits/add_lvalue_reference.h>
 #include <__type_traits/common_type.h>
 #include <__type_traits/conditional.h>
@@ -40,6 +42,8 @@
 #include <__utility/declval.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
+#include <__utility/private_constructor_tag.h>
+#include <climits>
 #include <cstddef>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -283,6 +287,85 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr {
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void swap(unique_ptr& __u) _NOEXCEPT { __ptr_.swap(__u.__ptr_); }
 };
 
+// Bounds checking in unique_ptr<T[]>
+// ==================================
+//
+// We provide some helper classes that allow bounds checking when accessing a unique_ptr<T[]>.
+// There are a few cases where bounds checking can be implemented:
+//
+// 1. When an array cookie (see [1]) exists at the beginning of the array allocation, we are
+//    able to reuse that cookie to extract the size of the array and perform bounds checking.
+//    An array cookie is a size inserted at the beginning of the allocation by the compiler.
+//    That size is inserted implicitly when doing `new T[n]` in some cases, and its purpose
+//    is to allow the runtime to destroy the `n` array elements when doing `delete array`.
+//    When we are able to use array cookies, we reuse information already available in the
+//    current runtime, so bounds checking does not require changing libc++'s ABI.
+//
+// 2. When the "bounded unique_ptr" ABI configuration (controlled by `_LIBCPP_ABI_BOUNDED_UNIQUE_PTR`)
+//    is enabled, we store the size of the allocation (when it is known) so we can check it when
+//    indexing into the `unique_ptr`. That changes the layout of `std::unique_ptr<T[]>`, which is
+//    an ABI break from the default configuration.
+//
+//    Note that even under this ABI configuration, we can't always know the size of the unique_ptr.
+//    Indeed, the size of the allocation can only be known when the unique_ptr is created via
+//    make_unique or a similar API. For example, it can't be known when constructed from an arbitrary
+//    pointer, in which case we are not able to check the bounds on access:
+//
+//      unique_ptr<T[], MyDeleter> ptr(new T[3]);
+//
+//    When we don't know the size of the allocation via the API used to create the unique_ptr, we
+//    try to fall back to using an array cookie when available.
+//
+//    Finally, note that when this ABI configuration is enabled, we have no choice but to always
+//    make space for a size to be stored in the unique_ptr. Indeed, while we might want to avoid
+//    storing the size when an array cookie is available, knowing whether an array cookie is available
+//    requires the type stored in the unique_ptr to be complete, while unique_ptr can normally
+//    accommodate incomplete types.
+//
+// (1) Implementation where we rely on the array cookie to know the size of the allocation, if
+//     an array cookie exists.
+struct __unique_ptr_array_bounds_stateless {
+  _LIBCPP_HIDE_FROM_ABI __unique_ptr_array_bounds_stateless() = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __unique_ptr_array_bounds_stateless(size_t) {}
+
+  template <class _Tp, __enable_if_t<__has_array_cookie<_Tp>::value, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI bool __in_bounds(_Tp* __ptr, size_t __index) const {
+    size_t __cookie = std::__get_array_cookie(__ptr);
+    return __index < __cookie;
+  }
+
+  template <class _Tp, __enable_if_t<!__has_array_cookie<_Tp>::value, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI bool __in_bounds(_Tp*, size_t) const {
+    return true; // If we don't have an array cookie, we assume the access is in-bounds
+  }
+};
+
+// (2) Implementation where we store the size in the class whenever we have it.
+//
+// Semantically, we'd need to store the size as an optional<size_t>. However, since that
+// is really heavy weight, we instead store a size_t and use SIZE_MAX as a magic value
+// meaning that we don't know the size.
+struct __unique_ptr_array_bounds_stored {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __unique_ptr_array_bounds_stored() : __size_(SIZE_MAX) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __unique_ptr_array_bounds_stored(size_t __size) : __size_(__size) {}
+
+  // Use the array cookie if there's one
+  template <class _Tp, __enable_if_t<__has_array_cookie<_Tp>::value, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI bool __in_bounds(_Tp* __ptr, size_t __index) const {
+    size_t __cookie = std::__get_array_cookie(__ptr);
+    return __index < __cookie;
+  }
+
+  // Otherwise, fall back on the stored size (if any)
+  template <class _Tp, __enable_if_t<!__has_array_cookie<_Tp>::value, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI bool __in_bounds(_Tp*, size_t __index) const {
+    return __index < __size_;
+  }
+
+private:
+  size_t __size_;
+};
+
 template <class _Tp, class _Dp>
 class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp> {
 public:
@@ -291,8 +374,9 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
   typedef typename __pointer<_Tp, deleter_type>::type pointer;
 
   // A unique_ptr contains the following members which may be trivially relocatable:
-  // - pointer : this may be trivially relocatable, so it's checked
+  // - pointer: this may be trivially relocatable, so it's checked
   // - deleter_type: this may be trivially relocatable, so it's checked
+  // - (optionally) size: this is trivially relocatable
   //
   // This unique_ptr implementation only contains a pointer to the unique object and a deleter, so there are no
   // references to itself. This means that the entire structure is trivially relocatable if its members are.
@@ -303,6 +387,15 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
 
 private:
   __compressed_pair<pointer, deleter_type> __ptr_;
+#ifdef _LIBCPP_ABI_BOUNDED_UNIQUE_PTR
+  using _BoundsChecker = __unique_ptr_array_bounds_stored;
+#else
+  using _BoundsChecker = __unique_ptr_array_bounds_stateless;
+#endif
+  _LIBCPP_NO_UNIQUE_ADDRESS _BoundsChecker __checker_;
+
+  template <class, class>
+  friend class unique_ptr; // access __checker_ in other unique_ptrs
 
   template <class _From>
   struct _CheckArrayPointerConversion : is_same<_From, pointer> {};
@@ -364,6 +457,12 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX23 explicit unique_ptr(_Pp __p) _NOEXCEPT : __ptr_(__p, __value_init_tag()) {}
 
+  // Private constructor used by make_unique & friends to pass the size that was allocated
+  template <class _Tag, class _Ptr, __enable_if_t<is_same<_Tag, __private_constructor_tag>::value, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 explicit unique_ptr(_Tag, _Ptr __ptr, size_t __size) _NOEXCEPT
+      : __ptr_(__ptr, __value_init_tag()),
+        __checker_(__size) {}
+
   template <class _Pp,
             bool _Dummy = true,
             class       = _EnableIfDeleterConstructible<_LValRefType<_Dummy> >,
@@ -397,11 +496,13 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
   _LIBCPP_HIDE_FROM_ABI unique_ptr(_Pp __p, _BadRValRefType<_Dummy> __d) = delete;
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unique_ptr(unique_ptr&& __u) _NOEXCEPT
-      : __ptr_(__u.release(), std::forward<deleter_type>(__u.get_deleter())) {}
+      : __ptr_(__u.release(), std::forward<deleter_type>(__u.get_deleter())),
+        __checker_(std::move(__u.__checker_)) {}
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unique_ptr& operator=(unique_ptr&& __u) _NOEXCEPT {
     reset(__u.release());
     __ptr_.second() = std::forward<deleter_type>(__u.get_deleter());
+    __checker_      = std::move(__u.__checker_);
     return *this;
   }
 
@@ -410,7 +511,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
             class = _EnableIfMoveConvertible<unique_ptr<_Up, _Ep>, _Up>,
             class = _EnableIfDeleterConvertible<_Ep> >
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unique_ptr(unique_ptr<_Up, _Ep>&& __u) _NOEXCEPT
-      : __ptr_(__u.release(), std::forward<_Ep>(__u.get_deleter())) {}
+      : __ptr_(__u.release(), std::forward<_Ep>(__u.get_deleter())),
+        __checker_(std::move(__u.__checker_)) {}
 
   template <class _Up,
             class _Ep,
@@ -419,6 +521,7 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unique_ptr& operator=(unique_ptr<_Up, _Ep>&& __u) _NOEXCEPT {
     reset(__u.release());
     __ptr_.second() = std::forward<_Ep>(__u.get_deleter());
+    __checker_      = std::move(__u.__checker_);
     return *this;
   }
 
@@ -436,6 +539,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
   }
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 __add_lvalue_reference_t<_Tp> operator[](size_t __i) const {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__checker_.__in_bounds(std::__to_address(__ptr_.first()), __i),
+                                        "unique_ptr<T[]>::operator[](index): index out of range");
     return __ptr_.first()[__i];
   }
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 pointer get() const _NOEXCEPT { return __ptr_.first(); }
@@ -452,6 +557,7 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 pointer release() _NOEXCEPT {
     pointer __t    = __ptr_.first();
     __ptr_.first() = pointer();
+    __checker_     = _BoundsChecker();
     return __t;
   }
 
@@ -459,6 +565,7 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void reset(_Pp __p) _NOEXCEPT {
     pointer __tmp  = __ptr_.first();
     __ptr_.first() = __p;
+    __checker_     = _BoundsChecker();
     if (__tmp)
       __ptr_.second()(__tmp);
   }
@@ -466,11 +573,15 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void reset(nullptr_t = nullptr) _NOEXCEPT {
     pointer __tmp  = __ptr_.first();
     __ptr_.first() = nullptr;
+    __checker_     = _BoundsChecker();
     if (__tmp)
       __ptr_.second()(__tmp);
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void swap(unique_ptr& __u) _NOEXCEPT { __ptr_.swap(__u.__ptr_); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void swap(unique_ptr& __u) _NOEXCEPT {
+    __ptr_.swap(__u.__ptr_);
+    std::swap(__checker_, __u.__checker_);
+  }
 };
 
 template <class _Tp, class _Dp, __enable_if_t<__is_swappable_v<_Dp>, int> = 0>
@@ -626,7 +737,7 @@ template <class _Tp>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 typename __unique_if<_Tp>::__unique_array_unknown_bound
 make_unique(size_t __n) {
   typedef __remove_extent_t<_Tp> _Up;
-  return unique_ptr<_Tp>(new _Up[__n]());
+  return unique_ptr<_Tp>(__private_constructor_tag(), new _Up[__n](), __n);
 }
 
 template <class _Tp, class... _Args>
@@ -645,7 +756,7 @@ make_unique_for_overwrite() {
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 typename __unique_if<_Tp>::__unique_array_unknown_bound
 make_unique_for_overwrite(size_t __n) {
-  return unique_ptr<_Tp>(new __remove_extent_t<_Tp>[__n]);
+  return unique_ptr<_Tp>(__private_constructor_tag(), new __remove_extent_t<_Tp>[__n], __n);
 }
 
 template <class _Tp, class... _Args>
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 13d0dce34d97e3..581edefae293ac 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1509,6 +1509,7 @@ module std_private_memory_allocator                       [system] { header "__m
 module std_private_memory_allocator_arg_t                 [system] { header "__memory/allocator_arg_t.h" }
 module std_private_memory_allocator_destructor            [system] { header "__memory/allocator_destructor.h" }
 module std_private_memory_allocator_traits                [system] { header "__memory/allocator_traits.h" }
+module std_private_memory_array_cookie                    [system] { header "__memory/array_cookie.h" }
 module std_private_memory_assume_aligned                  [system] { header "__memory/assume_aligned.h" }
 module std_private_memory_auto_ptr                        [system] { header "__memory/auto_ptr.h" }
 module std_private_memory_builtin_new_allocator           [system] {
diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/incomplete.sh.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/incomplete.sh.cpp
new file mode 100644
index 00000000000000..4a03d2bcf07bfe
--- /dev/null
+++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/incomplete.sh.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <memory>
+
+// unique_ptr
+
+// Make sure that we can form unique_ptrs to incomplete types and perform restricted
+// operations on them. This requires setting up a TU where the type is complete and
+// the unique_ptr is created and destroyed, and a TU where the type is incomplete and
+// we check that a restricted set of operations can be performed on the unique_ptr.
+
+// RUN: %{cxx} %s %{flags} %{compile_flags} -c -o %t.tu1.o -DCOMPLETE
+// RUN: %{cxx} %s %{flags} %{compile_flags} -c -o %t.tu2.o -DINCOMPLETE
+// RUN: %{cxx} %t.tu1.o %t.tu2.o %{flags} %{link_flags} -o %t.exe
+// RUN: %{exec} %t.exe
+
+#include <memory>
+#include <cassert>
+
+struct T;
+extern void use(std::unique_ptr<T>& ptr);
+extern void use(std::unique_ptr<T[]>& ptr);
+
+#ifdef INCOMPLETE
+
+void use(std::unique_ptr<T>& ptr) {
+  {
+    T* x = ptr.get();
+    assert(x != nullptr);
+  }
+  {
+    T& ref = *ptr;
+    assert(&ref == ptr.get());
+  }
+  {
+    bool engaged = static_cast<bool>(ptr);
+    assert(engaged);
+  }
+  {
+    assert(ptr == ptr);
+    assert(!(ptr != ptr));
+    assert(!(ptr < ptr));
+    assert(!(ptr > ptr));
+    assert(ptr <= ptr);
+    assert(ptr >= ptr);
+  }
+}
+
+void use(std::unique_ptr<T[]>& ptr) {
+  {
+    T* x = ptr.get();
+    assert(x != nullptr);
+  }
+  {
+    bool engaged = static_cast<bool>(ptr);
+    assert(engaged);
+  }
+  {
+    assert(ptr == ptr);
+    assert(!(ptr != ptr));
+    assert(!(ptr < ptr));
+    assert(!(ptr > ptr));
+    assert(ptr <= ptr);
+    assert(ptr >= ptr);
+  }
+}
+
+#endif // INCOMPLETE
+
+#ifdef COMPLETE
+
+struct T {}; // complete the type
+
+int main(int, char**) {
+  {
+    std::unique_ptr<T> ptr(new T());
+    use(ptr);
+  }
+
+  {
+    std::unique_ptr<T[]> ptr(new T[3]());
+    use(ptr);
+  }
+  return 0;
+}
+
+#endif // COMPLETE
diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/assert.subscript.pass.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/assert.subscript.pass.cpp
new file mode 100644
index 00000000000000..6d5bacb94a2950
--- /dev/null
+++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/assert.subscript.pass.cpp
@@ -0,0 +1,133 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <memory>
+//
+// unique_ptr<T[]>
+//
+// T& operator[](std::size_t);
+
+// This test ensures that we catch an out-of-bounds access in std::unique_ptr<T[]>::operator[]
+// when unique_ptr has the appropriate ABI configuration.
+
+#include <memory>
+#include <cstddef>
+#include <string>
+
+#include "check_assertion.h"
+
+struct MyDeleter {
+  void operator()(int* ptr) const { delete[] ptr; }
+};
+
+template <class WithCookie, class NoCookie>
+void test() {
+  // Check with a default deleter
+  {
+    // Types that have an array cookie
+    {
+      {
+        std::unique_ptr<WithCookie[]> ptr(new WithCookie[5]);
+        TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
+      }
+      {
+        std::unique_ptr<WithCookie[]> ptr = std::make_unique<WithCookie[]>(5);
+        TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
+      }
+#if TEST_STD_VER >= 20
+      {
+        std::unique_ptr<WithCookie[]> ptr = std::make_unique_for_overwrite<WithCookie[]>(5);
+        TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
+      }
+#endif
+    }
+
+    // Types that don't have an array cookie (only available under the right ABI configuration)
+#if defined(_LIBCPP_ABI_BOUNDED_UNIQUE_PTR)
+    {
+      {
+        std::unique_ptr<NoCookie[]> ptr(new NoCookie[5]);
+        TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
+      }
+      {
+        std::unique_ptr<NoCookie[]> ptr = std::make_unique<NoCookie[]>(5);
+        TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
+      }
+#  if TEST_STD_VER >= 20
+      {
+        std::unique_ptr<NoCookie[]> ptr = std::make_unique_for_overwrite<NoCookie[]>(5);
+        TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
+      }
+#  endif
+    }
+#endif // defined(_LIBCPP_ABI_BOUNDED_UNIQUE_PTR)
+  }
+
+  // Check with a custom deleter (only available under the right ABI configuration)
+#if defined(_LIBCPP_ABI_BOUNDED_UNIQUE_PTR)
+  {
+    // Types that have an array cookie
+    {
+      {
+        std::unique_ptr<WithCookie[], MyDeleter> ptr = std::make_unique<WithCookie[]>(5);
+        TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
+      }
+#  if TEST_STD_VER >= 20
+      {
+        std::unique_ptr<WithCookie[], MyDeleter> ptr = std::make_unique_for_overwrite<WithCookie[], MyDeleter>(5);
+        TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
+      }
+#  endif
+    }
+
+    // Types that don't have an array cookie
+    {
+      {
+        std::unique_ptr<NoCookie[], MyDeleter> ptr = std::make_unique<NoCookie[]>(5);
+        TEST_LIBCPP_ASSERT_FAILURE(ptr[6] = 42, "unique_ptr<T[]>::operator[](index): index out of range");
+      }
+#  if TEST_STD_VER >= 20
+      {
+        std::unique_ptr<NoCookie[], MyDeleter> ptr = std::make_unique_for_overwrite<NoCookie[], MyDeleter>(5);
+        TEST_LIBCPP_ASSERT_FAILURE(ptr[6] = 42, "unique_ptr<T[]>::operator[](index): index out of range");
+      }
+#  endif
+    }
+  }
+#endif // defined(_LIBCPP_ABI_BOUNDED_UNIQUE_PTR)
+}
+
+template <std::size_t Size>
+struct NoCookie {
+  char padding[Size];
+};
+
+template <std::size_t Size>
+struct WithCookie {
+  ~WithCookie() {}
+  char padding[Size];
+};
+
+int main(int, char**) {
+  test<WithCookie<1>, NoCookie<1>>();
+  test<WithCookie<2>, NoCookie<2>>();
+  test<WithCookie<3>, NoCookie<3>>();
+  test<WithCookie<4>, NoCookie<4>>();
+  test<WithCookie<8>, NoCookie<8>>();
+  test<WithCookie<16>, NoCookie<16>>();
+  test<WithCookie<32>, NoCookie<32>>();
+  test<WithCookie<256>, NoCookie<256>>();
+  test<std::string, int>();
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/get.pass.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/get.pass.cpp
index 3bd3788960e2a6..75c35caf1b4d45 100644
--- a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/get.pass.cpp
+++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/get.pass.cpp
@@ -10,43 +10,114 @@
 
 // unique_ptr
 
-// test get
+// pointer unique_ptr<T>::get() const noexcept;
+// pointer unique_ptr<T[]>::get() const noexcept;
 
 #include <memory>
 #include <cassert>
+#include <cstddef>
 
 #include "test_macros.h"
-#include "unique_ptr_test_helper.h"
 
-template <bool IsArray>
+template <class T>
 TEST_CONSTEXPR_CXX23 void test_basic() {
-  typedef typename std::conditional<IsArray, int[], int>::type VT;
-  typedef const VT CVT;
+  // non-const element type
   {
-    typedef std::unique_ptr<VT> U;
-    int* p = newValue<VT>(1);
-    U s(p);
-    U const& sc = s;
-    ASSERT_SAME_TYPE(decltype(s.get()), int*);
-    ASSERT_SAME_TYPE(decltype(sc.get()), int*);
-    assert(s.get() == p);
-    assert(sc.get() == s.get());
+    // non-const access
+    {
+      T* x = new T;
+      std::unique_ptr<T> ptr(x);
+      ASSERT_SAME_TYPE(decltype(ptr.get()), T*);
+      ASSERT_NOEXCEPT(ptr.get());
+      assert(ptr.get() == x);
+    }
+
+    // const access
+    {
+      T* x = new T;
+      std::unique_ptr<T> const ptr(x);
+      ASSERT_SAME_TYPE(decltype(ptr.get()), T*);
+      ASSERT_NOEXCEPT(ptr.get());
+      assert(ptr.get() == x);
+    }
+  }
+
+  // const element type
+  {
+    // non-const access
+    {
+      T* x = new T;
+      std::unique_ptr<T const> ptr(x);
+      ASSERT_SAME_TYPE(decltype(ptr.get()), T const*);
+      assert(ptr.get() == x);
+    }
+
+    // const access
+    {
+      T* x = new T;
+      std::unique_ptr<T const> const ptr(x);
+      ASSERT_SAME_TYPE(decltype(ptr.get()), T const*);
+      assert(ptr.get() == x);
+    }
+  }
+
+  // Same thing but for unique_ptr<T[]>
+  // non-const element type
+  {
+    // non-const access
+    {
+      T* x = new T[3];
+      std::unique_ptr<T[]> ptr(x);
+      ASSERT_SAME_TYPE(decltype(ptr.get()), T*);
+      ASSERT_NOEXCEPT(ptr.get());
+      assert(ptr.get() == x);
+    }
+
+    // const access
+    {
+      T* x = new T[3];
+      std::unique_ptr<T[]> const ptr(x);
+      ASSERT_SAME_TYPE(decltype(ptr.get()), T*);
+      ASSERT_NOEXCEPT(ptr.get());
+      assert(ptr.get() == x);
+    }
   }
+
+  // const element type
   {
-    typedef std::unique_ptr<CVT> U;
-    const int* p = newValue<VT>(1);
-    U s(p);
-    U const& sc = s;
-    ASSERT_SAME_TYPE(decltype(s.get()), const int*);
-    ASSERT_SAME_TYPE(decltype(sc.get()), const int*);
-    assert(s.get() == p);
-    assert(sc.get() == s.get());
+    // non-const access
+    {
+      T* x = new T[3];
+      std::unique_ptr<T const []> ptr(x);
+      ASSERT_SAME_TYPE(decltype(ptr.get()), T const*);
+      assert(ptr.get() == x);
+    }
+
+    // const access
+    {
+      T* x = new T[3];
+      std::unique_ptr<T const[]> const ptr(x);
+      ASSERT_SAME_TYPE(decltype(ptr.get()), T const*);
+      assert(ptr.get() == x);
+    }
   }
 }
 
+template <std::size_t Size>
+struct WithSize {
+  char padding[Size];
+};
+
 TEST_CONSTEXPR_CXX23 bool test() {
-  test_basic</*IsArray*/ false>();
-  test_basic<true>();
+  test_basic<char>();
+  test_basic<int>();
+  test_basic<WithSize<1> >();
+  test_basic<WithSize<2> >();
+  test_basic<WithSize<3> >();
+  test_basic<WithSize<4> >();
+  test_basic<WithSize<8> >();
+  test_basic<WithSize<16> >();
+  test_basic<WithSize<256> >();
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/op_subscript.runtime.pass.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/op_subscript.runtime.pass.cpp
index fbb4dbc6e03088..3e916d6b13d0f1 100644
--- a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/op_subscript.runtime.pass.cpp
+++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/op_subscript.runtime.pass.cpp
@@ -10,51 +10,117 @@
 
 // unique_ptr
 
-// test op[](size_t)
+// T& unique_ptr::operator[](size_t) const
 
 #include <memory>
 #include <cassert>
-
-// TODO: Move TEST_IS_CONSTANT_EVALUATED into its own header
 #include <type_traits>
+#include <array>
 
 #include "test_macros.h"
+#include "type_algorithms.h"
 
-class A {
-  int state_;
-  static int next_;
+static int next = 0;
+struct EnumeratedDefaultCtor {
+  EnumeratedDefaultCtor() : value(0) { value = ++next; }
+  int value;
+};
 
-public:
-  TEST_CONSTEXPR_CXX23 A() : state_(0) {
-    if (!TEST_IS_CONSTANT_EVALUATED)
-      state_ = ++next_;
+template <std::size_t Size>
+struct WithTrivialDtor {
+  std::array<char, Size> padding = {'x'};
+  TEST_CONSTEXPR_CXX23 friend bool operator==(WithTrivialDtor const& x, WithTrivialDtor const& y) {
+    return x.padding == y.padding;
   }
+};
 
-  TEST_CONSTEXPR_CXX23 int get() const { return state_; }
-
-  friend TEST_CONSTEXPR_CXX23 bool operator==(const A& x, int y) { return x.state_ == y; }
-
-  TEST_CONSTEXPR_CXX23 A& operator=(int i) {
-    state_ = i;
-    return *this;
+template <std::size_t Size>
+struct WithNonTrivialDtor {
+  std::array<char, Size> padding = {'x'};
+  TEST_CONSTEXPR_CXX23 friend bool operator==(WithNonTrivialDtor const& x, WithNonTrivialDtor const& y) {
+    return x.padding == y.padding;
   }
+  ~WithNonTrivialDtor() {}
 };
 
-int A::next_ = 0;
+template <class T>
+struct CustomDeleter : std::default_delete<T> {};
 
 TEST_CONSTEXPR_CXX23 bool test() {
-  std::unique_ptr<A[]> p(new A[3]);
-  if (!TEST_IS_CONSTANT_EVALUATED) {
-    assert(p[0] == 1);
-    assert(p[1] == 2);
-    assert(p[2] == 3);
+  // Basic test
+  {
+    std::unique_ptr<int[]> p(new int[3]);
+    {
+      int& result = p[0];
+      result      = 0;
+    }
+    {
+      int& result = p[1];
+      result      = 1;
+    }
+    {
+      int& result = p[2];
+      result      = 2;
+    }
+
+    assert(p[0] == 0);
+    assert(p[1] == 1);
+    assert(p[2] == 2);
+  }
+
+  // Ensure that the order of access is correct after initializing a unique_ptr but
+  // before actually modifying any of its elements. The implementation would have to
+  // really try for this not to be the case, but we still check it.
+  //
+  // This requires assigning known values to the elements when they are first constructed,
+  // which requires global state.
+  {
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      std::unique_ptr<EnumeratedDefaultCtor[]> p(new EnumeratedDefaultCtor[3]);
+      assert(p[0].value == 1);
+      assert(p[1].value == 2);
+      assert(p[2].value == 3);
+    }
+  }
+
+  // Make sure operator[] is const-qualified
+  {
+    std::unique_ptr<int[]> const p(new int[3]);
+    p[0] = 42;
+    assert(p[0] == 42);
+  }
+
+  // Make sure we properly handle types with trivial and non-trivial destructors of different
+  // sizes. This is relevant because some implementations may want to use properties of the
+  // ABI like array cookies and these properties often depend on e.g. the triviality of T's
+  // destructor, T's size and so on.
+#if TEST_STD_VER >= 20 // this test is too painful to write before C++20
+  {
+    using TrickyCookieTypes = types::type_list<
+        WithTrivialDtor<1>,
+        WithTrivialDtor<2>,
+        WithTrivialDtor<3>,
+        WithTrivialDtor<4>,
+        WithTrivialDtor<8>,
+        WithTrivialDtor<16>,
+        WithTrivialDtor<256>,
+        WithNonTrivialDtor<1>,
+        WithNonTrivialDtor<2>,
+        WithNonTrivialDtor<3>,
+        WithNonTrivialDtor<4>,
+        WithNonTrivialDtor<8>,
+        WithNonTrivialDtor<16>,
+        WithNonTrivialDtor<256>>;
+    types::for_each(TrickyCookieTypes(), []<class T> {
+      types::for_each(types::type_list<std::default_delete<T[]>, CustomDeleter<T[]>>(), []<class Deleter> {
+        std::unique_ptr<T[], Deleter> p(new T[3]);
+        assert(p[0] == T());
+        assert(p[1] == T());
+        assert(p[2] == T());
+      });
+    });
   }
-  p[0] = 3;
-  p[1] = 2;
-  p[2] = 1;
-  assert(p[0] == 3);
-  assert(p[1] == 2);
-  assert(p[2] == 1);
+#endif // C++20
 
   return true;
 }

>From ddf44b288da49bc63974b72f81e86d05fb2f498e Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 28 Aug 2024 13:58:03 -0400
Subject: [PATCH 2/6] Add missing constexpr

---
 .../unique.ptr.observers/op_subscript.runtime.pass.cpp          | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/op_subscript.runtime.pass.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/op_subscript.runtime.pass.cpp
index 3e916d6b13d0f1..ebfad8ec724e51 100644
--- a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/op_subscript.runtime.pass.cpp
+++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/op_subscript.runtime.pass.cpp
@@ -40,7 +40,7 @@ struct WithNonTrivialDtor {
   TEST_CONSTEXPR_CXX23 friend bool operator==(WithNonTrivialDtor const& x, WithNonTrivialDtor const& y) {
     return x.padding == y.padding;
   }
-  ~WithNonTrivialDtor() {}
+  TEST_CONSTEXPR_CXX23 ~WithNonTrivialDtor() {}
 };
 
 template <class T>

>From 0f56d47299971b1f5003efc3cc9f2e80a5cdc807 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 28 Aug 2024 14:04:27 -0400
Subject: [PATCH 3/6] Update transitive includes

---
 .../test/libcxx/transitive_includes/cxx03.csv | 15 +++++++++++++
 .../test/libcxx/transitive_includes/cxx11.csv | 16 ++++++++++++++
 .../test/libcxx/transitive_includes/cxx14.csv | 16 ++++++++++++++
 .../test/libcxx/transitive_includes/cxx17.csv | 16 ++++++++++++++
 .../test/libcxx/transitive_includes/cxx20.csv | 16 ++++++++++++++
 .../test/libcxx/transitive_includes/cxx23.csv | 22 +++++++++++++++++++
 .../test/libcxx/transitive_includes/cxx26.csv | 22 +++++++++++++++++++
 7 files changed, 123 insertions(+)

diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index fd2cd7f1c2d961..a60ec8134a1012 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -20,6 +20,7 @@ algorithm utility
 algorithm version
 any atomic
 any chrono
+any climits
 any concepts
 any cstddef
 any cstdint
@@ -63,6 +64,7 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier climits
 barrier concepts
 barrier cstddef
 barrier cstdint
@@ -139,6 +141,7 @@ cmath type_traits
 cmath version
 codecvt atomic
 codecvt cctype
+codecvt climits
 codecvt clocale
 codecvt concepts
 codecvt cstddef
@@ -204,6 +207,7 @@ cwchar cwctype
 cwctype cctype
 deque algorithm
 deque atomic
+deque climits
 deque compare
 deque concepts
 deque cstddef
@@ -310,6 +314,7 @@ forward_list typeinfo
 forward_list version
 fstream atomic
 fstream cctype
+fstream climits
 fstream clocale
 fstream concepts
 fstream cstddef
@@ -355,6 +360,7 @@ functional version
 future atomic
 future cerrno
 future chrono
+future climits
 future cstddef
 future cstdint
 future cstdlib
@@ -379,6 +385,7 @@ iomanip version
 ios atomic
 ios cctype
 ios cerrno
+ios climits
 ios clocale
 ios concepts
 ios cstddef
@@ -405,6 +412,7 @@ iostream streambuf
 iostream version
 istream bitset
 istream cerrno
+istream climits
 istream concepts
 istream cstddef
 istream cstdint
@@ -470,6 +478,7 @@ list version
 locale atomic
 locale cctype
 locale cerrno
+locale climits
 locale clocale
 locale concepts
 locale cstdarg
@@ -518,6 +527,7 @@ mdspan limits
 mdspan span
 mdspan version
 memory atomic
+memory climits
 memory compare
 memory concepts
 memory cstddef
@@ -607,6 +617,7 @@ optional version
 ostream atomic
 ostream bitset
 ostream cerrno
+ostream climits
 ostream concepts
 ostream cstddef
 ostream cstdint
@@ -682,6 +693,7 @@ ratio type_traits
 ratio version
 regex atomic
 regex cctype
+regex climits
 regex clocale
 regex compare
 regex concepts
@@ -769,6 +781,7 @@ span type_traits
 span version
 sstream bitset
 sstream cerrno
+sstream climits
 sstream cstddef
 sstream cstdint
 sstream cstring
@@ -936,6 +949,7 @@ typeinfo cstdlib
 typeinfo type_traits
 unordered_map algorithm
 unordered_map bit
+unordered_map climits
 unordered_map cmath
 unordered_map compare
 unordered_map concepts
@@ -952,6 +966,7 @@ unordered_map stdexcept
 unordered_map tuple
 unordered_map type_traits
 unordered_map version
+unordered_set climits
 unordered_set cmath
 unordered_set compare
 unordered_set concepts
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 04122fd0f4571a..8ab3bd163a037f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -20,6 +20,7 @@ algorithm utility
 algorithm version
 any atomic
 any chrono
+any climits
 any concepts
 any cstddef
 any cstdint
@@ -63,6 +64,7 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier climits
 barrier concepts
 barrier cstddef
 barrier cstdint
@@ -139,6 +141,7 @@ cmath type_traits
 cmath version
 codecvt atomic
 codecvt cctype
+codecvt climits
 codecvt clocale
 codecvt concepts
 codecvt cstddef
@@ -205,6 +208,7 @@ cwchar cwctype
 cwctype cctype
 deque algorithm
 deque atomic
+deque climits
 deque compare
 deque concepts
 deque cstddef
@@ -311,6 +315,7 @@ forward_list typeinfo
 forward_list version
 fstream atomic
 fstream cctype
+fstream climits
 fstream clocale
 fstream concepts
 fstream cstddef
@@ -335,6 +340,7 @@ fstream typeinfo
 fstream version
 functional array
 functional atomic
+functional climits
 functional concepts
 functional cstddef
 functional cstdint
@@ -357,6 +363,7 @@ functional version
 future atomic
 future cerrno
 future chrono
+future climits
 future cstddef
 future cstdint
 future cstdlib
@@ -381,6 +388,7 @@ iomanip version
 ios atomic
 ios cctype
 ios cerrno
+ios climits
 ios clocale
 ios concepts
 ios cstddef
@@ -408,6 +416,7 @@ iostream streambuf
 iostream version
 istream bitset
 istream cerrno
+istream climits
 istream concepts
 istream cstddef
 istream cstdint
@@ -473,6 +482,7 @@ list version
 locale atomic
 locale cctype
 locale cerrno
+locale climits
 locale clocale
 locale concepts
 locale cstdarg
@@ -522,6 +532,7 @@ mdspan limits
 mdspan span
 mdspan version
 memory atomic
+memory climits
 memory compare
 memory concepts
 memory cstddef
@@ -612,6 +623,7 @@ optional version
 ostream atomic
 ostream bitset
 ostream cerrno
+ostream climits
 ostream concepts
 ostream cstddef
 ostream cstdint
@@ -687,6 +699,7 @@ ratio type_traits
 ratio version
 regex atomic
 regex cctype
+regex climits
 regex clocale
 regex compare
 regex concepts
@@ -775,6 +788,7 @@ span type_traits
 span version
 sstream bitset
 sstream cerrno
+sstream climits
 sstream cstddef
 sstream cstdint
 sstream cstring
@@ -943,6 +957,7 @@ typeinfo cstdlib
 typeinfo type_traits
 unordered_map algorithm
 unordered_map bit
+unordered_map climits
 unordered_map cmath
 unordered_map compare
 unordered_map concepts
@@ -959,6 +974,7 @@ unordered_map stdexcept
 unordered_map tuple
 unordered_map type_traits
 unordered_map version
+unordered_set climits
 unordered_set cmath
 unordered_set compare
 unordered_set concepts
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index 42c742bead0c1f..59d65e16e4eeeb 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -21,6 +21,7 @@ algorithm utility
 algorithm version
 any atomic
 any chrono
+any climits
 any concepts
 any cstddef
 any cstdint
@@ -64,6 +65,7 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier climits
 barrier concepts
 barrier cstddef
 barrier cstdint
@@ -140,6 +142,7 @@ cmath type_traits
 cmath version
 codecvt atomic
 codecvt cctype
+codecvt climits
 codecvt clocale
 codecvt concepts
 codecvt cstddef
@@ -206,6 +209,7 @@ cwchar cwctype
 cwctype cctype
 deque algorithm
 deque atomic
+deque climits
 deque compare
 deque concepts
 deque cstddef
@@ -314,6 +318,7 @@ forward_list typeinfo
 forward_list version
 fstream atomic
 fstream cctype
+fstream climits
 fstream clocale
 fstream concepts
 fstream cstddef
@@ -338,6 +343,7 @@ fstream typeinfo
 fstream version
 functional array
 functional atomic
+functional climits
 functional concepts
 functional cstddef
 functional cstdint
@@ -360,6 +366,7 @@ functional version
 future atomic
 future cerrno
 future chrono
+future climits
 future cstddef
 future cstdint
 future cstdlib
@@ -384,6 +391,7 @@ iomanip version
 ios atomic
 ios cctype
 ios cerrno
+ios climits
 ios clocale
 ios concepts
 ios cstddef
@@ -411,6 +419,7 @@ iostream streambuf
 iostream version
 istream bitset
 istream cerrno
+istream climits
 istream concepts
 istream cstddef
 istream cstdint
@@ -476,6 +485,7 @@ list version
 locale atomic
 locale cctype
 locale cerrno
+locale climits
 locale clocale
 locale concepts
 locale cstdarg
@@ -525,6 +535,7 @@ mdspan limits
 mdspan span
 mdspan version
 memory atomic
+memory climits
 memory compare
 memory concepts
 memory cstddef
@@ -615,6 +626,7 @@ optional version
 ostream atomic
 ostream bitset
 ostream cerrno
+ostream climits
 ostream concepts
 ostream cstddef
 ostream cstdint
@@ -690,6 +702,7 @@ ratio type_traits
 ratio version
 regex atomic
 regex cctype
+regex climits
 regex clocale
 regex compare
 regex concepts
@@ -778,6 +791,7 @@ span type_traits
 span version
 sstream bitset
 sstream cerrno
+sstream climits
 sstream cstddef
 sstream cstdint
 sstream cstring
@@ -946,6 +960,7 @@ typeinfo cstdlib
 typeinfo type_traits
 unordered_map algorithm
 unordered_map bit
+unordered_map climits
 unordered_map cmath
 unordered_map compare
 unordered_map concepts
@@ -962,6 +977,7 @@ unordered_map stdexcept
 unordered_map tuple
 unordered_map type_traits
 unordered_map version
+unordered_set climits
 unordered_set cmath
 unordered_set compare
 unordered_set concepts
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index bc0659127d4bf5..0c4328081f6750 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -21,6 +21,7 @@ algorithm utility
 algorithm version
 any atomic
 any chrono
+any climits
 any concepts
 any cstddef
 any cstdint
@@ -64,6 +65,7 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier climits
 barrier concepts
 barrier cstddef
 barrier cstdint
@@ -140,6 +142,7 @@ cmath type_traits
 cmath version
 codecvt atomic
 codecvt cctype
+codecvt climits
 codecvt clocale
 codecvt concepts
 codecvt cstddef
@@ -206,6 +209,7 @@ cwchar cwctype
 cwctype cctype
 deque algorithm
 deque atomic
+deque climits
 deque compare
 deque concepts
 deque cstddef
@@ -314,6 +318,7 @@ forward_list typeinfo
 forward_list version
 fstream atomic
 fstream cctype
+fstream climits
 fstream clocale
 fstream concepts
 fstream cstddef
@@ -338,6 +343,7 @@ fstream typeinfo
 fstream version
 functional array
 functional atomic
+functional climits
 functional concepts
 functional cstddef
 functional cstdint
@@ -360,6 +366,7 @@ functional version
 future atomic
 future cerrno
 future chrono
+future climits
 future cstddef
 future cstdint
 future cstdlib
@@ -384,6 +391,7 @@ iomanip version
 ios atomic
 ios cctype
 ios cerrno
+ios climits
 ios clocale
 ios concepts
 ios cstddef
@@ -411,6 +419,7 @@ iostream streambuf
 iostream version
 istream bitset
 istream cerrno
+istream climits
 istream concepts
 istream cstddef
 istream cstdint
@@ -476,6 +485,7 @@ list version
 locale atomic
 locale cctype
 locale cerrno
+locale climits
 locale clocale
 locale concepts
 locale cstdarg
@@ -525,6 +535,7 @@ mdspan limits
 mdspan span
 mdspan version
 memory atomic
+memory climits
 memory compare
 memory concepts
 memory cstddef
@@ -616,6 +627,7 @@ optional version
 ostream atomic
 ostream bitset
 ostream cerrno
+ostream climits
 ostream concepts
 ostream cstddef
 ostream cstdint
@@ -691,6 +703,7 @@ ratio type_traits
 ratio version
 regex atomic
 regex cctype
+regex climits
 regex clocale
 regex compare
 regex concepts
@@ -779,6 +792,7 @@ span type_traits
 span version
 sstream bitset
 sstream cerrno
+sstream climits
 sstream cstddef
 sstream cstdint
 sstream cstring
@@ -947,6 +961,7 @@ typeinfo cstdlib
 typeinfo type_traits
 unordered_map algorithm
 unordered_map bit
+unordered_map climits
 unordered_map cmath
 unordered_map compare
 unordered_map concepts
@@ -963,6 +978,7 @@ unordered_map stdexcept
 unordered_map tuple
 unordered_map type_traits
 unordered_map version
+unordered_set climits
 unordered_set cmath
 unordered_set compare
 unordered_set concepts
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index fed0944f0219c4..bfb28bbcdbb749 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -20,6 +20,7 @@ algorithm type_traits
 algorithm utility
 algorithm version
 any atomic
+any climits
 any concepts
 any cstddef
 any cstdint
@@ -63,6 +64,7 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier climits
 barrier concepts
 barrier cstddef
 barrier cstdint
@@ -145,6 +147,7 @@ cmath type_traits
 cmath version
 codecvt atomic
 codecvt cctype
+codecvt climits
 codecvt clocale
 codecvt concepts
 codecvt cstddef
@@ -214,6 +217,7 @@ cwchar cwctype
 cwctype cctype
 deque algorithm
 deque atomic
+deque climits
 deque compare
 deque concepts
 deque cstddef
@@ -322,6 +326,7 @@ forward_list typeinfo
 forward_list version
 fstream atomic
 fstream cctype
+fstream climits
 fstream clocale
 fstream concepts
 fstream cstddef
@@ -346,6 +351,7 @@ fstream typeinfo
 fstream version
 functional array
 functional atomic
+functional climits
 functional concepts
 functional cstddef
 functional cstdint
@@ -367,6 +373,7 @@ functional vector
 functional version
 future atomic
 future cerrno
+future climits
 future cstddef
 future cstdint
 future cstdlib
@@ -391,6 +398,7 @@ iomanip version
 ios atomic
 ios cctype
 ios cerrno
+ios climits
 ios clocale
 ios concepts
 ios cstddef
@@ -418,6 +426,7 @@ iostream streambuf
 iostream version
 istream bitset
 istream cerrno
+istream climits
 istream concepts
 istream cstddef
 istream cstdint
@@ -483,6 +492,7 @@ list version
 locale atomic
 locale cctype
 locale cerrno
+locale climits
 locale clocale
 locale concepts
 locale cstdarg
@@ -532,6 +542,7 @@ mdspan limits
 mdspan span
 mdspan version
 memory atomic
+memory climits
 memory compare
 memory concepts
 memory cstddef
@@ -623,6 +634,7 @@ optional version
 ostream atomic
 ostream bitset
 ostream cerrno
+ostream climits
 ostream concepts
 ostream cstddef
 ostream cstdint
@@ -699,6 +711,7 @@ ratio type_traits
 ratio version
 regex atomic
 regex cctype
+regex climits
 regex clocale
 regex compare
 regex concepts
@@ -787,6 +800,7 @@ span type_traits
 span version
 sstream bitset
 sstream cerrno
+sstream climits
 sstream cstddef
 sstream cstdint
 sstream cstring
@@ -954,6 +968,7 @@ typeinfo cstdlib
 typeinfo type_traits
 unordered_map algorithm
 unordered_map bit
+unordered_map climits
 unordered_map cmath
 unordered_map compare
 unordered_map concepts
@@ -970,6 +985,7 @@ unordered_map stdexcept
 unordered_map tuple
 unordered_map type_traits
 unordered_map version
+unordered_set climits
 unordered_set cmath
 unordered_set compare
 unordered_set concepts
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 53f99384a7f573..2ebec7dd0f0439 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -12,6 +12,7 @@ algorithm optional
 algorithm ratio
 algorithm tuple
 algorithm version
+any climits
 any cstddef
 any cstdint
 any cstring
@@ -36,6 +37,7 @@ atomic ctime
 atomic limits
 atomic ratio
 atomic version
+barrier climits
 barrier cstddef
 barrier cstdint
 barrier cstring
@@ -90,6 +92,7 @@ cmath initializer_list
 cmath limits
 cmath version
 codecvt cctype
+codecvt climits
 codecvt clocale
 codecvt cstddef
 codecvt cstdint
@@ -115,6 +118,7 @@ concepts cstddef
 concepts version
 condition_variable atomic
 condition_variable cerrno
+condition_variable climits
 condition_variable cstddef
 condition_variable cstdint
 condition_variable cstring
@@ -136,6 +140,7 @@ ctgmath ccomplex
 ctgmath cmath
 cwchar cwctype
 cwctype cctype
+deque climits
 deque compare
 deque cstddef
 deque cstdint
@@ -184,6 +189,7 @@ filesystem version
 format array
 format cctype
 format cerrno
+format climits
 format clocale
 format cmath
 format cstddef
@@ -211,6 +217,7 @@ forward_list new
 forward_list tuple
 forward_list version
 fstream cctype
+fstream climits
 fstream clocale
 fstream cstddef
 fstream cstdint
@@ -229,6 +236,7 @@ fstream tuple
 fstream typeinfo
 fstream version
 functional array
+functional climits
 functional cstddef
 functional cstdint
 functional cstring
@@ -241,6 +249,7 @@ functional unordered_map
 functional vector
 functional version
 future cerrno
+future climits
 future cstddef
 future cstdint
 future cstdlib
@@ -260,6 +269,7 @@ iomanip istream
 iomanip version
 ios cctype
 ios cerrno
+ios climits
 ios clocale
 ios cstddef
 ios cstdint
@@ -285,6 +295,7 @@ iostream streambuf
 iostream version
 istream bitset
 istream cerrno
+istream climits
 istream cstddef
 istream cstdint
 istream cstring
@@ -326,6 +337,7 @@ list tuple
 list version
 locale cctype
 locale cerrno
+locale climits
 locale clocale
 locale cstddef
 locale cstdint
@@ -344,6 +356,7 @@ locale string
 locale tuple
 locale typeinfo
 locale version
+map climits
 map compare
 map cstddef
 map cstdint
@@ -362,6 +375,7 @@ mdspan cstddef
 mdspan limits
 mdspan span
 mdspan version
+memory climits
 memory compare
 memory cstddef
 memory cstdint
@@ -380,6 +394,7 @@ memory_resource new
 memory_resource tuple
 memory_resource version
 mutex cerrno
+mutex climits
 mutex cstddef
 mutex cstdint
 mutex ctime
@@ -415,6 +430,7 @@ optional new
 optional version
 ostream bitset
 ostream cerrno
+ostream climits
 ostream cstddef
 ostream cstdint
 ostream cstring
@@ -473,6 +489,7 @@ ratio climits
 ratio cstdint
 ratio version
 regex cctype
+regex climits
 regex clocale
 regex compare
 regex cstddef
@@ -503,6 +520,7 @@ semaphore ctime
 semaphore limits
 semaphore ratio
 semaphore version
+set climits
 set compare
 set cstddef
 set cstdint
@@ -530,6 +548,7 @@ span stdexcept
 span version
 sstream bitset
 sstream cerrno
+sstream climits
 sstream cstddef
 sstream cstdint
 sstream cstring
@@ -625,6 +644,7 @@ thread array
 thread atomic
 thread cctype
 thread cerrno
+thread climits
 thread clocale
 thread compare
 thread cstddef
@@ -658,6 +678,7 @@ typeindex typeinfo
 typeindex version
 typeinfo cstddef
 typeinfo cstdint
+unordered_map climits
 unordered_map compare
 unordered_map cstddef
 unordered_map cstdint
@@ -669,6 +690,7 @@ unordered_map optional
 unordered_map stdexcept
 unordered_map tuple
 unordered_map version
+unordered_set climits
 unordered_set compare
 unordered_set cstddef
 unordered_set cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 53f99384a7f573..2ebec7dd0f0439 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -12,6 +12,7 @@ algorithm optional
 algorithm ratio
 algorithm tuple
 algorithm version
+any climits
 any cstddef
 any cstdint
 any cstring
@@ -36,6 +37,7 @@ atomic ctime
 atomic limits
 atomic ratio
 atomic version
+barrier climits
 barrier cstddef
 barrier cstdint
 barrier cstring
@@ -90,6 +92,7 @@ cmath initializer_list
 cmath limits
 cmath version
 codecvt cctype
+codecvt climits
 codecvt clocale
 codecvt cstddef
 codecvt cstdint
@@ -115,6 +118,7 @@ concepts cstddef
 concepts version
 condition_variable atomic
 condition_variable cerrno
+condition_variable climits
 condition_variable cstddef
 condition_variable cstdint
 condition_variable cstring
@@ -136,6 +140,7 @@ ctgmath ccomplex
 ctgmath cmath
 cwchar cwctype
 cwctype cctype
+deque climits
 deque compare
 deque cstddef
 deque cstdint
@@ -184,6 +189,7 @@ filesystem version
 format array
 format cctype
 format cerrno
+format climits
 format clocale
 format cmath
 format cstddef
@@ -211,6 +217,7 @@ forward_list new
 forward_list tuple
 forward_list version
 fstream cctype
+fstream climits
 fstream clocale
 fstream cstddef
 fstream cstdint
@@ -229,6 +236,7 @@ fstream tuple
 fstream typeinfo
 fstream version
 functional array
+functional climits
 functional cstddef
 functional cstdint
 functional cstring
@@ -241,6 +249,7 @@ functional unordered_map
 functional vector
 functional version
 future cerrno
+future climits
 future cstddef
 future cstdint
 future cstdlib
@@ -260,6 +269,7 @@ iomanip istream
 iomanip version
 ios cctype
 ios cerrno
+ios climits
 ios clocale
 ios cstddef
 ios cstdint
@@ -285,6 +295,7 @@ iostream streambuf
 iostream version
 istream bitset
 istream cerrno
+istream climits
 istream cstddef
 istream cstdint
 istream cstring
@@ -326,6 +337,7 @@ list tuple
 list version
 locale cctype
 locale cerrno
+locale climits
 locale clocale
 locale cstddef
 locale cstdint
@@ -344,6 +356,7 @@ locale string
 locale tuple
 locale typeinfo
 locale version
+map climits
 map compare
 map cstddef
 map cstdint
@@ -362,6 +375,7 @@ mdspan cstddef
 mdspan limits
 mdspan span
 mdspan version
+memory climits
 memory compare
 memory cstddef
 memory cstdint
@@ -380,6 +394,7 @@ memory_resource new
 memory_resource tuple
 memory_resource version
 mutex cerrno
+mutex climits
 mutex cstddef
 mutex cstdint
 mutex ctime
@@ -415,6 +430,7 @@ optional new
 optional version
 ostream bitset
 ostream cerrno
+ostream climits
 ostream cstddef
 ostream cstdint
 ostream cstring
@@ -473,6 +489,7 @@ ratio climits
 ratio cstdint
 ratio version
 regex cctype
+regex climits
 regex clocale
 regex compare
 regex cstddef
@@ -503,6 +520,7 @@ semaphore ctime
 semaphore limits
 semaphore ratio
 semaphore version
+set climits
 set compare
 set cstddef
 set cstdint
@@ -530,6 +548,7 @@ span stdexcept
 span version
 sstream bitset
 sstream cerrno
+sstream climits
 sstream cstddef
 sstream cstdint
 sstream cstring
@@ -625,6 +644,7 @@ thread array
 thread atomic
 thread cctype
 thread cerrno
+thread climits
 thread clocale
 thread compare
 thread cstddef
@@ -658,6 +678,7 @@ typeindex typeinfo
 typeindex version
 typeinfo cstddef
 typeinfo cstdint
+unordered_map climits
 unordered_map compare
 unordered_map cstddef
 unordered_map cstdint
@@ -669,6 +690,7 @@ unordered_map optional
 unordered_map stdexcept
 unordered_map tuple
 unordered_map version
+unordered_set climits
 unordered_set compare
 unordered_set cstddef
 unordered_set cstdint

>From 0d493a86e369792281a0be6fee8dd10047ac707d Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 30 Aug 2024 09:17:13 -0400
Subject: [PATCH 4/6] Fix clang-tidy diagnostic

---
 libcxx/include/__memory/unique_ptr.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h
index ea2a38eab6f8f5..88e43ef9b6b24f 100644
--- a/libcxx/include/__memory/unique_ptr.h
+++ b/libcxx/include/__memory/unique_ptr.h
@@ -325,7 +325,7 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr {
 // (1) Implementation where we rely on the array cookie to know the size of the allocation, if
 //     an array cookie exists.
 struct __unique_ptr_array_bounds_stateless {
-  _LIBCPP_HIDE_FROM_ABI __unique_ptr_array_bounds_stateless() = default;
+  __unique_ptr_array_bounds_stateless() = default;
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __unique_ptr_array_bounds_stateless(size_t) {}
 
   template <class _Tp, __enable_if_t<__has_array_cookie<_Tp>::value, int> = 0>

>From 7e89fdf10a8433cd499be62bdf91489f218e20c5 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 30 Aug 2024 09:18:06 -0400
Subject: [PATCH 5/6] Add missing include

---
 libcxx/include/__memory/unique_ptr.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h
index 88e43ef9b6b24f..d6309afc92987f 100644
--- a/libcxx/include/__memory/unique_ptr.h
+++ b/libcxx/include/__memory/unique_ptr.h
@@ -10,6 +10,7 @@
 #ifndef _LIBCPP___MEMORY_UNIQUE_PTR_H
 #define _LIBCPP___MEMORY_UNIQUE_PTR_H
 
+#include <__assert>
 #include <__compare/compare_three_way.h>
 #include <__compare/compare_three_way_result.h>
 #include <__compare/three_way_comparable.h>

>From 3aae45db7864d1a9adbfe0bc634d8c3ff1168a2c Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 30 Aug 2024 09:24:53 -0400
Subject: [PATCH 6/6] Avoid Asan in __get_array_cookie

---
 libcxx/include/__memory/array_cookie.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__memory/array_cookie.h b/libcxx/include/__memory/array_cookie.h
index 29f80df9e7c97a..34eec643206103 100644
--- a/libcxx/include/__memory/array_cookie.h
+++ b/libcxx/include/__memory/array_cookie.h
@@ -11,7 +11,6 @@
 #define _LIBCPP___MEMORY_ARRAY_COOKIE_H
 
 #include <__config>
-
 #include <__configuration/abi.h>
 #include <__type_traits/integral_constant.h>
 #include <__type_traits/is_trivially_destructible.h>
@@ -43,7 +42,8 @@ struct __has_array_cookie : false_type {};
 #endif
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI size_t __get_array_cookie(_Tp const* __ptr) {
+// Avoid failures when -fsanitize-address-poison-custom-array-cookie is enabled
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_NO_SANITIZE("address") size_t __get_array_cookie(_Tp const* __ptr) {
   static_assert(
       __has_array_cookie<_Tp>::value, "Trying to access the array cookie of a type that is not guaranteed to have one");
   size_t const* __cookie = reinterpret_cast<size_t const*>(__ptr) - 1; // TODO: Use a builtin instead



More information about the libcxx-commits mailing list