Skip to content

Commit d21e8ef

Browse files
Control bounds checks via BUILD flags.
Create BUILD flag for bounds_check_mode. Inject macros into repeated_field.h and repeated_ptr_field.h to control the behavior of `Mutable` and `Get` accordingly. PiperOrigin-RevId: 728507203
1 parent 37b6652 commit d21e8ef

File tree

3 files changed

+77
-16
lines changed

3 files changed

+77
-16
lines changed

src/google/protobuf/port.h

+12
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,18 @@ class alignas(8) GlobalEmptyString {
524524
PROTOBUF_EXPORT extern GlobalEmptyString fixed_address_empty_string;
525525
#endif
526526

527+
enum class BoundsCheckMode { kNoEnforcement, kReturnDefault, kAbort };
528+
529+
PROTOBUF_EXPORT constexpr BoundsCheckMode GetBoundsCheckMode() {
530+
#if defined(PROTOBUF_INTERNAL_BOUNDS_CHECK_MODE_ABORT)
531+
return BoundsCheckMode::kAbort;
532+
#elif defined(PROTOBUF_INTERNAL_BOUNDS_CHECK_MODE_RETURN_DEFAULT)
533+
return BoundsCheckMode::kReturnDefault;
534+
#else
535+
return BoundsCheckMode::kNoEnforcement;
536+
#endif
537+
}
538+
527539

528540
} // namespace internal
529541
} // namespace protobuf

src/google/protobuf/repeated_field.h

