Check consistency of ClassData types of RepeatedPtrField members in the destructor.

The repeated field destructor caches the element destructor and calls it on each element of a repeated field, rather than going through full dynamic dispatch for each element. Currently, if the elements of a repeated field are of different types, this code may segfault in non-obvious ways (e.g. mismatch size-delete, or memory corruption). The DCHECK gives a better error message when you have repeated fields of mixed element types.

This code is only active with `enable_custom_vtable`.

PiperOrigin-RevId: 966610091
diff --git a/src/google/protobuf/class_data.h b/src/google/protobuf/class_data.h
index 806aab2..8fed1be 100644
--- a/src/google/protobuf/class_data.h
+++ b/src/google/protobuf/class_data.h
@@ -16,6 +16,7 @@
 
 #include <cstddef>
 #include <cstdint>
+#include <string>
 
 #include "absl/log/absl_check.h"
 #include "google/protobuf/port.h"
@@ -227,6 +228,8 @@
         << " and " << from.GetTypeName();
     this->merge_to_from(to, from);
   }
+
+  std::string DebugName() const;
 };
 
 #ifndef PROTOBUF_MESSAGE_GLOBALS
diff --git a/src/google/protobuf/message_lite.cc b/src/google/protobuf/message_lite.cc
index 755b2a9..23f810f 100644
--- a/src/google/protobuf/message_lite.cc
+++ b/src/google/protobuf/message_lite.cc
@@ -202,6 +202,16 @@
 
 namespace internal {
 
+std::string ClassData::DebugName() const {
+  absl::string_view type_name = TypeIdFromClassData(this).name();
+  if (is_dynamic) {
+    return absl::StrCat(type_name, " (dynamic, class_data = 0x",
+                        absl::Hex(this), ")");
+  } else {
+    return std::string(type_name);
+  }
+}
+
 void FailDynamicCast(
     const MessageLite& from,
     std::variant<const char*, const MessageLite*> to_type_name) {
diff --git a/src/google/protobuf/repeated_ptr_field.cc b/src/google/protobuf/repeated_ptr_field.cc
index 9a96301..3915804 100644
--- a/src/google/protobuf/repeated_ptr_field.cc
+++ b/src/google/protobuf/repeated_ptr_field.cc
@@ -27,6 +27,7 @@
 #include "google/protobuf/message_traits.h"
 #include "google/protobuf/port.h"
 #include "google/protobuf/repeated_field.h"
+#include "google/protobuf/type_id.h"
 
 // Must be included last.
 #include "google/protobuf/port_def.inc"
@@ -118,6 +119,13 @@
       absl::PrefetchToLocalCacheNta(elems[i + 5]);
     }
     auto* ptr = cast<H>(elems[i]);
+
+    ABSL_DCHECK_EQ(GetClassData(*ptr), class_data)
+        << "Type mismatch in RepeatedPtrFieldBase::DestroyMessageLites: found "
+           "element of type "
+        << GetClassData(*ptr)->DebugName() << " at index " << i
+        << " in a repeated field of " << class_data->DebugName();
+
     destroy(*ptr);
     internal::SizedDelete(ptr, allocation_size);
   }
diff --git a/src/google/protobuf/repeated_ptr_field_unittest.cc b/src/google/protobuf/repeated_ptr_field_unittest.cc
index 87e179f..2f3a62b 100644
--- a/src/google/protobuf/repeated_ptr_field_unittest.cc
+++ b/src/google/protobuf/repeated_ptr_field_unittest.cc
@@ -326,6 +326,40 @@
   EXPECT_GE(field.SpaceUsedExcludingSelf(), min_expected_usage);
 }
 
+TEST_F(RepeatedPtrFieldTest, DestroyErroneousIncorrectElement) {
+#if defined(NDEBUG) || !defined(PROTOBUF_CUSTOM_VTABLE)
+  GTEST_SKIP() << "The `DCHECK` is disabled in release builds / "
+                  "!PROTOBUF_CUSTOM_VTABLE.";
+#else
+  auto destroy_wrong_element_type = []() {
+    RepeatedPtrField<TestAllTypes> field;
+    field.AddAllocated(reinterpret_cast<TestAllTypes*>(
+        new proto2_unittest::NestedTestAllTypes));
+  };
+  // The destructor of `RepeatedPtrField` verifies that all elements are of the
+  // expected type.
+  ASSERT_DEATH(destroy_wrong_element_type(),
+               "Type mismatch in RepeatedPtrFieldBase::DestroyMessageLites");
+#endif
+}
+
+TEST_F(RepeatedPtrFieldTest, DestroyErroneousMixedElements) {
+#if defined(NDEBUG) || !defined(PROTOBUF_CUSTOM_VTABLE)
+  GTEST_SKIP() << "The `DCHECK` is disabled in release builds / "
+                  "!PROTOBUF_CUSTOM_VTABLE.";
+#else
+  auto destroy_heterogenous_repeated_field = []() {
+    RepeatedPtrField<google::protobuf::MessageLite> field;
+    field.AddAllocated(new TestAllTypes);
+    field.AddAllocated(new proto2_unittest::NestedTestAllTypes);
+  };
+  // The destructor of `RepeatedPtrField` verifies that all elements are of the
+  // same type if the element type is `Message` or `MessageLite`.
+  ASSERT_DEATH(destroy_heterogenous_repeated_field(),
+               "Type mismatch in RepeatedPtrFieldBase::DestroyMessageLites");
+#endif
+}
+
 namespace {
 
 template <typename Elem>
diff --git a/src/google/protobuf/type_id.h b/src/google/protobuf/type_id.h
index 3679a09..5dd5561 100644
--- a/src/google/protobuf/type_id.h
+++ b/src/google/protobuf/type_id.h
@@ -23,9 +23,11 @@
 namespace protobuf {
 
 class MessageLite;
+class TypeId;
 
 namespace internal {
 struct ClassData;
+TypeId TypeIdFromClassData(const ClassData* data);
 }  // namespace internal
 
 // A `std::type_info` equivalent for protobuf message types.
@@ -97,11 +99,21 @@
   }
 
  private:
+  friend TypeId internal::TypeIdFromClassData(const internal::ClassData* data);
+
   constexpr explicit TypeId(const internal::ClassData* data) : data_(data) {}
 
   const internal::ClassData* data_;
 };
 
+namespace internal {
+
+inline TypeId TypeIdFromClassData(const ClassData* data) {
+  return TypeId(data);
+}
+
+}  // namespace internal
+
 }  // namespace protobuf
 }  // namespace google