Mitigate use-after-free due to overflow of RefcountAndFlags::count_ Detects refcounts for Cord going too high, resulting in a fatal error in that case. Since the check happens after the increment, we stay conservative with the bounds and leave a good deal of margin to mitigate the chances of Unref() racing with Ref() and happening earlier than its check, which could still result in a use-after-free. PiperOrigin-RevId: 945345773 Change-Id: Ide1151618ae836fd30dbc0a476e29ba85ca45862
diff --git a/absl/strings/internal/cord_internal.cc b/absl/strings/internal/cord_internal.cc index 57d9d38..becfc6a 100644 --- a/absl/strings/internal/cord_internal.cc +++ b/absl/strings/internal/cord_internal.cc
@@ -31,6 +31,10 @@ ABSL_CONST_INIT std::atomic<bool> shallow_subcords_enabled( kCordShallowSubcordsDefault); +void RefcountAndFlags::IncrementOverflow() { + ABSL_INTERNAL_LOG(FATAL, "refcount is too large and vulnerable to overflow"); +} + void LogFatalNodeType(CordRep* rep) { ABSL_INTERNAL_LOG(FATAL, absl::StrCat("Unexpected node type: ", static_cast<int>(rep->tag)));
diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index 27a8b9f..98dc4b5 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h
@@ -20,11 +20,13 @@ #include <cstddef> #include <cstdint> #include <cstring> +#include <limits> #include <string> #include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/base/internal/endian.h" +#include "absl/base/internal/raw_logging.h" #include "absl/base/macros.h" #include "absl/base/nullability.h" #include "absl/base/optimization.h" @@ -133,9 +135,18 @@ struct Immortal {}; explicit constexpr RefcountAndFlags(Immortal) : count_(kImmortalFlag) {} + static void IncrementOverflow(); + // Increments the reference count. Imposes no memory ordering. inline void Increment() { - count_.fetch_add(kRefIncrement, std::memory_order_relaxed); + const int32_t prev_count = + count_.fetch_add(kRefIncrement, std::memory_order_relaxed); + if (ABSL_PREDICT_FALSE( + prev_count >= + ((std::numeric_limits<decltype(count_)::value_type>::max)() / 3) * + 2)) { + IncrementOverflow(); + } } // Asserts that the current refcount is greater than 0. If the refcount is