From 30084d74765c70e9cab4f11e9f2c29f141df299e Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Tue, 24 Mar 2026 15:44:54 -0700 Subject: [PATCH] [libc++] Fix type confusion in hash_{,multi}map The type `__gnu_cxx::hash_{,multi}map` creates objects of type `std::pair` and returns pointers to them of type `std::pair`. If either `Key` or `Value` are non-standard-layout, this is UB, and is furthermore considered by pointer field protection to be a type confusion, which leads to a program crash. Fix it by using the correct type for the pair's storage and using const_cast to form a pointer to the key in the one place where that is needed. Reviewers: ldionne Reviewed By: ldionne Pull Request: https://github.com/llvm/llvm-project/pull/183223 --- libcxx/include/ext/hash_map | 18 +++++++----------- .../gnu/hash_map/non_standard_layout.pass.cpp | 16 ++++++++++++++++ .../hash_multimap/non_standard_layout.pass.cpp | 16 ++++++++++++++++ 3 files changed, 39 insertions(+), 11 deletions(-) create mode 100644 libcxx/test/extensions/gnu/hash_map/non_standard_layout.pass.cpp create mode 100644 libcxx/test/extensions/gnu/hash_multimap/non_standard_layout.pass.cpp diff --git a/libcxx/include/ext/hash_map b/libcxx/include/ext/hash_map index 09c981131ff9..7df44370df96 100644 --- a/libcxx/include/ext/hash_map +++ b/libcxx/include/ext/hash_map @@ -426,12 +426,10 @@ public: typedef const value_type& const_reference; private: - typedef std::pair __value_type; - typedef __hash_map_hasher<__value_type, hasher> __hasher; - typedef __hash_map_equal<__value_type, key_equal> __key_equal; - typedef std::__rebind_alloc, __value_type> __allocator_type; + typedef __hash_map_hasher __hasher; + typedef __hash_map_equal __key_equal; - typedef std::__hash_table<__value_type, __hasher, __key_equal, __allocator_type> __table; + typedef std::__hash_table __table; __table __table_; @@ -577,7 +575,7 @@ typename hash_map<_Key, _Tp, _Hash, _Pred, _Alloc>::__node_holder hash_map<_Key, _Tp, _Hash, _Pred, _Alloc>::__construct_node(const key_type& __k) { __node_allocator& __na = __table_.__node_alloc(); __node_holder __h(__node_traits::allocate(__na, 1), _Dp(__na)); - __node_traits::construct(__na, std::addressof(__h->__get_value().first), __k); + __node_traits::construct(__na, const_cast<_Key*>(std::addressof(__h->__get_value().first)), __k); __h.get_deleter().__first_constructed = true; __node_traits::construct(__na, std::addressof(__h->__get_value().second)); __h.get_deleter().__second_constructed = true; @@ -647,12 +645,10 @@ public: typedef const value_type& const_reference; private: - typedef std::pair __value_type; - typedef __hash_map_hasher<__value_type, hasher> __hasher; - typedef __hash_map_equal<__value_type, key_equal> __key_equal; - typedef std::__rebind_alloc, __value_type> __allocator_type; + typedef __hash_map_hasher __hasher; + typedef __hash_map_equal __key_equal; - typedef std::__hash_table<__value_type, __hasher, __key_equal, __allocator_type> __table; + typedef std::__hash_table __table; __table __table_; diff --git a/libcxx/test/extensions/gnu/hash_map/non_standard_layout.pass.cpp b/libcxx/test/extensions/gnu/hash_map/non_standard_layout.pass.cpp new file mode 100644 index 000000000000..4c8403f5ad74 --- /dev/null +++ b/libcxx/test/extensions/gnu/hash_map/non_standard_layout.pass.cpp @@ -0,0 +1,16 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated +#include + +int main(int, char**) { + __gnu_cxx::hash_map m; + auto it = m.insert(std::make_pair("foo", "bar")).first; + return it->first == nullptr; +} diff --git a/libcxx/test/extensions/gnu/hash_multimap/non_standard_layout.pass.cpp b/libcxx/test/extensions/gnu/hash_multimap/non_standard_layout.pass.cpp new file mode 100644 index 000000000000..c8b8a3c63cf5 --- /dev/null +++ b/libcxx/test/extensions/gnu/hash_multimap/non_standard_layout.pass.cpp @@ -0,0 +1,16 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated +#include + +int main(int, char**) { + __gnu_cxx::hash_multimap m; + auto it = m.insert(std::make_pair("foo", "bar")); + return it->first == nullptr; +}