CVE-2025-0838 - Heap buffer overflow vulnerable in Abseil-cpp
authorBenjamin Barenblat <bbaren@debian.org>
Mon, 12 May 2025 15:26:59 +0000 (17:26 +0200)
committerTobias Frost <tobi@debian.org>
Mon, 12 May 2025 15:26:59 +0000 (17:26 +0200)
Origin: https://github.com/abseil/abseil-cpp/commit/5a0e2cb5e3958dd90bb8569a2766622cb74d90c1
Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1098903

Backported for bookworm from upstream.
From 5a0e2cb5e3958dd90bb8569a2766622cb74d90c1 Mon Sep 17 00:00:00 2001
From: Derek Mauro <dmauro@google.com>
Date: Thu, 23 Jan 2025 06:33:43 -0800
Subject: [PATCH] Fix potential integer overflow in hash container
 create/resize

The sized constructors, reserve(), and rehash() methods of
absl::{flat,node}_hash_{set,map} did not impose an upper bound on
their size argument. As a result, it was possible for a caller to pass
a very large size that would cause an integer overflow when computing
the size of the container's backing store. Subsequent accesses to the
container might then access out-of-bounds memory.

The fix is in two parts:

1) Update max_size() to return the maximum number of items that can be
stored in the container

2) Validate the size arguments to the constructors, reserve(), and
rehash() methods, and abort the program when the argument is invalid

We've looked at uses of these containers in Google codebases like
Chrome, and determined this vulnerability is likely to be difficult to
exploit. This is primarily because container sizes are rarely
attacker-controlled.

The bug was discovered by Dmitry Vyukov <dvyukov@google.com>.

PiperOrigin-RevId: 718841870
Change-Id: Ic09dc9de140a35dbb45ab9d90f58383cf2de8286

Gbp-Pq: Name CVE-2025-0838.patch

absl/container/internal/raw_hash_set.cc
absl/container/internal/raw_hash_set.h
absl/container/internal/raw_hash_set_test.cc

index c63a2e02d1138bf5cab9c313954f9c78f27a0415..c2a4c17063d646d84931630cfdfc7f144fa6a7ac 100644 (file)
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "absl/container/internal/raw_hash_set.h"
+#include "absl/base/internal/raw_logging.h"
 
 #include <atomic>
 #include <cstddef>
@@ -66,6 +67,10 @@ void ConvertDeletedToEmptyAndFullToDeleted(ctrl_t* ctrl, size_t capacity) {
 // Extern template instantiotion for inline function.
 template FindInfo find_first_non_full(const ctrl_t*, size_t, size_t);
 
+void HashTableSizeOverflow() {
+  ABSL_RAW_LOG(FATAL, "Hash table size overflow");
+}
+
 }  // namespace container_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
index ea912f8305ff698d1282a611f16eea28cfafe8de..70eb5976cecdd90f7306d2eca27b7be07d782685 100644 (file)
@@ -219,6 +219,15 @@ namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace container_internal {
 
+#ifdef ABSL_SWISSTABLE_ASSERT
+#error ABSL_SWISSTABLE_ASSERT cannot be directly set
+#else
+// We use this macro for assertions that users may see when the table is in an
+// invalid state that sanitizers may help diagnose.
+#define ABSL_SWISSTABLE_ASSERT(CONDITION) \
+  assert((CONDITION) && "Try enabling sanitizers.")
+#endif
+
 template <typename AllocType>
 void SwapAlloc(AllocType& lhs, AllocType& rhs,
                std::true_type /* propagate_on_container_swap */) {
@@ -745,6 +754,15 @@ inline size_t NormalizeCapacity(size_t n) {
   return n ? ~size_t{} >> countl_zero(n) : 1;
 }
 
+template <size_t kSlotSize>
+size_t MaxValidCapacity() {
+  return NormalizeCapacity((std::numeric_limits<size_t>::max)() / 4 /
+                           kSlotSize);
+}
+
+// Use a non-inlined function to avoid code bloat.
+[[noreturn]] void HashTableSizeOverflow();
+
 // 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.
@@ -1148,6 +1166,10 @@ class raw_hash_set {
       : ctrl_(EmptyGroup()),
         settings_(0, HashtablezInfoHandle(), hash, eq, alloc) {
     if (bucket_count) {
+      if (ABSL_PREDICT_FALSE(bucket_count >
+                             MaxValidCapacity<sizeof(slot_type)>())) {
+        HashTableSizeOverflow();
+      }
       capacity_ = NormalizeCapacity(bucket_count);
       initialize_slots();
     }
@@ -1341,7 +1363,9 @@ class raw_hash_set {
   bool empty() const { return !size(); }
   size_t size() const { return size_; }
   size_t capacity() const { return capacity_; }
-  size_t max_size() const { return (std::numeric_limits<size_t>::max)(); }
+  size_t max_size() const {
+    return CapacityToGrowth(MaxValidCapacity<sizeof(slot_type)>());
+  }
 
   ABSL_ATTRIBUTE_REINITIALIZES void clear() {
     // Iterating over this container is O(bucket_count()). When bucket_count()
@@ -1678,6 +1702,9 @@ class raw_hash_set {
     auto m = NormalizeCapacity(n | GrowthToLowerboundCapacity(size()));
     // n == 0 unconditionally rehashes as per the standard.
     if (n == 0 || m > capacity_) {
+      if (ABSL_PREDICT_FALSE(m > MaxValidCapacity<sizeof(slot_type)>())) {
+        HashTableSizeOverflow();
+      }
       resize(m);
 
       // This is after resize, to ensure that we have completed the allocation
@@ -1688,6 +1715,9 @@ class raw_hash_set {
 
   void reserve(size_t n) {
     if (n > size() + growth_left()) {
+      if (ABSL_PREDICT_FALSE(n > max_size())) {
+        HashTableSizeOverflow();
+      }
       size_t m = GrowthToLowerboundCapacity(n);
       resize(NormalizeCapacity(m));
 
@@ -2361,5 +2391,6 @@ ABSL_NAMESPACE_END
 }  // namespace absl
 
 #undef ABSL_INTERNAL_ASSERT_IS_FULL
+#undef ABSL_SWISSTABLE_ASSERT
 
 #endif  // ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_
index f77ffbc11c51791079b7ec7abccd405969e9a436..078bbadc141b1ad01174ae7411493d58730ebe22 100644 (file)
@@ -2181,6 +2181,14 @@ TEST(Table, AlignOne) {
   }
 }
 
+TEST(Table, MaxSizeOverflow) {
+  size_t overflow = (std::numeric_limits<size_t>::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