From 65f3ea60ddd578b2822e804f647c2cd3628a28ab Mon Sep 17 00:00:00 2001 From: Braden Ganetsky Date: Tue, 6 Aug 2024 08:48:13 -0500 Subject: [PATCH 1/4] Use as_const to remove any possibility of use-after-move (#272) --- include/boost/unordered/detail/type_traits.hpp | 11 +++++++++++ include/boost/unordered/unordered_map.hpp | 4 ++-- include/boost/unordered/unordered_set.hpp | 4 ++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/include/boost/unordered/detail/type_traits.hpp b/include/boost/unordered/detail/type_traits.hpp index 4369df2e..f99f7f42 100644 --- a/include/boost/unordered/detail/type_traits.hpp +++ b/include/boost/unordered/detail/type_traits.hpp @@ -219,6 +219,17 @@ namespace boost { using iter_to_alloc_t = typename std::pair const, iter_val_t >; #endif + +#if BOOST_CXX_VERSION < 201703L + template + constexpr typename std::add_const::type& as_const(T& t) noexcept + { + return t; + } + template void as_const(const T&&) = delete; +#else + using std::as_const; +#endif } // namespace detail } // namespace unordered } // namespace boost diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index 47986d4d..6b7418b0 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -198,7 +198,7 @@ namespace boost { template std::pair emplace(Args&&... args) { return table_.emplace_unique( - table::extractor::extract(std::forward(args)...), + table::extractor::extract(detail::as_const(args)...), std::forward(args)...); } @@ -206,7 +206,7 @@ namespace boost { iterator emplace_hint(const_iterator hint, Args&&... args) { return table_.emplace_hint_unique(hint, - table::extractor::extract(std::forward(args)...), + table::extractor::extract(detail::as_const(args)...), std::forward(args)...); } diff --git a/include/boost/unordered/unordered_set.hpp b/include/boost/unordered/unordered_set.hpp index d891a418..eae846dd 100644 --- a/include/boost/unordered/unordered_set.hpp +++ b/include/boost/unordered/unordered_set.hpp @@ -194,7 +194,7 @@ namespace boost { template std::pair emplace(Args&&... args) { return table_.emplace_unique( - table::extractor::extract(std::forward(args)...), + table::extractor::extract(detail::as_const(args)...), std::forward(args)...); } @@ -202,7 +202,7 @@ namespace boost { iterator emplace_hint(const_iterator hint, Args&&... args) { return table_.emplace_hint_unique(hint, - table::extractor::extract(std::forward(args)...), + table::extractor::extract(detail::as_const(args)...), std::forward(args)...); } From c117f4448f629337dd55386646733dd842b2351e Mon Sep 17 00:00:00 2001 From: Braden Ganetsky Date: Wed, 7 Aug 2024 14:16:09 -0500 Subject: [PATCH 2/4] static_assert on the constructibility of the containers' types --- .../unordered/detail/foa/flat_map_types.hpp | 8 + .../unordered/detail/foa/flat_set_types.hpp | 6 + .../unordered/detail/foa/node_map_types.hpp | 12 +- .../unordered/detail/foa/node_set_types.hpp | 10 +- .../detail/foa/types_constructibility.hpp | 172 ++++++++++++++++++ 5 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 include/boost/unordered/detail/foa/types_constructibility.hpp diff --git a/include/boost/unordered/detail/foa/flat_map_types.hpp b/include/boost/unordered/detail/foa/flat_map_types.hpp index 05610e46..c7f58706 100644 --- a/include/boost/unordered/detail/foa/flat_map_types.hpp +++ b/include/boost/unordered/detail/foa/flat_map_types.hpp @@ -6,6 +6,8 @@ #ifndef BOOST_UNORDERED_DETAIL_FOA_FLAT_MAP_TYPES_HPP #define BOOST_UNORDERED_DETAIL_FOA_FLAT_MAP_TYPES_HPP +#include + #include namespace boost { @@ -25,6 +27,9 @@ namespace boost { using element_type = value_type; + using types = flat_map_types; + using constructibility_checker = map_types_constructibility; + static value_type& value_from(element_type& x) { return x; } template @@ -48,18 +53,21 @@ namespace boost { template static void construct(A& al, init_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); boost::allocator_construct(al, p, std::forward(args)...); } template static void construct(A& al, value_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); boost::allocator_construct(al, p, std::forward(args)...); } template static void construct(A& al, key_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); boost::allocator_construct(al, p, std::forward(args)...); } diff --git a/include/boost/unordered/detail/foa/flat_set_types.hpp b/include/boost/unordered/detail/foa/flat_set_types.hpp index 493cb4fe..558077f4 100644 --- a/include/boost/unordered/detail/foa/flat_set_types.hpp +++ b/include/boost/unordered/detail/foa/flat_set_types.hpp @@ -5,6 +5,8 @@ #ifndef BOOST_UNORDERED_DETAIL_FOA_FLAT_SET_TYPES_HPP #define BOOST_UNORDERED_DETAIL_FOA_FLAT_SET_TYPES_HPP +#include + #include namespace boost { @@ -21,6 +23,9 @@ namespace boost { using element_type = value_type; + using types = flat_set_types; + using constructibility_checker = set_types_constructibility; + static Key& value_from(element_type& x) { return x; } static element_type&& move(element_type& x) { return std::move(x); } @@ -28,6 +33,7 @@ namespace boost { template static void construct(A& al, value_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); boost::allocator_construct(al, p, std::forward(args)...); } diff --git a/include/boost/unordered/detail/foa/node_map_types.hpp b/include/boost/unordered/detail/foa/node_map_types.hpp index 4de679a9..0b3d4528 100644 --- a/include/boost/unordered/detail/foa/node_map_types.hpp +++ b/include/boost/unordered/detail/foa/node_map_types.hpp @@ -7,6 +7,7 @@ #define BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP #include +#include #include #include @@ -29,6 +30,9 @@ namespace boost { using element_type = foa::element_type; + using types = node_map_types; + using constructibility_checker = map_types_constructibility; + static value_type& value_from(element_type const& x) { return *(x.p); @@ -74,18 +78,21 @@ namespace boost { template static void construct(A& al, init_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); boost::allocator_construct(al, p, std::forward(args)...); } template static void construct(A& al, value_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); boost::allocator_construct(al, p, std::forward(args)...); } template static void construct(A& al, key_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); boost::allocator_construct(al, p, std::forward(args)...); } @@ -95,8 +102,11 @@ namespace boost { p->p = boost::allocator_allocate(al, 1); BOOST_TRY { + auto address = boost::to_address(p->p); + constructibility_checker::check( + al, address, std::forward(args)...); boost::allocator_construct( - al, boost::to_address(p->p), std::forward(args)...); + al, address, std::forward(args)...); } BOOST_CATCH(...) { diff --git a/include/boost/unordered/detail/foa/node_set_types.hpp b/include/boost/unordered/detail/foa/node_set_types.hpp index 710ac6cc..79cf12f0 100644 --- a/include/boost/unordered/detail/foa/node_set_types.hpp +++ b/include/boost/unordered/detail/foa/node_set_types.hpp @@ -6,6 +6,7 @@ #define BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP #include +#include #include #include @@ -26,6 +27,9 @@ namespace boost { using element_type = foa::element_type; + using types = node_set_types; + using constructibility_checker = set_types_constructibility; + static value_type& value_from(element_type const& x) { return *x.p; } static Key const& extract(element_type const& k) { return *k.p; } static element_type&& move(element_type& x) { return std::move(x); } @@ -49,6 +53,7 @@ namespace boost { template static void construct(A& al, value_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); boost::allocator_construct(al, p, std::forward(args)...); } @@ -58,8 +63,11 @@ namespace boost { p->p = boost::allocator_allocate(al, 1); BOOST_TRY { + auto address = boost::to_address(p->p); + constructibility_checker::check( + al, address, std::forward(args)...); boost::allocator_construct( - al, boost::to_address(p->p), std::forward(args)...); + al, address, std::forward(args)...); } BOOST_CATCH(...) { diff --git a/include/boost/unordered/detail/foa/types_constructibility.hpp b/include/boost/unordered/detail/foa/types_constructibility.hpp new file mode 100644 index 00000000..10b9208b --- /dev/null +++ b/include/boost/unordered/detail/foa/types_constructibility.hpp @@ -0,0 +1,172 @@ +// Copyright (C) 2024 Braden Ganetsky +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_UNORDERED_DETAIL_FOA_TYPES_CONSTRUCTIBILITY_HPP +#define BOOST_UNORDERED_DETAIL_FOA_TYPES_CONSTRUCTIBILITY_HPP + +#include +#include +#include +#include + +namespace boost { + namespace unordered { + namespace detail { + namespace foa { + template struct check_key_type_t + { + static_assert(std::is_constructible::value, + "key_type must be constructible from Args"); + }; + template struct check_key_type_t + { + static_assert(std::is_constructible::value, + "key_type must be default constructible"); + }; + template struct check_key_type_t + { + static_assert(std::is_constructible::value, + "key_type must be copy constructible"); + }; + template struct check_key_type_t + { + static_assert(std::is_constructible::value, + "key_type must be move constructible"); + }; + + template struct check_mapped_type_t + { + static_assert(std::is_constructible::value, + "mapped_type must be constructible from Args"); + }; + template struct check_mapped_type_t + { + static_assert(std::is_constructible::value, + "mapped_type must be default constructible"); + }; + template + struct check_mapped_type_t + { + static_assert(std::is_constructible::value, + "mapped_type must be copy constructible"); + }; + template struct check_mapped_type_t + { + static_assert(std::is_constructible::value, + "mapped_type must be move constructible"); + }; + + template struct map_types_constructibility + { + using key_type = typename TypePolicy::key_type; + using mapped_type = typename TypePolicy::mapped_type; + using init_type = typename TypePolicy::init_type; + using value_type = typename TypePolicy::value_type; + + template + static void check(A&, X*, Args&&...) + { + // Pass through, as we cannot say anything about a general allocator + } + + template static void check_key_type() + { + (void)check_key_type_t{}; + } + template static void check_mapped_type() + { + (void)check_mapped_type_t{}; + } + + template + static void check(std::allocator&, key_type*, Arg&&) + { + check_key_type(); + } + + template + static void check( + std::allocator&, value_type*, Arg1&&, Arg2&&) + { + check_key_type(); + check_mapped_type(); + } + template + static void check(std::allocator&, value_type*, + const std::pair&) + { + check_key_type(); + check_mapped_type(); + } + template + static void check( + std::allocator&, value_type*, std::pair&&) + { + check_key_type(); + check_mapped_type(); + } + template + static void check(std::allocator&, value_type*, + std::piecewise_construct_t, std::tuple&&, + std::tuple&&) + { + check_key_type(); + check_mapped_type(); + } + + template + static void check( + std::allocator&, init_type*, Arg1&&, Arg2&&) + { + check_key_type(); + check_mapped_type(); + } + template + static void check(std::allocator&, init_type*, + const std::pair&) + { + check_key_type(); + check_mapped_type(); + } + template + static void check( + std::allocator&, init_type*, std::pair&&) + { + check_key_type(); + check_mapped_type(); + } + template + static void check(std::allocator&, init_type*, + std::piecewise_construct_t, std::tuple&&, + std::tuple&&) + { + check_key_type(); + check_mapped_type(); + } + }; + + template struct set_types_constructibility + { + using key_type = typename TypePolicy::key_type; + using value_type = typename TypePolicy::value_type; + static_assert(std::is_same::value, ""); + + template + static void check(A&, X*, Args&&...) + { + // Pass through, as we cannot say anything about a general allocator + } + + template + static void check(std::allocator&, key_type*, Args&&...) + { + (void)check_key_type_t{}; + } + }; + } // namespace foa + } // namespace detail + } // namespace unordered +} // namespace boost + +#endif // BOOST_UNORDERED_DETAIL_FOA_TYPES_CONSTRUCTIBILITY_HPP From 793fad5620a7f9fda20852a987df90f3eb676d09 Mon Sep 17 00:00:00 2001 From: Braden Ganetsky Date: Sat, 3 Aug 2024 11:10:46 -0500 Subject: [PATCH 3/4] Run clang-format on the 'types' files --- include/boost/unordered/detail/foa/flat_map_types.hpp | 4 ++-- include/boost/unordered/detail/foa/flat_set_types.hpp | 4 ++-- include/boost/unordered/detail/foa/node_map_types.hpp | 4 ++-- include/boost/unordered/detail/foa/node_set_types.hpp | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/boost/unordered/detail/foa/flat_map_types.hpp b/include/boost/unordered/detail/foa/flat_map_types.hpp index c7f58706..75069131 100644 --- a/include/boost/unordered/detail/foa/flat_map_types.hpp +++ b/include/boost/unordered/detail/foa/flat_map_types.hpp @@ -87,8 +87,8 @@ namespace boost { } }; } // namespace foa - } // namespace detail - } // namespace unordered + } // namespace detail + } // namespace unordered } // namespace boost #endif // BOOST_UNORDERED_DETAIL_FOA_FLAT_MAP_TYPES_HPP diff --git a/include/boost/unordered/detail/foa/flat_set_types.hpp b/include/boost/unordered/detail/foa/flat_set_types.hpp index 558077f4..3da1f49e 100644 --- a/include/boost/unordered/detail/foa/flat_set_types.hpp +++ b/include/boost/unordered/detail/foa/flat_set_types.hpp @@ -43,8 +43,8 @@ namespace boost { } }; } // namespace foa - } // namespace detail - } // namespace unordered + } // namespace detail + } // namespace unordered } // namespace boost #endif // BOOST_UNORDERED_DETAIL_FOA_FLAT_SET_TYPES_HPP diff --git a/include/boost/unordered/detail/foa/node_map_types.hpp b/include/boost/unordered/detail/foa/node_map_types.hpp index 0b3d4528..789251a6 100644 --- a/include/boost/unordered/detail/foa/node_map_types.hpp +++ b/include/boost/unordered/detail/foa/node_map_types.hpp @@ -142,8 +142,8 @@ namespace boost { }; } // namespace foa - } // namespace detail - } // namespace unordered + } // namespace detail + } // namespace unordered } // namespace boost #endif // BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP diff --git a/include/boost/unordered/detail/foa/node_set_types.hpp b/include/boost/unordered/detail/foa/node_set_types.hpp index 79cf12f0..bc2ee8ac 100644 --- a/include/boost/unordered/detail/foa/node_set_types.hpp +++ b/include/boost/unordered/detail/foa/node_set_types.hpp @@ -93,8 +93,8 @@ namespace boost { }; } // namespace foa - } // namespace detail - } // namespace unordered + } // namespace detail + } // namespace unordered } // namespace boost #endif // BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP From a14b1596650d0c402b8af98f9df0477db9106b3a Mon Sep 17 00:00:00 2001 From: Braden Ganetsky Date: Wed, 7 Aug 2024 16:15:36 -0500 Subject: [PATCH 4/4] Add missing calls to as_const(), otherwise we may call a 'T(T&)' constructor instead of the intended 'T(T const&)' --- include/boost/unordered/detail/foa/node_map_types.hpp | 3 ++- include/boost/unordered/detail/foa/node_set_types.hpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/boost/unordered/detail/foa/node_map_types.hpp b/include/boost/unordered/detail/foa/node_map_types.hpp index 789251a6..f13a7a84 100644 --- a/include/boost/unordered/detail/foa/node_map_types.hpp +++ b/include/boost/unordered/detail/foa/node_map_types.hpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -72,7 +73,7 @@ namespace boost { static void construct( A& al, element_type* p, element_type const& copy) { - construct(al, p, *copy.p); + construct(al, p, detail::as_const(*copy.p)); } template diff --git a/include/boost/unordered/detail/foa/node_set_types.hpp b/include/boost/unordered/detail/foa/node_set_types.hpp index bc2ee8ac..da47793f 100644 --- a/include/boost/unordered/detail/foa/node_set_types.hpp +++ b/include/boost/unordered/detail/foa/node_set_types.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -39,7 +40,7 @@ namespace boost { static void construct( A& al, element_type* p, element_type const& copy) { - construct(al, p, *copy.p); + construct(al, p, detail::as_const(*copy.p)); } template