-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[libc++] Remove UB from std::map by updating std::__tree_node construction
#153908
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[libc++] Remove UB from std::map by updating std::__tree_node construction
#153908
Conversation
|
|
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
libcxx/include/__tree
Outdated
| @@ -1276,7 +1310,7 @@ private: | |||
| return nullptr; | |||
| } | |||
|
|
|||
| __assign_value(__dest->__value_, __src->__value_); | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As we no longer expose the member directly, all accesses need to go through the member function so the right type is used.
otherwise, every access needs to be updated to use ifndef CXX03
| @@ -1808,7 +1842,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->__value_), std::forward<_Args>(__args)...); | |||
| std::__construct_at(std::addressof(*__h), __na, std::forward<_Args>(__args)...); | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
N.B. This is the line that prompts this change due to needing workarounds when implementing P3372 for map (Add constexpr)
libcxx/include/__tree
Outdated
| @@ -572,7 +574,8 @@ 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; | |||
| _LIBCPP_HIDE_FROM_ABI ~__tree_node_base() = default; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can just remove this line. Possibly also the the default constructor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the reason I had to add these was because I got errors like these:
Case 1):
Both 577 and 578 commented
failures
# .---command stderr------------
# | In file included from /source-dir/libcxx/test/std/containers/associative/map/compare.pass.cpp:18:
# | In file included from /source-dir/build/myclang22/libcxx/test-suite-install/include/c++/v1/map:599:
# | /source-dir/build/myclang22/libcxx/test-suite-install/include/c++/v1/__tree:611:34: error: constructor for 'std::__tree_node<std::__value_type<Key, int>, void *>' must explicitly initialize the base class '__tree_node_base<void *>' which does not have a default constructor
# | 611 | _LIBCPP_HIDE_FROM_ABI explicit __tree_node(_Alloc& __na, _Args&&... __args) {
# | | ^
# | /source-dir/build/myclang22/libcxx/test-suite-install/include/c++/v1/__memory/construct_at.h:38:49: note: in instantiation of function template specialization 'std::__tree_node<std::__value_type<Key, int>, void *>::__tree_node<std::allocator<std::__tree_node<std::__value_type<Key, int>, void *>>, const std::piecewise_construct_t &, std::tuple<Key &&>, std::tuple<>>' requested here
# | 38 | return ::new (static_cast<void*>(__location)) _Tp(std::forward<_Args>(__args)...);
# | | ^
# | /source-dir/build/myclang22/libcxx/test-suite-install/include/c++/v1/__tree:1812:25: note: in instantiation of function template specialization 'std::__tree<std::__value_type<Key, int>, std::__map_value_compare<Key, std::pair<const Key, int>, std::less<Key>>, std::allocator<std::pair<const Key, int>>>::__construct_node<const std::piecewise_construct_t &, std::tuple<Key &&>, std::tuple<>>' requested here
# | 1812 | __node_holder __h = __construct_node(std::forward<_Args>(__args)...);
# | | ^
# | /source-dir/build/myclang22/libcxx/test-suite-install/include/c++/v1/map:1410:8: note: in instantiation of function template specialization 'std::__tree<std::__value_type<Key, int>, std::__map_value_compare<Key, std::pair<const Key, int>, std::less<Key>>, std::allocator<std::pair<const Key, int>>>::__emplace_unique_key_args<Key, const std::piecewise_construct_t &, std::tuple<Key &&>, std::tuple<>>' requested here
# | 1410 | .__emplace_unique_key_args(
# | | ^
# | /source-dir/libcxx/test/std/containers/associative/map/compare.pass.cpp:37:15: note: in instantiation of member function 'std::map<Key, int>::operator[]' requested here
# | 37 | m_contains[Key(0)] = 42;
# | | ^
# | /source-dir/build/myclang22/libcxx/test-suite-install/include/c++/v1/__tree:564:1: note: 'std::__tree_node_base<void *>' declared here
# | 564 | __tree_node_base : public __tree_end_node<__rebind_pointer_t<_VoidPtr, __tree_node_base<_VoidPtr> > > {
# | | ^ ^
From 43/64 /map tests
Case 2): Only 578 commented, (maybe a default destructortor is created)
all pass
case 3): constructor() = default; and destructor = delete
failures
# | In file included from /source-dir/libcxx/test/std/containers/associative/map/map.cons/copy_assign.addressof.compile.pass.cpp:17:
# | In file included from /source-dir/build/myclang22/libcxx/test-suite-install/include/c++/v1/map:599:
# | /source-dir/build/myclang22/libcxx/test-suite-install/include/c++/v1/__tree:612:34: error: attempt to use a deleted function
# | 612 | _LIBCPP_HIDE_FROM_ABI explicit __tree_node(_Alloc& __na, _Args&&... __args) {
# | | ^
# | /source-dir/build/myclang22/libcxx/test-suite-install/include/c++/v1/__memory/construct_at.h:38:49: note: in instantiation of function template specialization 'std::__tree_node<std::__value_type<int, operator_hijacker>, void *>::__tree_node<std::allocator<std::__tree_node<std::__value_type<int, operator_hijacker>, void *>>, std::pair<const int, operator_hijacker> &>' requested here
# | 38 | return ::new (static_cast<void*>(__location)) _Tp(std::forward<_Args>(__args)...);
# | | ^
# | /source-dir/build/myclang22/libcxx/test-suite-install/include/c++/v1/__tree:1283:32: note: in instantiation of function template specialization 'std::__tree<std::__value_type<int, operator_hijacker>, std::__map_value_compare<int, std::pair<const int, operator_hijacker>, std::less<int>>, std::allocator<std::pair<const int, operator_hijacker>>>::__construct_node<std::pair<const int, operator_hijacker> &>' requested here
# | 1283 | __node_holder __new_node = __construct_node(__src->__get_value());
# | | ^
# | /source-dir/build/myclang22/libcxx/test-suite-install/include/c++/v1/__tree:1414:54: note: in instantiation of member function 'std::__tree<std::__value_type<int, operator_hijacker>, std::__map_value_compare<int, std::pair<const int, operator_hijacker>, std::less<int>>, std::allocator<std::pair<const int, operator_hijacker>>>::__copy_construct_tree' requested here
# | 1414 | *__root_ptr() = static_cast<__node_base_pointer>(__copy_construct_tree(__t.__root()));
# | | ^
# | /source-dir/build/myclang22/libcxx/test-suite-install/include/c++/v1/map:977:64: note: in instantiation of member function 'std::__tree<std::__value_type<int, operator_hijacker>, std::__map_value_compare<int, std::pair<const int, operator_hijacker>, std::less<int>>, std::allocator<std::pair<const int, operator_hijacker>>>::operator=' requested here
# | 977 | _LIBCPP_HIDE_FROM_ABI map& operator=(const map& __m) = default;
# | | ^
# | /source-dir/build/myclang22/libcxx/test-suite-install/include/c++/v1/__tree:579:3: note: '~__tree_node_base' has been explicitly marked deleted here
# | 579 | ~__tree_node_base() = delete;
| ^~~~~~~~~~
</details?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can just remove this line. Possibly also the the default constructor?
Seems like that the default constructor needs to be explicitly defaulted. Otherwise, it won't exist because its declaration will be suppressed.
I believe the reason I had to add these was because I got errors like these:
I don't think we need to explicitly declare the destructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need to explicitly declare the destructor.
THis should be fixed as of 8e9153e
my bad, false alarm I do get test failures if I don't update set and multiset... |
… for UB removal (#154225) Prepare for: llvm/llvm-project#153908 (comment)
…roject into vinay-issue-128660-map-ub
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with the last two open comments addressed.
std::map by updating std::__tree_node construction / Update std::__hash_node construction
std::map by updating std::__tree_node construction / Update std::__hash_node constructionstd::map by updating std::__tree_node construction
| // | ||
| // 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), /* next = */ nullptr, /* hash = */ 0, __na, std::forward<_Args>(__args)...); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we ever construct the node with anything but nullptr and 0? If not, I'd just move that into the constructor.


This patch also updates
__hash_tableto match what we do in__treenow.Fixes #134330 (comment)