aboutsummaryrefslogtreecommitdiffhomepage
path: root/objectivec/GPBCodedInputStream.m
diff options
context:
space:
mode:
Diffstat (limited to 'objectivec/GPBCodedInputStream.m')
-rw-r--r--objectivec/GPBCodedInputStream.m161
1 files changed, 88 insertions, 73 deletions
diff --git a/objectivec/GPBCodedInputStream.m b/objectivec/GPBCodedInputStream.m
index fd877838..dd05ddb4 100644
--- a/objectivec/GPBCodedInputStream.m
+++ b/objectivec/GPBCodedInputStream.m
@@ -36,17 +36,53 @@
#import "GPBUtilities_PackagePrivate.h"
#import "GPBWireFormat.h"
-static const NSUInteger kDefaultRecursionLimit = 64;
+NSString *const GPBCodedInputStreamException =
+ GPBNSStringifySymbol(GPBCodedInputStreamException);
+
+NSString *const GPBCodedInputStreamUnderlyingErrorKey =
+ GPBNSStringifySymbol(GPBCodedInputStreamUnderlyingErrorKey);
+
+NSString *const GPBCodedInputStreamErrorDomain =
+ GPBNSStringifySymbol(GPBCodedInputStreamErrorDomain);
+
+// Matching:
+// https://github.com/google/protobuf/blob/master/java/core/src/main/java/com/google/protobuf/CodedInputStream.java#L62
+// private static final int DEFAULT_RECURSION_LIMIT = 100;
+// https://github.com/google/protobuf/blob/master/src/google/protobuf/io/coded_stream.cc#L86
+// int CodedInputStream::default_recursion_limit_ = 100;
+static const NSUInteger kDefaultRecursionLimit = 100;
+
+static void RaiseException(NSInteger code, NSString *reason) {
+ NSDictionary *errorInfo = nil;
+ if ([reason length]) {
+ errorInfo = @{ GPBErrorReasonKey: reason };
+ }
+ NSError *error = [NSError errorWithDomain:GPBCodedInputStreamErrorDomain
+ code:code
+ userInfo:errorInfo];
+
+ NSDictionary *exceptionInfo =
+ @{ GPBCodedInputStreamUnderlyingErrorKey: error };
+ [[NSException exceptionWithName:GPBCodedInputStreamException
+ reason:reason
+ userInfo:exceptionInfo] raise];
+}
+
+static void CheckRecursionLimit(GPBCodedInputStreamState *state) {
+ if (state->recursionDepth >= kDefaultRecursionLimit) {
+ RaiseException(GPBCodedInputStreamErrorRecursionDepthExceeded, nil);
+ }
+}
static void CheckSize(GPBCodedInputStreamState *state, size_t size) {
size_t newSize = state->bufferPos + size;
if (newSize > state->bufferSize) {
- [NSException raise:NSParseErrorException format:@""];
+ RaiseException(GPBCodedInputStreamErrorInvalidSize, nil);
}
if (newSize > state->currentLimit) {
// Fast forward to end of currentLimit;
state->bufferPos = state->currentLimit;
- [NSException raise:NSParseErrorException format:@""];
+ RaiseException(GPBCodedInputStreamErrorSubsectionLimitReached, nil);
}
}
@@ -69,56 +105,25 @@ static int64_t ReadRawLittleEndian64(GPBCodedInputStreamState *state) {
return value;
}
-static int32_t ReadRawVarint32(GPBCodedInputStreamState *state) {
- int8_t tmp = ReadRawByte(state);
- if (tmp >= 0) {
- return tmp;
- }
- int32_t result = tmp & 0x7f;
- if ((tmp = ReadRawByte(state)) >= 0) {
- result |= tmp << 7;
- } else {
- result |= (tmp & 0x7f) << 7;
- if ((tmp = ReadRawByte(state)) >= 0) {
- result |= tmp << 14;
- } else {
- result |= (tmp & 0x7f) << 14;
- if ((tmp = ReadRawByte(state)) >= 0) {
- result |= tmp << 21;
- } else {
- result |= (tmp & 0x7f) << 21;
- result |= (tmp = ReadRawByte(state)) << 28;
- if (tmp < 0) {
- // Discard upper 32 bits.
- for (int i = 0; i < 5; i++) {
- if (ReadRawByte(state) >= 0) {
- return result;
- }
- }
- [NSException raise:NSParseErrorException
- format:@"Unable to read varint32"];
- }
- }
- }
- }
- return result;
-}
-
static int64_t ReadRawVarint64(GPBCodedInputStreamState *state) {
int32_t shift = 0;
int64_t result = 0;
while (shift < 64) {
int8_t b = ReadRawByte(state);
- result |= (int64_t)(b & 0x7F) << shift;
+ result |= (int64_t)((uint64_t)(b & 0x7F) << shift);
if ((b & 0x80) == 0) {
return result;
}
shift += 7;
}
- [NSException raise:NSParseErrorException format:@"Unable to read varint64"];
+ RaiseException(GPBCodedInputStreamErrorInvalidVarInt, @"Invalid VarInt64");
return 0;
}
+static int32_t ReadRawVarint32(GPBCodedInputStreamState *state) {
+ return (int32_t)ReadRawVarint64(state);
+}
+
static void SkipRawData(GPBCodedInputStreamState *state, size_t size) {
CheckSize(state, size);
state->bufferPos += size;
@@ -200,10 +205,15 @@ int32_t GPBCodedInputStreamReadTag(GPBCodedInputStreamState *state) {
}
state->lastTag = ReadRawVarint32(state);
- if (state->lastTag == 0) {
- // If we actually read zero, that's not a valid tag.
- [NSException raise:NSParseErrorException
- format:@"Invalid last tag %d", state->lastTag];
+ // Tags have to include a valid wireformat.
+ if (!GPBWireFormatIsValidTag(state->lastTag)) {
+ RaiseException(GPBCodedInputStreamErrorInvalidTag,
+ @"Invalid wireformat in tag.");
+ }
+ // Zero is not a valid field number.
+ if (GPBWireFormatGetTagFieldNumber(state->lastTag) == 0) {
+ RaiseException(GPBCodedInputStreamErrorInvalidTag,
+ @"A zero field number on the wire is invalid.");
}
return state->lastTag;
}
@@ -219,15 +229,15 @@ NSString *GPBCodedInputStreamReadRetainedString(
result = [[NSString alloc] initWithBytes:&state->bytes[state->bufferPos]
length:size
encoding:NSUTF8StringEncoding];
+ state->bufferPos += size;
if (!result) {
- result = @"";
#ifdef DEBUG
// https://developers.google.com/protocol-buffers/docs/proto#scalar
- NSLog(@"UTF8 failure, is some field type 'string' when it should be "
+ NSLog(@"UTF-8 failure, is some field type 'string' when it should be "
@"'bytes'?");
#endif
+ RaiseException(GPBCodedInputStreamErrorInvalidUTF8, nil);
}
- state->bufferPos += size;
}
return result;
}
@@ -261,8 +271,7 @@ size_t GPBCodedInputStreamPushLimit(GPBCodedInputStreamState *state,
byteLimit += state->bufferPos;
size_t oldLimit = state->currentLimit;
if (byteLimit > oldLimit) {
- [NSException raise:NSInvalidArgumentException
- format:@"byteLimit > oldLimit: %tu > %tu", byteLimit, oldLimit];
+ RaiseException(GPBCodedInputStreamErrorInvalidSubsectionLimit, nil);
}
state->currentLimit = byteLimit;
return oldLimit;
@@ -285,8 +294,7 @@ BOOL GPBCodedInputStreamIsAtEnd(GPBCodedInputStreamState *state) {
void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state,
int32_t value) {
if (state->lastTag != value) {
- [NSException raise:NSParseErrorException
- format:@"Last tag: %d should be %d", state->lastTag, value];
+ RaiseException(GPBCodedInputStreamErrorInvalidTag, @"Unexpected tag read");
}
}
@@ -315,6 +323,12 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state,
[super dealloc];
}
+// Direct access is use for speed, to avoid even internally declaring things
+// read/write, etc. The warning is enabled in the project to ensure code calling
+// protos can turn on -Wdirect-ivar-access without issues.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdirect-ivar-access"
+
- (int32_t)readTag {
return GPBCodedInputStreamReadTag(&state_);
}
@@ -324,6 +338,7 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state,
}
- (BOOL)skipField:(int32_t)tag {
+ NSAssert(GPBWireFormatIsValidTag(tag), @"Invalid tag");
switch (GPBWireFormatGetTagWireType(tag)) {
case GPBWireFormatVarint:
GPBCodedInputStreamReadInt32(&state_);
@@ -346,8 +361,6 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state,
SkipRawData(&state_, sizeof(int32_t));
return YES;
}
- [NSException raise:NSParseErrorException format:@"Invalid tag %d", tag];
- return NO;
}
- (void)skipMessage {
@@ -359,6 +372,22 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state,
}
}
+- (BOOL)isAtEnd {
+ return GPBCodedInputStreamIsAtEnd(&state_);
+}
+
+- (size_t)position {
+ return state_.bufferPos;
+}
+
+- (size_t)pushLimit:(size_t)byteLimit {
+ return GPBCodedInputStreamPushLimit(&state_, byteLimit);
+}
+
+- (void)popLimit:(size_t)oldLimit {
+ GPBCodedInputStreamPopLimit(&state_, oldLimit);
+}
+
- (double)readDouble {
return GPBCodedInputStreamReadDouble(&state_);
}
@@ -398,11 +427,7 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state,
- (void)readGroup:(int32_t)fieldNumber
message:(GPBMessage *)message
extensionRegistry:(GPBExtensionRegistry *)extensionRegistry {
- if (state_.recursionDepth >= kDefaultRecursionLimit) {
- [NSException raise:NSParseErrorException
- format:@"recursionDepth(%tu) >= %tu", state_.recursionDepth,
- kDefaultRecursionLimit];
- }
+ CheckRecursionLimit(&state_);
++state_.recursionDepth;
[message mergeFromCodedInputStream:self extensionRegistry:extensionRegistry];
GPBCodedInputStreamCheckLastTagWas(
@@ -412,11 +437,7 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state,
- (void)readUnknownGroup:(int32_t)fieldNumber
message:(GPBUnknownFieldSet *)message {
- if (state_.recursionDepth >= kDefaultRecursionLimit) {
- [NSException raise:NSParseErrorException
- format:@"recursionDepth(%tu) >= %tu", state_.recursionDepth,
- kDefaultRecursionLimit];
- }
+ CheckRecursionLimit(&state_);
++state_.recursionDepth;
[message mergeFromCodedInputStream:self];
GPBCodedInputStreamCheckLastTagWas(
@@ -426,12 +447,8 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state,
- (void)readMessage:(GPBMessage *)message
extensionRegistry:(GPBExtensionRegistry *)extensionRegistry {
+ CheckRecursionLimit(&state_);
int32_t length = ReadRawVarint32(&state_);
- if (state_.recursionDepth >= kDefaultRecursionLimit) {
- [NSException raise:NSParseErrorException
- format:@"recursionDepth(%tu) >= %tu", state_.recursionDepth,
- kDefaultRecursionLimit];
- }
size_t oldLimit = GPBCodedInputStreamPushLimit(&state_, length);
++state_.recursionDepth;
[message mergeFromCodedInputStream:self extensionRegistry:extensionRegistry];
@@ -444,12 +461,8 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state,
extensionRegistry:(GPBExtensionRegistry *)extensionRegistry
field:(GPBFieldDescriptor *)field
parentMessage:(GPBMessage *)parentMessage {
+ CheckRecursionLimit(&state_);
int32_t length = ReadRawVarint32(&state_);
- if (state_.recursionDepth >= kDefaultRecursionLimit) {
- [NSException raise:NSParseErrorException
- format:@"recursionDepth(%tu) >= %tu", state_.recursionDepth,
- kDefaultRecursionLimit];
- }
size_t oldLimit = GPBCodedInputStreamPushLimit(&state_, length);
++state_.recursionDepth;
GPBDictionaryReadEntry(mapDictionary, self, extensionRegistry, field,
@@ -487,4 +500,6 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state,
return GPBCodedInputStreamReadSInt64(&state_);
}
+#pragma clang diagnostic pop
+
@end