diff --git a/objectivec/GPBCodedInputStream.m b/objectivec/GPBCodedInputStream.m index 9acf6f170f..c8464a2920 100644 --- a/objectivec/GPBCodedInputStream.m +++ b/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)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);