[ObjC] Enforcement of the 2GB limit when parsing messages.

Add a helper to put the logic in one place in the file.

Ensure all places that read messages also do the 2GB check for message size.

PiperOrigin-RevId: 511260874
pull/11959/head
Protobuf Team Bot 2 years ago committed by Copybara-Service
parent 68b8b43d42
commit cffde99fc6
  1. 49
      objectivec/GPBCodedInputStream.m

@ -51,10 +51,6 @@ NSString *const GPBCodedInputStreamErrorDomain =
// int CodedInputStream::default_recursion_limit_ = 100;
static const NSUInteger kDefaultRecursionLimit = 100;
// Bytes and Strings have a max size of 2GB.
// https://protobuf.dev/programming-guides/encoding/#cheat-sheet
static const uint32_t kMaxFieldSize = 0x7fffffff;
static void RaiseException(NSInteger code, NSString *reason) {
NSDictionary *errorInfo = nil;
if ([reason length]) {
@ -69,12 +65,24 @@ static void RaiseException(NSInteger code, NSString *reason) {
userInfo:exceptionInfo] raise];
}
static void CheckRecursionLimit(GPBCodedInputStreamState *state) {
GPB_INLINE void CheckRecursionLimit(GPBCodedInputStreamState *state) {
if (state->recursionDepth >= kDefaultRecursionLimit) {
RaiseException(GPBCodedInputStreamErrorRecursionDepthExceeded, nil);
}
}
GPB_INLINE void CheckFieldSize(uint64_t size) {
// Bytes and Strings have a max size of 2GB. And since messages are on the wire as bytes/length
// delimited, they also have a 2GB size limit. The C++ does the same sort of enforcement (see
// parse_context, delimited_message_util, message_lite, etc.).
// https://protobuf.dev/programming-guides/encoding/#cheat-sheet
if (size > 0x7fffffff) {
// TODO(thomasvl): Maybe a different error code for this, but adding one is a breaking
// change so reuse an existing one.
RaiseException(GPBCodedInputStreamErrorInvalidSize, nil);
}
}
static void CheckSize(GPBCodedInputStreamState *state, size_t size) {
size_t newSize = state->bufferPos + size;
if (newSize > state->bufferSize) {
@ -228,11 +236,7 @@ int32_t GPBCodedInputStreamReadTag(GPBCodedInputStreamState *state) {
NSString *GPBCodedInputStreamReadRetainedString(GPBCodedInputStreamState *state) {
uint64_t size = GPBCodedInputStreamReadUInt64(state);
if (size > kMaxFieldSize) {
// TODO(thomasvl): Maybe a different error code for this, but adding one is a breaking change
// so reuse an existing one.
RaiseException(GPBCodedInputStreamErrorInvalidSize, nil);
}
CheckFieldSize(size);
NSUInteger ns_size = (NSUInteger)size;
NSString *result;
if (size == 0) {
@ -257,11 +261,7 @@ NSString *GPBCodedInputStreamReadRetainedString(GPBCodedInputStreamState *state)
NSData *GPBCodedInputStreamReadRetainedBytes(GPBCodedInputStreamState *state) {
uint64_t size = GPBCodedInputStreamReadUInt64(state);
if (size > kMaxFieldSize) {
// TODO(thomasvl): Maybe a different error code for this, but adding one is a breaking change
// so reuse an existing one.
RaiseException(GPBCodedInputStreamErrorInvalidSize, nil);
}
CheckFieldSize(size);
NSUInteger ns_size = (NSUInteger)size;
CheckSize(state, size);
NSData *result = [[NSData alloc] initWithBytes:state->bytes + state->bufferPos length:ns_size];
@ -271,11 +271,7 @@ NSData *GPBCodedInputStreamReadRetainedBytes(GPBCodedInputStreamState *state) {
NSData *GPBCodedInputStreamReadRetainedBytesNoCopy(GPBCodedInputStreamState *state) {
uint64_t size = GPBCodedInputStreamReadUInt64(state);
if (size > kMaxFieldSize) {
// TODO(thomasvl): Maybe a different error code for this, but adding one is a breaking change
// so reuse an existing one.
RaiseException(GPBCodedInputStreamErrorInvalidSize, nil);
}
CheckFieldSize(size);
NSUInteger ns_size = (NSUInteger)size;
CheckSize(state, size);
// Cast is safe because freeWhenDone is NO.
@ -362,9 +358,12 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state, int32_t
case GPBWireFormatFixed64:
SkipRawData(&state_, sizeof(int64_t));
return YES;
case GPBWireFormatLengthDelimited:
SkipRawData(&state_, ReadRawVarint32(&state_));
case GPBWireFormatLengthDelimited: {
uint64_t size = GPBCodedInputStreamReadUInt64(&state_);
CheckFieldSize(size);
SkipRawData(&state_, size);
return YES;
}
case GPBWireFormatStartGroup:
[self skipMessage];
GPBCodedInputStreamCheckLastTagWas(
@ -463,7 +462,8 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state, int32_t
- (void)readMessage:(GPBMessage *)message
extensionRegistry:(id<GPBExtensionRegistry>)extensionRegistry {
CheckRecursionLimit(&state_);
int32_t length = ReadRawVarint32(&state_);
uint64_t length = GPBCodedInputStreamReadUInt64(&state_);
CheckFieldSize(length);
size_t oldLimit = GPBCodedInputStreamPushLimit(&state_, length);
++state_.recursionDepth;
[message mergeFromCodedInputStream:self extensionRegistry:extensionRegistry];
@ -477,7 +477,8 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state, int32_t
field:(GPBFieldDescriptor *)field
parentMessage:(GPBMessage *)parentMessage {
CheckRecursionLimit(&state_);
int32_t length = ReadRawVarint32(&state_);
uint64_t length = GPBCodedInputStreamReadUInt64(&state_);
CheckFieldSize(length);
size_t oldLimit = GPBCodedInputStreamPushLimit(&state_, length);
++state_.recursionDepth;
GPBDictionaryReadEntry(mapDictionary, self, extensionRegistry, field, parentMessage);

Loading…
Cancel
Save