diff --git a/absl/base/config.h b/absl/base/config.h index 8e11df2aba4..b8ae9b6857d 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -112,7 +112,7 @@ // LTS releases can be obtained from // https://github.com/abseil/abseil-cpp/releases. #define ABSL_LTS_RELEASE_VERSION 20230802 -#define ABSL_LTS_RELEASE_PATCH_LEVEL 2 +#define ABSL_LTS_RELEASE_PATCH_LEVEL 3 // Helper macro to convert a CPP variable to a string literal. #define ABSL_INTERNAL_DO_TOKEN_STR(x) #x diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 5f89d8efee6..92b93453314 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -1076,6 +1076,12 @@ inline size_t NormalizeCapacity(size_t n) { return n ? ~size_t{} >> countl_zero(n) : 1; } +template +size_t MaxValidCapacity() { + return NormalizeCapacity((std::numeric_limits::max)() / 4 / + kSlotSize); +} + // General notes on capacity/growth methods below: // - We use 7/8th as maximum load factor. For 16-wide groups, that gives an // average of two empty slots per group. @@ -1717,6 +1723,8 @@ class raw_hash_set { const allocator_type& alloc = allocator_type()) : settings_(CommonFields{}, hash, eq, alloc) { if (bucket_count) { + ABSL_RAW_CHECK(bucket_count <= MaxValidCapacity(), + "Hash table size overflow"); common().set_capacity(NormalizeCapacity(bucket_count)); initialize_slots(); } @@ -1916,7 +1924,10 @@ class raw_hash_set { bool empty() const { return !size(); } size_t size() const { return common().size(); } size_t capacity() const { return common().capacity(); } - size_t max_size() const { return (std::numeric_limits::max)(); } + size_t max_size() const { + return CapacityToGrowth(MaxValidCapacity()); + } + ABSL_ATTRIBUTE_REINITIALIZES void clear() { // Iterating over this container is O(bucket_count()). When bucket_count() @@ -2266,6 +2277,8 @@ class raw_hash_set { auto m = NormalizeCapacity(n | GrowthToLowerboundCapacity(size())); // n == 0 unconditionally rehashes as per the standard. if (n == 0 || m > capacity()) { + ABSL_RAW_CHECK(m <= MaxValidCapacity(), + "Hash table size overflow"); resize(m); // This is after resize, to ensure that we have completed the allocation @@ -2276,6 +2289,7 @@ class raw_hash_set { void reserve(size_t n) { if (n > size() + growth_left()) { + ABSL_RAW_CHECK(n <= max_size(), "Hash table size overflow"); size_t m = GrowthToLowerboundCapacity(n); resize(NormalizeCapacity(m)); diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index 242a97cbe3f..d5d5f3934da 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc @@ -2510,6 +2510,14 @@ TEST(Iterator, InvalidComparisonDifferentTables) { "Invalid iterator comparison.*non-end"); } +TEST(Table, MaxSizeOverflow) { + size_t overflow = (std::numeric_limits::max)(); + EXPECT_DEATH_IF_SUPPORTED(IntTable t(overflow), "Hash table size overflow"); + IntTable t; + EXPECT_DEATH_IF_SUPPORTED(t.reserve(overflow), "Hash table size overflow"); + EXPECT_DEATH_IF_SUPPORTED(t.rehash(overflow), "Hash table size overflow"); +} + } // namespace } // namespace container_internal ABSL_NAMESPACE_END