Skip to content

Commit

Permalink
Validate the tag numbers when parsing. (protocolbuffers#1725)
Browse files Browse the repository at this point in the history
There was a twist code path (that some times showed up due to what happened to
be in memory in failure cases), that would cast a bogus wire type into the
enum, and then fall through switch statements.

Resolve this by validating all wire types when parsing tags and throwing the
error at that point so it can't enter the system.

As added safety, stick in a few asserts for apis that get passed tags to ensure
they also are only seeing valid data.

Bonus: Tweak the parsing loop to skip some work when we get the end marker
(zero tag) instead of still looping through all the fields.
  • Loading branch information
thomasvl authored Jun 29, 2016
1 parent e0016c5 commit c18aa77
Show file tree
Hide file tree
Showing 5 changed files with 21 additions and 4 deletions.
11 changes: 8 additions & 3 deletions objectivec/GPBCodedInputStream.m
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,13 @@ int32_t GPBCodedInputStreamReadTag(GPBCodedInputStreamState *state) {
state->lastTag = ReadRawVarint32(state);
if (state->lastTag == 0) {
// If we actually read zero, that's not a valid tag.
RaiseException(GPBCodedInputStreamErrorInvalidTag, @"Last tag can't be 0");
RaiseException(GPBCodedInputStreamErrorInvalidTag,
@"A zero tag on the wire is invalid.");
}
// Tags have to include a valid wireformat, check that also.
if (!GPBWireFormatIsValidTag(state->lastTag)) {
RaiseException(GPBCodedInputStreamErrorInvalidTag,
@"Invalid wireformat in tag.");
}
return state->lastTag;
}
Expand Down Expand Up @@ -352,6 +358,7 @@ - (void)checkLastTagWas:(int32_t)value {
}

- (BOOL)skipField:(int32_t)tag {
NSAssert(GPBWireFormatIsValidTag(tag), @"Invalid tag");
switch (GPBWireFormatGetTagWireType(tag)) {
case GPBWireFormatVarint:
GPBCodedInputStreamReadInt32(&state_);
Expand All @@ -374,8 +381,6 @@ - (BOOL)skipField:(int32_t)tag {
SkipRawData(&state_, sizeof(int32_t));
return YES;
}
RaiseException(GPBCodedInputStreamErrorInvalidTag, nil);
return NO;
}

- (void)skipMessage {
Expand Down
5 changes: 4 additions & 1 deletion objectivec/GPBMessage.m
Original file line number Diff line number Diff line change
Expand Up @@ -2279,6 +2279,9 @@ - (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input
while (YES) {
BOOL merged = NO;
tag = GPBCodedInputStreamReadTag(state);
if (tag == 0) {
break; // Reached end.
}
for (NSUInteger i = 0; i < numFields; ++i) {
if (startingIndex >= numFields) startingIndex = 0;
GPBFieldDescriptor *fieldDescriptor = fields[startingIndex];
Expand Down Expand Up @@ -2317,7 +2320,7 @@ - (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input
}
} // for(i < numFields)

if (!merged) {
if (!merged && (tag != 0)) {
// Primitive, repeated types can be packed on unpacked on the wire, and
// are parsed either way. The above loop covered tag in the preferred
// for, so this need to check the alternate form.
Expand Down
1 change: 1 addition & 0 deletions objectivec/GPBUnknownFieldSet.m
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ - (void)mergeVarintField:(int32_t)number value:(int32_t)value {
}

- (BOOL)mergeFieldFrom:(int32_t)tag input:(GPBCodedInputStream *)input {
NSAssert(GPBWireFormatIsValidTag(tag), @"Got passed an invalid tag");
int32_t number = GPBWireFormatGetTagFieldNumber(tag);
GPBCodedInputStreamState *state = &input->state_;
switch (GPBWireFormatGetTagWireType(tag)) {
Expand Down
1 change: 1 addition & 0 deletions objectivec/GPBWireFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ uint32_t GPBWireFormatMakeTag(uint32_t fieldNumber, GPBWireFormat wireType)
__attribute__((const));
GPBWireFormat GPBWireFormatGetTagWireType(uint32_t tag) __attribute__((const));
uint32_t GPBWireFormatGetTagFieldNumber(uint32_t tag) __attribute__((const));
BOOL GPBWireFormatIsValidTag(uint32_t tag) __attribute__((const));

GPBWireFormat GPBWireFormatForType(GPBDataType dataType, BOOL isPacked)
__attribute__((const));
Expand Down
7 changes: 7 additions & 0 deletions objectivec/GPBWireFormat.m
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ uint32_t GPBWireFormatGetTagFieldNumber(uint32_t tag) {
return GPBLogicalRightShift32(tag, GPBWireFormatTagTypeBits);
}

BOOL GPBWireFormatIsValidTag(uint32_t tag) {
uint32_t formatBits = (tag & GPBWireFormatTagTypeMask);
// The valid GPBWireFormat* values are 0-5, anything else is not a valid tag.
BOOL result = (formatBits <= 5);
return result;
}

GPBWireFormat GPBWireFormatForType(GPBDataType type, BOOL isPacked) {
if (isPacked) {
return GPBWireFormatLengthDelimited;
Expand Down

0 comments on commit c18aa77

Please sign in to comment.