+15-4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <algorithm>
2525
#include <cstddef>
2626
#include <cstdint>
27+
#include <cstdlib>
2728
#include <cstring>
2829
#include <iterator>
2930
#include <limits>
@@ -803,8 +804,13 @@ inline void RepeatedField<Element>::Resize(int new_size, const Element& value) {
803804
template <typename Element>
804805
inline const Element& RepeatedField<Element>::Get(int index) const
805806
ABSL_ATTRIBUTE_LIFETIME_BOUND {
806-
ABSL_DCHECK_GE(index, 0);
807-
ABSL_DCHECK_LT(index, size());
807+
if constexpr (internal::GetBoundsCheckMode() ==
808+
internal::BoundsCheckMode::kAbort) {
809+
internal::RuntimeAssertInBounds(index, size());
810+
} else {
811+
ABSL_DCHECK_GE(index, 0);
812+
ABSL_DCHECK_LT(index, size());
813+
}
808814
return elements(is_soo())[index];
809815
}
810816

@@ -827,8 +833,13 @@ inline Element& RepeatedField<Element>::at(int index)
827833
template <typename Element>
828834
inline Element* RepeatedField<Element>::Mutable(int index)
829835
ABSL_ATTRIBUTE_LIFETIME_BOUND {
830-
ABSL_DCHECK_GE(index, 0);
831-
ABSL_DCHECK_LT(index, size());
836+
if constexpr (internal::GetBoundsCheckMode() ==
837+
internal::BoundsCheckMode::kAbort) {
838+
internal::RuntimeAssertInBounds(index, size());
839+
} else {
840+
ABSL_DCHECK_GE(index, 0);
841+
ABSL_DCHECK_LT(index, size());
842+
}
832843
return &elements(is_soo())[index];
833844
}
834845

src/google/protobuf/repeated_ptr_field.h

+50-12
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,20 @@ struct ArenaOffsetHelper {
9595
PROTOBUF_EXPORT MessageLite* CloneSlow(Arena* arena, const MessageLite& value);
9696
PROTOBUF_EXPORT std::string* CloneSlow(Arena* arena, const std::string& value);
9797

98+
// A utility function for logging that doesn't need any template types.
99+
PROTOBUF_EXPORT void LogIndexOutOfBounds(int index, int size);
100+
101+
// A utility function for logging that doesn't need any template types. Same as
102+
// LogIndexOutOfBounds, but aborts the program in all cases by logging to FATAL
103+
// instead of DFATAL.
104+
[[noreturn]] PROTOBUF_EXPORT void LogIndexOutOfBoundsAndAbort(int index,
105+
int size);
106+
PROTOBUF_EXPORT inline void RuntimeAssertInBounds(int index, int size) {
107+
if (ABSL_PREDICT_FALSE(index < 0 || index >= size)) {
108+
LogIndexOutOfBoundsAndAbort(index, size);
109+
}
110+
}
111+
98112
// Defined further below.
99113
template <typename Type>
100114
class GenericTypeHandler;
@@ -184,8 +198,12 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase {
184198

185199
template <typename TypeHandler>
186200
Value<TypeHandler>* Mutable(int index) {
187-
ABSL_DCHECK_GE(index, 0);
188-
ABSL_DCHECK_LT(index, current_size_);
201+
if constexpr (GetBoundsCheckMode() == BoundsCheckMode::kAbort) {
202+
RuntimeAssertInBounds(index, current_size_);
203+
} else {
204+
ABSL_DCHECK_GE(index, 0);
205+
ABSL_DCHECK_LT(index, current_size_);
206+
}
189207
return cast<TypeHandler>(element_at(index));
190208
}
191209

@@ -251,8 +269,22 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase {
251269

252270
template <typename TypeHandler>
253271
const Value<TypeHandler>& Get(int index) const {
254-
ABSL_DCHECK_GE(index, 0);
255-
ABSL_DCHECK_LT(index, current_size_);
272+
if constexpr (GetBoundsCheckMode() == BoundsCheckMode::kReturnDefault) {
273+
if (ABSL_PREDICT_FALSE(index < 0 || index >= current_size_)) {
274+
// `default_instance()` is not supported for MessageLite and Message.
275+
if constexpr (TypeHandler::has_default_instance()) {
276+
LogIndexOutOfBounds(index, current_size_);
277+
return TypeHandler::default_instance();
278+
}
279+
}
280+
} else if constexpr (GetBoundsCheckMode() == BoundsCheckMode::kAbort) {
281+
// We refactor this to a separate function instead of inlining it so we
282+
// can measure the performance impact more easily.
283+
RuntimeAssertInBounds(index, current_size_);
284+
} else {
285+
ABSL_DCHECK_GE(index, 0);
286+
ABSL_DCHECK_LT(index, current_size_);
287+
}
256288
return *cast<TypeHandler>(element_at(index));
257289
}
258290

@@ -847,19 +879,31 @@ class GenericTypeHandler {
847879
return *static_cast<const GenericType*>(
848880
MessageTraits<Type>::default_instance());
849881
}
882+
883+
static constexpr bool has_default_instance() { return true; }
850884
};
851885

852886
template <>
853887
inline Arena* GenericTypeHandler<MessageLite>::GetArena(MessageLite* value) {
854888
return value->GetArena();
855889
}
856890

891+
template <>
892+
inline constexpr bool GenericTypeHandler<MessageLite>::has_default_instance() {
893+
return false;
894+
}
895+
857896
// Message specialization bodies defined in message.cc. This split is necessary
858897
// to allow proto2-lite (which includes this header) to be independent of
859898
// Message.
860899
template <>
861900
PROTOBUF_EXPORT Arena* GenericTypeHandler<Message>::GetArena(Message* value);
862901

902+
template <>
903+
inline constexpr bool GenericTypeHandler<Message>::has_default_instance() {
904+
return false;
905+
}
906+
863907
PROTOBUF_EXPORT void* NewStringElement(Arena* arena);
864908

865909
template <>
@@ -887,6 +931,8 @@ class GenericTypeHandler<std::string> {
887931
static const Type& default_instance() {
888932
return GetEmptyStringAlreadyInited();
889933
}
934+
935+
static constexpr bool has_default_instance() { return true; }
890936
};
891937

892938
} // namespace internal
@@ -1981,14 +2027,6 @@ class UnsafeArenaAllocatedRepeatedPtrFieldBackInsertIterator {
19812027
RepeatedPtrField<T>* field_;
19822028
};
19832029

1984-
// A utility function for logging that doesn't need any template types.
1985-
PROTOBUF_EXPORT void LogIndexOutOfBounds(int index, int size);
1986-
1987-
// A utility function for logging that doesn't need any template types. Same as
1988-
// LogIndexOutOfBounds, but aborts the program in all cases by logging to FATAL
1989-
// instead of DFATAL.
1990-
[[noreturn]] PROTOBUF_EXPORT void LogIndexOutOfBoundsAndAbort(int index,
1991-
int size);
19922030

19932031
template <typename T>
19942032
const T& CheckedGetOrDefault(const RepeatedPtrField<T>& field, int index) {

0 commit comments

Comments
 (0)