[libcxx-commits] [libcxx] daa88b3 - [libc++] Remove UB from `std::__tree_node` construction (#153908)
via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Sep 11 03:52:22 PDT 2025
Author: Vinay Deshmukh
Date: 2025-09-11T12:52:18+02:00
New Revision: daa88b3f43aedf648d0b1715dd2c3ebe26eea484
URL: https://github.com/llvm/llvm-project/commit/daa88b3f43aedf648d0b1715dd2c3ebe26eea484
DIFF: https://github.com/llvm/llvm-project/commit/daa88b3f43aedf648d0b1715dd2c3ebe26eea484.diff
LOG: [libc++] Remove UB from `std::__tree_node` construction (#153908)
This patch also updates `__hash_table` to match what we do in `__tree`
now.
Fixes #102547
Fixes
https://github.com/llvm/llvm-project/pull/134330#discussion_r2265558356
Added:
Modified:
libcxx/include/__config
libcxx/include/__hash_table
libcxx/include/__tree
Removed:
################################################################################
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 23b9123fa6917..b4c081dcdff1b 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -1176,12 +1176,6 @@ typedef __char32_t char32_t;
# define _LIBCPP_NO_SPECIALIZATIONS
# endif
-# if __has_cpp_attribute(_Clang::__standalone_debug__)
-# define _LIBCPP_STANDALONE_DEBUG [[_Clang::__standalone_debug__]]
-# else
-# define _LIBCPP_STANDALONE_DEBUG
-# endif
-
# if __has_cpp_attribute(_Clang::__preferred_name__)
# define _LIBCPP_PREFERRED_NAME(x) [[_Clang::__preferred_name__(x)]]
# else
diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index f0e88bbf2bc38..2018b5a194e89 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -166,7 +166,12 @@ public:
}
#endif
- _LIBCPP_HIDE_FROM_ABI explicit __hash_node(__next_pointer __next, size_t __hash) : _Base(__next), __hash_(__hash) {}
+ template <class _Alloc, class... _Args>
+ _LIBCPP_HIDE_FROM_ABI explicit __hash_node(size_t __hash, _Alloc& __na, _Args&&... __args)
+ : _Base(nullptr), __hash_(__hash) {
+ allocator_traits<_Alloc>::construct(__na, std::addressof(__get_value()), std::forward<_Args>(__args)...);
+ }
+
_LIBCPP_HIDE_FROM_ABI ~__hash_node() {}
};
@@ -1855,16 +1860,13 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::__construct_node(_Args&&... __args) {
__node_allocator& __na = __node_alloc();
__node_holder __h(__node_traits::allocate(__na, 1), _Dp(__na));
- // Begin the lifetime of the node itself. Note that this doesn't begin the lifetime of the value
- // held inside the node, since we need to use the allocator's construct() method for that.
+ // Begin the lifetime of the node itself and the value_type contained within.
//
// We don't use the allocator's construct() method to construct the node itself since the
// Cpp17FooInsertable named requirements don't require the allocator's construct() method
// to work on anything other than the value_type.
- std::__construct_at(std::addressof(*__h), /* next = */ nullptr, /* hash = */ 0);
+ std::__construct_at(std::addressof(*__h), /* hash = */ 0, __na, std::forward<_Args>(__args)...);
- // Now construct the value_type using the allocator's construct() method.
- __node_traits::construct(__na, std::addressof(__h->__get_value()), std::forward<_Args>(__args)...);
__h.get_deleter().__value_constructed = true;
__h->__hash_ = hash_function()(__h->__get_value());
@@ -1878,8 +1880,7 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::__construct_node_hash(size_t __hash, _
static_assert(!__is_hash_value_type<_Args...>::value, "Construct cannot be called with a hash value type");
__node_allocator& __na = __node_alloc();
__node_holder __h(__node_traits::allocate(__na, 1), _Dp(__na));
- std::__construct_at(std::addressof(*__h), /* next = */ nullptr, /* hash = */ __hash);
- __node_traits::construct(__na, std::addressof(__h->__get_value()), std::forward<_Args>(__args)...);
+ std::__construct_at(std::addressof(*__h), /* hash = */ __hash, __na, std::forward<_Args>(__args)...);
__h.get_deleter().__value_constructed = true;
return __h;
}
diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index 81a73342cc61b..61c910c52c536 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -20,9 +20,11 @@
#include <__memory/addressof.h>
#include <__memory/allocator_traits.h>
#include <__memory/compressed_pair.h>
+#include <__memory/construct_at.h>
#include <__memory/pointer_traits.h>
#include <__memory/swap_allocator.h>
#include <__memory/unique_ptr.h>
+#include <__new/launder.h>
#include <__type_traits/copy_cvref.h>
#include <__type_traits/enable_if.h>
#include <__type_traits/invoke.h>
@@ -559,8 +561,7 @@ public:
};
template <class _VoidPtr>
-class _LIBCPP_STANDALONE_DEBUG
-__tree_node_base : public __tree_end_node<__rebind_pointer_t<_VoidPtr, __tree_node_base<_VoidPtr> > > {
+class __tree_node_base : public __tree_end_node<__rebind_pointer_t<_VoidPtr, __tree_node_base<_VoidPtr> > > {
public:
using pointer = __rebind_pointer_t<_VoidPtr, __tree_node_base>;
using __end_node_pointer _LIBCPP_NODEBUG = __rebind_pointer_t<_VoidPtr, __tree_end_node<pointer> >;
@@ -573,22 +574,41 @@ public:
_LIBCPP_HIDE_FROM_ABI void __set_parent(pointer __p) { __parent_ = static_cast<__end_node_pointer>(__p); }
- ~__tree_node_base() = delete;
+ _LIBCPP_HIDE_FROM_ABI __tree_node_base() = default;
__tree_node_base(__tree_node_base const&) = delete;
__tree_node_base& operator=(__tree_node_base const&) = delete;
};
template <class _Tp, class _VoidPtr>
-class _LIBCPP_STANDALONE_DEBUG __tree_node : public __tree_node_base<_VoidPtr> {
+class __tree_node : public __tree_node_base<_VoidPtr> {
public:
using __node_value_type _LIBCPP_NODEBUG = __get_node_value_type_t<_Tp>;
+// We use a union to avoid initialization during member initialization, which allows us
+// to use the allocator from the container to construct the `__node_value_type` in the
+// memory provided by the union member
+#ifndef _LIBCPP_CXX03_LANG
+
private:
- __node_value_type __value_;
+ union {
+ __node_value_type __value_;
+ };
public:
_LIBCPP_HIDE_FROM_ABI __node_value_type& __get_value() { return __value_; }
+#else
+
+private:
+ _ALIGNAS_TYPE(__node_value_type) unsigned char __buffer_[sizeof(__node_value_type)];
+public:
+ _LIBCPP_HIDE_FROM_ABI __node_value_type& __get_value() { return *reinterpret_cast<__node_value_type*>(__buffer_); }
+#endif
+
+ template <class _Alloc, class... _Args>
+ _LIBCPP_HIDE_FROM_ABI explicit __tree_node(_Alloc& __na, _Args&&... __args) {
+ allocator_traits<_Alloc>::construct(__na, std::addressof(__get_value()), std::forward<_Args>(__args)...);
+ }
~__tree_node() = delete;
__tree_node(__tree_node const&) = delete;
__tree_node& operator=(__tree_node const&) = delete;
@@ -1816,7 +1836,7 @@ typename __tree<_Tp, _Compare, _Allocator>::__node_holder
__tree<_Tp, _Compare, _Allocator>::__construct_node(_Args&&... __args) {
__node_allocator& __na = __node_alloc();
__node_holder __h(__node_traits::allocate(__na, 1), _Dp(__na));
- __node_traits::construct(__na, std::addressof(__h->__get_value()), std::forward<_Args>(__args)...);
+ std::__construct_at(std::addressof(*__h), __na, std::forward<_Args>(__args)...);
__h.get_deleter().__value_constructed = true;
return __h;
}
More information about the libcxx-commits
mailing list