Pass the element default instance to Add in RepeatedPtrFieldAccessor. Constructing the element using the user-provided to-be-added value is error prone, as it allows users to add a message of an unrelated type to repeated fields. With this change, if the `value` to be added is an unrelated type, `CopyFrom` will crash in `Message::CopyFrom`'s `CHECK_EQ(from.GetDescriptor(), descriptor)`. PiperOrigin-RevId: 950631755
diff --git a/src/google/protobuf/BUILD.bazel b/src/google/protobuf/BUILD.bazel index 24aadcb..261fb06 100644 --- a/src/google/protobuf/BUILD.bazel +++ b/src/google/protobuf/BUILD.bazel
@@ -2564,11 +2564,11 @@ }), deps = [ ":cc_test_protos", - ":port", ":protobuf", ":test_util", "//src/google/protobuf/stubs", - "@abseil-cpp//absl/base", + "@abseil-cpp//absl/log:absl_check", + "@abseil-cpp//absl/strings", "@abseil-cpp//absl/strings:cord", "@googletest//:gtest", "@googletest//:gtest_main",
diff --git a/src/google/protobuf/reflection.h b/src/google/protobuf/reflection.h index fe6e6f9..da3fe68 100644 --- a/src/google/protobuf/reflection.h +++ b/src/google/protobuf/reflection.h
@@ -138,7 +138,8 @@ if constexpr (internal::CanMakeConstSpan<T, Container>::value) { absl::Span<const T> span = absl::MakeConstSpan(container); if (!span.empty()) { - accessor_->AddRange(data_, span.data(), sizeof(T), span.size()); + accessor_->AddRange(data_, span.data(), sizeof(T), span.size(), + /*prototype=*/nullptr); } } else { for (const auto& value : container) { @@ -255,7 +256,9 @@ void Set(int index, const T& value) const { accessor_->Set(data_, index, &value); } - void Add(const T& value) const { accessor_->Add(data_, &value); } + void Add(const T& value) const { + accessor_->Add(data_, &value, default_instance_); + } void RemoveLast() const { accessor_->RemoveLast(data_); } void SwapElements(int index1, int index2) const { accessor_->SwapElements(data_, index1, index2); @@ -271,7 +274,8 @@ if constexpr (internal::CanMakeConstSpan<T, Container>::value) { absl::Span<const T> span = absl::MakeConstSpan(container); if (!span.empty()) { - accessor_->AddRange(data_, span.data(), sizeof(T), span.size()); + accessor_->AddRange(data_, span.data(), sizeof(T), span.size(), + default_instance_); } } else { for (const auto& value : container) { @@ -353,10 +357,12 @@ virtual void Set(Field* PROTOBUF_NONNULL data, int index, const Value* PROTOBUF_NONNULL value) const = 0; virtual void Add(Field* PROTOBUF_NONNULL data, - const Value* PROTOBUF_NONNULL value) const = 0; + const Value* PROTOBUF_NONNULL value, + const Value* PROTOBUF_NULLABLE prototype) const = 0; virtual void AddRange(Field* PROTOBUF_NONNULL data, const Value* PROTOBUF_NONNULL values, int value_size, - size_t size) const = 0; + size_t size, + const Value* PROTOBUF_NULLABLE prototype) const = 0; virtual void RemoveLast(Field* PROTOBUF_NONNULL data) const = 0; virtual void SwapElements(Field* PROTOBUF_NONNULL data, int index1, int index2) const = 0; @@ -429,7 +435,7 @@ // may be a generated enum type while ActualType is int32_t). To be safe // we make a copy to get a temporary ActualType object and use it. ActualType tmp = static_cast<ActualType>(value); - Add(data, static_cast<const Value*>(&tmp)); + Add(data, static_cast<const Value*>(&tmp), /*prototype=*/nullptr); } protected:
diff --git a/src/google/protobuf/reflection_internal.h b/src/google/protobuf/reflection_internal.h index 5947dba..f4bc1fb 100644 --- a/src/google/protobuf/reflection_internal.h +++ b/src/google/protobuf/reflection_internal.h
@@ -15,6 +15,7 @@ #include "absl/log/absl_check.h" #include "absl/strings/cord.h" #include "google/protobuf/map_field.h" +#include "google/protobuf/message.h" #include "google/protobuf/port.h" #include "google/protobuf/reflection.h" #include "google/protobuf/repeated_field.h" @@ -89,7 +90,8 @@ void Set(Field* data, int index, const Value* value) const override { MutableRepeatedField(data)->Set(index, ConvertToT(value)); } - void Add(Field* data, const Value* value) const override { + void Add(Field* data, const Value* value, + const Value* /*prototype*/) const override { MutableRepeatedField(data)->Add(ConvertToT(value)); } void RemoveLast(Field* data) const override { @@ -147,19 +149,20 @@ void Set(Field* data, int index, const Value* value) const override { ConvertToT(value, MutableRepeatedField(data)->Mutable(index)); } - void Add(Field* data, const Value* value) const override { - T* allocated = New(value); + void Add(Field* data, const Value* value, + const Value* prototype) const override { + T* allocated = New(prototype); ConvertToT(value, allocated); MutableRepeatedField(data)->AddAllocated(allocated); } - void AddRange(Field* data, const Value* values, int value_size, - size_t size) const override { + void AddRange(Field* data, const Value* values, int value_size, size_t size, + const Value* prototype) const override { auto* repeated = MutableRepeatedField(data); int old_size = repeated->size(); repeated->Reserve(internal::CheckedAdd(old_size, size)); const char* ptr = reinterpret_cast<const char*>(values); for (size_t i = 0; i < size; ++i) { - Add(data, ptr); + Add(data, ptr, prototype); ptr += value_size; } } @@ -185,10 +188,9 @@ } // Create a new T instance. For repeated message fields, T can be specified - // as google::protobuf::Message so we can't use "new T()" directly. In that case, value - // should be a message of the same type (it's ensured by the caller) and a - // new message object will be created using it. - virtual T* New(const Value* value) const = 0; + // as google::protobuf::Message so we can't use "new T()" directly. In that case, + // `prototype` should the prototype of the message to be created. + virtual T* New(const Value* prototype) const = 0; // Convert an object received by this accessor to an object that will be // stored in the underlying RepeatedPtrField. @@ -222,8 +224,8 @@ MutableRepeatedField(data)->Swap(MutableRepeatedField(other_data)); } - void AddRange(Field* data, const Value* values, int value_size, - size_t size) const override { + void AddRange(Field* data, const Value* values, int value_size, size_t size, + const Value* /*prototype*/) const override { const T* ptr = reinterpret_cast<const T*>(values); MutableRepeatedField(data)->Add(ptr, ptr + size); } @@ -296,8 +298,8 @@ } protected: - Message* New(const Value* value) const override { - return static_cast<const Message*>(value)->New(); + Message* New(const Value* prototype) const override { + return static_cast<const Message*>(prototype)->New(); } void ConvertToT(const Value* value, Message* result) const override { result->CopyFrom(*static_cast<const Message*>(value));
diff --git a/src/google/protobuf/repeated_field_reflection_unittest.inc b/src/google/protobuf/repeated_field_reflection_unittest.inc index 3dcf128..90317d7 100644 --- a/src/google/protobuf/repeated_field_reflection_unittest.inc +++ b/src/google/protobuf/repeated_field_reflection_unittest.inc
@@ -10,11 +10,16 @@ // Test reflection methods for aggregate access to Repeated[Ptr]Fields. // This test proto2 methods on a proto2 layout. +#include <cstdint> +#include <string> + #include <gtest/gtest.h> -#include "absl/base/casts.h" +#include "absl/log/absl_check.h" #include "absl/strings/cord.h" +#include "absl/strings/str_cat.h" +#include "google/protobuf/descriptor.h" #include "google/protobuf/dynamic_message.h" -#include "google/protobuf/port.h" +#include "google/protobuf/message.h" #include "google/protobuf/reflection.h" namespace google { @@ -682,6 +687,23 @@ refl->GetRepeatedInt32(*dynamic_message, fd_repeated_int32, 0)); } +#if GTEST_HAS_DEATH_TEST +TEST(REFLECTION_TEST, RepeatedFieldRefAddRejectsUnrelatedMessageType) { + TestAllTypes m0; + const Reflection* refl = m0.GetReflection(); + const Descriptor* desc = m0.GetDescriptor(); + + const FieldDescriptor* fd_repeated_nested_message = + desc->FindFieldByName("repeated_nested_message"); + auto rf_nested_message = refl->GetMutableRepeatedFieldRef<Message>( + &m0, fd_repeated_nested_message); + + TestAllTypes m1; + EXPECT_DEATH(rf_nested_message.Add(m1), + "Tried to copy from a message with a different type."); +} +#endif // GTEST_HAS_DEATH_TEST + } // namespace } // namespace protobuf } // namespace google