Skip to content

Commit

Permalink
Fix proto deserialization issue when parsing a packed repeated enum f…
Browse files Browse the repository at this point in the history
…ield whose

possible values form a contiguous range that starts with 0 or 1, and end no
greater than 126, when parsing a noncontiguous stream (such as from a Cord).

This routine was misusing `ParseContext::Done`. It should only be used at tag boundaries and not within field parsing.

PiperOrigin-RevId: 501107542
  • Loading branch information
protobuf-github-bot authored and copybara-github committed Jan 10, 2023
1 parent 9dee1ca commit afdf6da
Show file tree
Hide file tree
Showing 2 changed files with 10 additions and 37 deletions.
45 changes: 8 additions & 37 deletions src/google/protobuf/generated_message_tctable_lite.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1316,52 +1316,23 @@ const char* TcParser::PackedEnumSmallRange(PROTOBUF_TC_PARAM_DECL) {
PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
}
}

// Since ctx->ReadPackedVarint does not use TailCall or Return, sync any
// pending hasbits now:
SyncHasbits(msg, hasbits, table);

const auto saved_tag = UnalignedLoad<TagType>(ptr);
ptr += sizeof(TagType);
auto* field = &RefAt<RepeatedField<int32_t>>(msg, data.offset());
const uint8_t max = data.aux_idx();

int size = static_cast<int>(ReadSize(&ptr));
if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) {
return Error(PROTOBUF_TC_PARAM_PASS);
}

while (size > 0) {
uint8_t v = *ptr;
return ctx->ReadPackedVarint(ptr, [=](int32_t v) {
if (PROTOBUF_PREDICT_FALSE(min > v || v > max)) {
// If it is out of range it might be because it is a non-canonical varint
// with a small value (ie the continuation bit is on).
// Do the full varint parse just in case.
uint64_t tmp;
const char* const old_ptr = ptr;
ptr = ParseVarint(ptr, &tmp);
if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) {
return Error(PROTOBUF_TC_PARAM_PASS);
}
size -= static_cast<int>(ptr - old_ptr);
if (PROTOBUF_PREDICT_FALSE(size < 0)) {
return Error(PROTOBUF_TC_PARAM_PASS);
}
int32_t v32 = static_cast<int32_t>(tmp);
if (PROTOBUF_PREDICT_FALSE(min > v32 || v32 > max)) {
UnknownPackedEnum(msg, table, FastDecodeTag(saved_tag), v32);
} else {
field->Add(v32);
}
UnknownPackedEnum(msg, table, FastDecodeTag(saved_tag), v);
} else {
ptr += 1;
size -= 1;
field->Add(v);
}

if (PROTOBUF_PREDICT_FALSE(!ctx->DataAvailable(ptr))) {
if (ctx->Done(&ptr) && size != 0) {
return Error(PROTOBUF_TC_PARAM_PASS);
}
}
}

PROTOBUF_MUSTTAIL return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
});
}

PROTOBUF_NOINLINE const char* TcParser::FastEr0P1(PROTOBUF_TC_PARAM_DECL) {
Expand Down
2 changes: 2 additions & 0 deletions src/google/protobuf/parse_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,8 @@ class PROTOBUF_EXPORT ParseContext : public EpsCopyInputStream {

void TrackCorrectEnding() { group_depth_ = 0; }

// Done should only be called when the parsing pointer is pointing to the
// beginning of field data - that is, at a tag. Or if it is NULL.
bool Done(const char** ptr) { return DoneWithCheck(ptr, group_depth_); }

int depth() const { return depth_; }
Expand Down

0 comments on commit afdf6da

Please sign in to comment.