Protocol Buffers - Google's data interchange format (grpc依赖)
https://developers.google.com/protocol-buffers/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
811 lines
25 KiB
811 lines
25 KiB
/* |
|
* upb - a minimalist implementation of protocol buffers. |
|
* |
|
* Copyright (c) 2011 Google Inc. See LICENSE for details. |
|
* |
|
* An exhaustive set of tests for parsing both valid and invalid protobuf |
|
* input, with buffer breaks in arbitrary places. |
|
* |
|
* Tests to add: |
|
* - unknown field handler called appropriately |
|
* - unknown fields can be inserted in random places |
|
* - fuzzing of valid input |
|
* - resource limits (max stack depth, max string len) |
|
* - testing of groups |
|
* - more throrough testing of sequences |
|
* - test skipping of submessages |
|
* - test suspending the decoder |
|
* - buffers that are close enough to the end of the address space that |
|
* pointers overflow (this might be difficult). |
|
* - a few "kitchen sink" examples (one proto that uses all types, lots |
|
* of submsg/sequences, etc. |
|
*/ |
|
|
|
#ifndef __STDC_FORMAT_MACROS |
|
#define __STDC_FORMAT_MACROS // For PRIuS, etc. |
|
#endif |
|
|
|
#include <inttypes.h> |
|
#include <stdarg.h> |
|
#include <stdint.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include "upb/handlers.h" |
|
#include "upb/pb/decoder.h" |
|
#include "upb/pb/varint.h" |
|
#include "upb/upb.h" |
|
#include "upb_test.h" |
|
|
|
// Copied from decoder.c, since this is not a public interface. |
|
typedef struct { |
|
uint8_t native_wire_type; |
|
bool is_numeric; |
|
} upb_decoder_typeinfo; |
|
|
|
static const upb_decoder_typeinfo upb_decoder_types[] = { |
|
{UPB_WIRE_TYPE_END_GROUP, false}, // ENDGROUP |
|
{UPB_WIRE_TYPE_64BIT, true}, // DOUBLE |
|
{UPB_WIRE_TYPE_32BIT, true}, // FLOAT |
|
{UPB_WIRE_TYPE_VARINT, true}, // INT64 |
|
{UPB_WIRE_TYPE_VARINT, true}, // UINT64 |
|
{UPB_WIRE_TYPE_VARINT, true}, // INT32 |
|
{UPB_WIRE_TYPE_64BIT, true}, // FIXED64 |
|
{UPB_WIRE_TYPE_32BIT, true}, // FIXED32 |
|
{UPB_WIRE_TYPE_VARINT, true}, // BOOL |
|
{UPB_WIRE_TYPE_DELIMITED, false}, // STRING |
|
{UPB_WIRE_TYPE_START_GROUP, false}, // GROUP |
|
{UPB_WIRE_TYPE_DELIMITED, false}, // MESSAGE |
|
{UPB_WIRE_TYPE_DELIMITED, false}, // BYTES |
|
{UPB_WIRE_TYPE_VARINT, true}, // UINT32 |
|
{UPB_WIRE_TYPE_VARINT, true}, // ENUM |
|
{UPB_WIRE_TYPE_32BIT, true}, // SFIXED32 |
|
{UPB_WIRE_TYPE_64BIT, true}, // SFIXED64 |
|
{UPB_WIRE_TYPE_VARINT, true}, // SINT32 |
|
{UPB_WIRE_TYPE_VARINT, true}, // SINT64 |
|
}; |
|
|
|
|
|
class buffer { |
|
public: |
|
buffer(const void *data, size_t len) : len_(0) { append(data, len); } |
|
explicit buffer(const char *data) : len_(0) { append(data); } |
|
explicit buffer(size_t len) : len_(len) { memset(buf_, 0, len); } |
|
buffer(const buffer& buf) : len_(0) { append(buf); } |
|
buffer() : len_(0) {} |
|
|
|
void append(const void *data, size_t len) { |
|
ASSERT_NOCOUNT(len + len_ < sizeof(buf_)); |
|
memcpy(buf_ + len_, data, len); |
|
len_ += len; |
|
buf_[len_] = NULL; |
|
} |
|
|
|
void append(const buffer& buf) { |
|
append(buf.buf_, buf.len_); |
|
} |
|
|
|
void append(const char *str) { |
|
append(str, strlen(str)); |
|
} |
|
|
|
void vappendf(const char *fmt, va_list args) { |
|
size_t avail = sizeof(buf_) - len_; |
|
size_t size = vsnprintf(buf_ + len_, avail, fmt, args); |
|
ASSERT_NOCOUNT(avail > size); |
|
len_ += size; |
|
} |
|
|
|
void appendf(const char *fmt, ...) { |
|
va_list args; |
|
va_start(args, fmt); |
|
vappendf(fmt, args); |
|
va_end(args); |
|
} |
|
|
|
void assign(const buffer& buf) { |
|
clear(); |
|
append(buf); |
|
} |
|
|
|
bool eql(const buffer& other) const { |
|
return len_ == other.len_ && memcmp(buf_, other.buf_, len_) == 0; |
|
} |
|
|
|
void clear() { len_ = 0; } |
|
size_t len() const { return len_; } |
|
const char *buf() const { return buf_; } |
|
|
|
private: |
|
// Has to be big enough for the largest string used in the test. |
|
char buf_[32768]; |
|
size_t len_; |
|
}; |
|
|
|
|
|
/* Routines for building arbitrary protos *************************************/ |
|
|
|
const buffer empty; |
|
|
|
buffer cat(const buffer& a, const buffer& b, |
|
const buffer& c = empty, |
|
const buffer& d = empty, |
|
const buffer& e = empty) { |
|
buffer ret; |
|
ret.append(a); |
|
ret.append(b); |
|
ret.append(c); |
|
ret.append(d); |
|
ret.append(e); |
|
return ret; |
|
} |
|
|
|
buffer varint(uint64_t x) { |
|
char buf[UPB_PB_VARINT_MAX_LEN]; |
|
size_t len = upb_vencode64(x, buf); |
|
return buffer(buf, len); |
|
} |
|
|
|
// TODO: proper byte-swapping for big-endian machines. |
|
buffer fixed32(void *data) { return buffer(data, 4); } |
|
buffer fixed64(void *data) { return buffer(data, 8); } |
|
|
|
buffer delim(const buffer& buf) { return cat(varint(buf.len()), buf); } |
|
buffer uint32(uint32_t u32) { return fixed32(&u32); } |
|
buffer uint64(uint64_t u64) { return fixed64(&u64); } |
|
buffer flt(float f) { return fixed32(&f); } |
|
buffer dbl(double d) { return fixed64(&d); } |
|
buffer zz32(int32_t x) { return varint(upb_zzenc_32(x)); } |
|
buffer zz64(int64_t x) { return varint(upb_zzenc_64(x)); } |
|
|
|
buffer tag(uint32_t fieldnum, char wire_type) { |
|
return varint((fieldnum << 3) | wire_type); |
|
} |
|
|
|
buffer submsg(uint32_t fn, const buffer& buf) { |
|
return cat( tag(fn, UPB_WIRE_TYPE_DELIMITED), delim(buf) ); |
|
} |
|
|
|
|
|
/* A set of handlers that covers all .proto types *****************************/ |
|
|
|
// The handlers simply append to a string indicating what handlers were called. |
|
// This string is similar to protobuf text format but fields are referred to by |
|
// number instead of name and sequences are explicitly delimited. We indent |
|
// using the closure depth to test that the stack of closures is properly |
|
// handled. |
|
|
|
int closures[UPB_MAX_NESTING]; |
|
buffer output; |
|
|
|
void indentbuf(buffer *buf, int depth) { |
|
for (int i = 0; i < depth; i++) |
|
buf->append(" ", 2); |
|
} |
|
|
|
void indent(void *depth) { |
|
indentbuf(&output, *(int*)depth); |
|
} |
|
|
|
#define VALUE_HANDLER(member, fmt) \ |
|
upb_flow_t value_ ## member(void *closure, upb_value fval, upb_value val) { \ |
|
indent(closure); \ |
|
output.appendf("%" PRIu32 ":%" fmt "\n", \ |
|
upb_value_getuint32(fval), upb_value_get ## member(val)); \ |
|
return UPB_CONTINUE; \ |
|
} |
|
|
|
VALUE_HANDLER(uint32, PRIu32) |
|
VALUE_HANDLER(uint64, PRIu64) |
|
VALUE_HANDLER(int32, PRId32) |
|
VALUE_HANDLER(int64, PRId64) |
|
VALUE_HANDLER(float, "g") |
|
VALUE_HANDLER(double, "g") |
|
|
|
upb_flow_t value_bool(void *closure, upb_value fval, upb_value val) { |
|
indent(closure); |
|
output.appendf("%" PRIu32 ":%s\n", |
|
upb_value_getuint32(fval), |
|
upb_value_getbool(val) ? "true" : "false"); |
|
return UPB_CONTINUE; |
|
} |
|
|
|
upb_flow_t value_string(void *closure, upb_value fval, upb_value val) { |
|
// Note: won't work with strings that contain NULL. |
|
indent(closure); |
|
char *str = upb_byteregion_strdup(upb_value_getbyteregion(val)); |
|
output.appendf("%" PRIu32 ":%s\n", upb_value_getuint32(fval), str); |
|
free(str); |
|
return UPB_CONTINUE; |
|
} |
|
|
|
upb_sflow_t startsubmsg(void *closure, upb_value fval) { |
|
indent(closure); |
|
output.appendf("%" PRIu32 ":{\n", upb_value_getuint32(fval)); |
|
return UPB_CONTINUE_WITH(((int*)closure) + 1); |
|
} |
|
|
|
upb_flow_t endsubmsg(void *closure, upb_value fval) { |
|
indent(closure); |
|
output.append("}\n"); |
|
return UPB_CONTINUE; |
|
} |
|
|
|
upb_sflow_t startseq(void *closure, upb_value fval) { |
|
indent(closure); |
|
output.appendf("%" PRIu32 ":[\n", upb_value_getuint32(fval)); |
|
return UPB_CONTINUE_WITH(((int*)closure) + 1); |
|
} |
|
|
|
upb_flow_t endseq(void *closure, upb_value fval) { |
|
indent(closure); |
|
output.append("]\n"); |
|
return UPB_CONTINUE; |
|
} |
|
|
|
upb_flow_t startmsg(void *closure) { |
|
indent(closure); |
|
output.append("<\n"); |
|
return UPB_CONTINUE; |
|
} |
|
|
|
void endmsg(void *closure, upb_status *status) { |
|
(void)status; |
|
indent(closure); |
|
output.append(">\n"); |
|
} |
|
|
|
void doreg(upb_mhandlers *m, uint32_t num, upb_fieldtype_t type, bool repeated, |
|
upb_value_handler *handler) { |
|
upb_fhandlers *f = upb_mhandlers_newfhandlers(m, num, type, repeated); |
|
ASSERT(f); |
|
upb_fhandlers_setvalue(f, handler); |
|
upb_fhandlers_setstartseq(f, &startseq); |
|
upb_fhandlers_setendseq(f, &endseq); |
|
upb_fhandlers_setfval(f, upb_value_uint32(num)); |
|
} |
|
|
|
// The repeated field number to correspond to the given non-repeated field |
|
// number. |
|
uint32_t rep_fn(uint32_t fn) { |
|
return (UPB_MAX_FIELDNUMBER - 1000) + fn; |
|
} |
|
|
|
#define NOP_FIELD 40 |
|
#define UNKNOWN_FIELD 666 |
|
|
|
void reg(upb_mhandlers *m, upb_fieldtype_t type, upb_value_handler *handler) { |
|
// We register both a repeated and a non-repeated field for every type. |
|
// For the non-repeated field we make the field number the same as the |
|
// type. For the repeated field we make it a function of the type. |
|
doreg(m, type, type, false, handler); |
|
doreg(m, rep_fn(type), type, true, handler); |
|
} |
|
|
|
void reg_subm(upb_mhandlers *m, uint32_t num, upb_fieldtype_t type, |
|
bool repeated) { |
|
upb_fhandlers *f = |
|
upb_mhandlers_newfhandlers_subm(m, num, type, repeated, m); |
|
ASSERT(f); |
|
upb_fhandlers_setstartseq(f, &startseq); |
|
upb_fhandlers_setendseq(f, &endseq); |
|
upb_fhandlers_setstartsubmsg(f, &startsubmsg); |
|
upb_fhandlers_setendsubmsg(f, &endsubmsg); |
|
upb_fhandlers_setfval(f, upb_value_uint32(num)); |
|
} |
|
|
|
void reghandlers(upb_mhandlers *m) { |
|
upb_mhandlers_setstartmsg(m, &startmsg); |
|
upb_mhandlers_setendmsg(m, &endmsg); |
|
|
|
// Register handlers for each type. |
|
reg(m, UPB_TYPE(DOUBLE), &value_double); |
|
reg(m, UPB_TYPE(FLOAT), &value_float); |
|
reg(m, UPB_TYPE(INT64), &value_int64); |
|
reg(m, UPB_TYPE(UINT64), &value_uint64); |
|
reg(m, UPB_TYPE(INT32) , &value_int32); |
|
reg(m, UPB_TYPE(FIXED64), &value_uint64); |
|
reg(m, UPB_TYPE(FIXED32), &value_uint32); |
|
reg(m, UPB_TYPE(BOOL), &value_bool); |
|
reg(m, UPB_TYPE(STRING), &value_string); |
|
reg(m, UPB_TYPE(BYTES), &value_string); |
|
reg(m, UPB_TYPE(UINT32), &value_uint32); |
|
reg(m, UPB_TYPE(ENUM), &value_int32); |
|
reg(m, UPB_TYPE(SFIXED32), &value_int32); |
|
reg(m, UPB_TYPE(SFIXED64), &value_int64); |
|
reg(m, UPB_TYPE(SINT32), &value_int32); |
|
reg(m, UPB_TYPE(SINT64), &value_int64); |
|
|
|
// Register submessage/group handlers that are self-recursive |
|
// to this type, eg: message M { optional M m = 1; } |
|
reg_subm(m, UPB_TYPE(MESSAGE), UPB_TYPE(MESSAGE), false); |
|
reg_subm(m, UPB_TYPE(GROUP), UPB_TYPE(GROUP), false); |
|
reg_subm(m, rep_fn(UPB_TYPE(MESSAGE)), UPB_TYPE(MESSAGE), true); |
|
reg_subm(m, rep_fn(UPB_TYPE(GROUP)), UPB_TYPE(GROUP), true); |
|
|
|
// Register a no-op string field so we can pad the proto wherever we want. |
|
upb_mhandlers_newfhandlers(m, NOP_FIELD, UPB_TYPE(STRING), false); |
|
} |
|
|
|
|
|
/* Custom bytesrc that can insert buffer seams in arbitrary places ************/ |
|
|
|
typedef struct { |
|
upb_bytesrc bytesrc; |
|
const char *str; |
|
size_t len, seam1, seam2; |
|
upb_byteregion byteregion; |
|
} upb_seamsrc; |
|
|
|
size_t upb_seamsrc_avail(const upb_seamsrc *src, size_t ofs) { |
|
if (ofs < src->seam1) return src->seam1 - ofs; |
|
if (ofs < src->seam2) return src->seam2 - ofs; |
|
return src->len - ofs; |
|
} |
|
|
|
upb_bytesuccess_t upb_seamsrc_fetch(void *_src, uint64_t ofs, size_t *read) { |
|
upb_seamsrc *src = (upb_seamsrc*)_src; |
|
assert(ofs < src->len); |
|
if (ofs == src->len) { |
|
upb_status_seteof(&src->bytesrc.status); |
|
return UPB_BYTE_EOF; |
|
} |
|
*read = upb_seamsrc_avail(src, ofs); |
|
return UPB_BYTE_OK; |
|
} |
|
|
|
void upb_seamsrc_copy(const void *_src, uint64_t ofs, |
|
size_t len, char *dst) { |
|
const upb_seamsrc *src = (const upb_seamsrc*)_src; |
|
assert(ofs + len <= src->len); |
|
memcpy(dst, src->str + ofs, len); |
|
} |
|
|
|
void upb_seamsrc_discard(void *src, uint64_t ofs) { |
|
(void)src; |
|
(void)ofs; |
|
} |
|
|
|
const char *upb_seamsrc_getptr(const void *_s, uint64_t ofs, size_t *len) { |
|
const upb_seamsrc *src = (const upb_seamsrc*)_s; |
|
*len = upb_seamsrc_avail(src, ofs); |
|
return src->str + ofs; |
|
} |
|
|
|
void upb_seamsrc_init(upb_seamsrc *s, const char *str, size_t len) { |
|
static upb_bytesrc_vtbl vtbl = { |
|
&upb_seamsrc_fetch, |
|
&upb_seamsrc_discard, |
|
&upb_seamsrc_copy, |
|
&upb_seamsrc_getptr, |
|
}; |
|
upb_bytesrc_init(&s->bytesrc, &vtbl); |
|
s->seam1 = 0; |
|
s->seam2 = 0; |
|
s->str = str; |
|
s->len = len; |
|
s->byteregion.bytesrc = &s->bytesrc; |
|
s->byteregion.toplevel = true; |
|
s->byteregion.start = 0; |
|
s->byteregion.end = len; |
|
} |
|
|
|
void upb_seamsrc_resetseams(upb_seamsrc *s, size_t seam1, size_t seam2) { |
|
assert(seam1 <= seam2); |
|
s->seam1 = seam1; |
|
s->seam2 = seam2; |
|
s->byteregion.discard = 0; |
|
s->byteregion.fetch = 0; |
|
} |
|
|
|
void upb_seamsrc_uninit(upb_seamsrc *s) { (void)s; } |
|
|
|
upb_bytesrc *upb_seamsrc_bytesrc(upb_seamsrc *s) { |
|
return &s->bytesrc; |
|
} |
|
|
|
// Returns the top-level upb_byteregion* for this seamsrc. Invalidated when |
|
// the seamsrc is reset. |
|
upb_byteregion *upb_seamsrc_allbytes(upb_seamsrc *s) { |
|
return &s->byteregion; |
|
} |
|
|
|
|
|
/* Running of test cases ******************************************************/ |
|
|
|
upb_decoderplan *plan; |
|
#define LINE(x) x "\n" |
|
void run_decoder(const buffer& proto, const buffer* expected_output) { |
|
upb_seamsrc src; |
|
upb_seamsrc_init(&src, proto.buf(), proto.len()); |
|
upb_decoder d; |
|
upb_decoder_init(&d); |
|
upb_decoder_resetplan(&d, plan, 0); |
|
for (size_t i = 0; i < proto.len(); i++) { |
|
for (size_t j = i; j < UPB_MIN(proto.len(), i + 5); j++) { |
|
upb_seamsrc_resetseams(&src, i, j); |
|
upb_byteregion *input = upb_seamsrc_allbytes(&src); |
|
output.clear(); |
|
upb_decoder_resetinput(&d, input, &closures[0]); |
|
upb_success_t success = UPB_SUSPENDED; |
|
while (success == UPB_SUSPENDED) |
|
success = upb_decoder_decode(&d); |
|
ASSERT(upb_ok(upb_decoder_status(&d)) == (success == UPB_OK)); |
|
if (expected_output) { |
|
ASSERT_STATUS(success == UPB_OK, upb_decoder_status(&d)); |
|
// The input should be fully consumed. |
|
ASSERT(upb_byteregion_fetchofs(input) == upb_byteregion_endofs(input)); |
|
ASSERT(upb_byteregion_discardofs(input) == |
|
upb_byteregion_endofs(input)); |
|
if (!output.eql(*expected_output)) { |
|
fprintf(stderr, "Text mismatch: '%s' vs '%s'\n", |
|
output.buf(), expected_output->buf()); |
|
} |
|
ASSERT(output.eql(*expected_output)); |
|
} else { |
|
ASSERT(success == UPB_ERROR); |
|
} |
|
} |
|
} |
|
upb_decoder_uninit(&d); |
|
upb_seamsrc_uninit(&src); |
|
} |
|
|
|
const static buffer thirty_byte_nop = buffer(cat( |
|
tag(NOP_FIELD, UPB_WIRE_TYPE_DELIMITED), delim(buffer(30)) )); |
|
|
|
void assert_successful_parse(const buffer& proto, |
|
const char *expected_fmt, ...) { |
|
buffer expected_text; |
|
va_list args; |
|
va_start(args, expected_fmt); |
|
expected_text.vappendf(expected_fmt, args); |
|
va_end(args); |
|
// The JIT is only used for data >=20 bytes from end-of-buffer, so |
|
// repeat once with no-op padding data at the end of buffer. |
|
run_decoder(proto, &expected_text); |
|
run_decoder(cat( proto, thirty_byte_nop ), &expected_text); |
|
} |
|
|
|
void assert_does_not_parse_at_eof(const buffer& proto) { |
|
run_decoder(proto, NULL); |
|
} |
|
|
|
void assert_does_not_parse(const buffer& proto) { |
|
// The JIT is only used for data >=20 bytes from end-of-buffer, so |
|
// repeat once with no-op padding data at the end of buffer. |
|
assert_does_not_parse_at_eof(proto); |
|
assert_does_not_parse_at_eof(cat( proto, thirty_byte_nop )); |
|
} |
|
|
|
|
|
/* The actual tests ***********************************************************/ |
|
|
|
void test_premature_eof_for_type(upb_fieldtype_t type) { |
|
// Incomplete values for each wire type. |
|
static const buffer incompletes[6] = { |
|
buffer("\x80"), // UPB_WIRE_TYPE_VARINT |
|
buffer("abcdefg"), // UPB_WIRE_TYPE_64BIT |
|
buffer("\x80"), // UPB_WIRE_TYPE_DELIMITED (partial length) |
|
buffer(), // UPB_WIRE_TYPE_START_GROUP (no value required) |
|
buffer(), // UPB_WIRE_TYPE_END_GROUP (no value required) |
|
buffer("abc") // UPB_WIRE_TYPE_32BIT |
|
}; |
|
|
|
uint32_t fieldnum = type; |
|
uint32_t rep_fieldnum = rep_fn(type); |
|
int wire_type = upb_decoder_types[type].native_wire_type; |
|
const buffer& incomplete = incompletes[wire_type]; |
|
|
|
// EOF before a known non-repeated value. |
|
assert_does_not_parse_at_eof(tag(fieldnum, wire_type)); |
|
|
|
// EOF before a known repeated value. |
|
assert_does_not_parse_at_eof(tag(rep_fieldnum, wire_type)); |
|
|
|
// EOF before an unknown value. |
|
assert_does_not_parse_at_eof(tag(UNKNOWN_FIELD, wire_type)); |
|
|
|
// EOF inside a known non-repeated value. |
|
assert_does_not_parse_at_eof( |
|
cat( tag(fieldnum, wire_type), incomplete )); |
|
|
|
// EOF inside a known repeated value. |
|
assert_does_not_parse_at_eof( |
|
cat( tag(rep_fieldnum, wire_type), incomplete )); |
|
|
|
// EOF inside an unknown value. |
|
assert_does_not_parse_at_eof( |
|
cat( tag(UNKNOWN_FIELD, wire_type), incomplete )); |
|
|
|
if (wire_type == UPB_WIRE_TYPE_DELIMITED) { |
|
// EOF in the middle of delimited data for known non-repeated value. |
|
assert_does_not_parse_at_eof( |
|
cat( tag(fieldnum, wire_type), varint(1) )); |
|
|
|
// EOF in the middle of delimited data for known repeated value. |
|
assert_does_not_parse_at_eof( |
|
cat( tag(rep_fieldnum, wire_type), varint(1) )); |
|
|
|
// EOF in the middle of delimited data for unknown value. |
|
assert_does_not_parse_at_eof( |
|
cat( tag(UNKNOWN_FIELD, wire_type), varint(1) )); |
|
|
|
if (type == UPB_TYPE(MESSAGE)) { |
|
// Submessage ends in the middle of a value. |
|
buffer incomplete_submsg = |
|
cat ( tag(UPB_TYPE(INT32), UPB_WIRE_TYPE_VARINT), |
|
incompletes[UPB_WIRE_TYPE_VARINT] ); |
|
assert_does_not_parse( |
|
cat( tag(fieldnum, UPB_WIRE_TYPE_DELIMITED), |
|
varint(incomplete_submsg.len()), |
|
incomplete_submsg )); |
|
} |
|
} else { |
|
// Packed region ends in the middle of a value. |
|
assert_does_not_parse( |
|
cat( tag(rep_fieldnum, UPB_WIRE_TYPE_DELIMITED), |
|
varint(incomplete.len()), |
|
incomplete )); |
|
|
|
// EOF in the middle of packed region. |
|
assert_does_not_parse_at_eof( |
|
cat( tag(rep_fieldnum, UPB_WIRE_TYPE_DELIMITED), varint(1) )); |
|
} |
|
} |
|
|
|
// "33" and "66" are just two random values that all numeric types can |
|
// represent. |
|
void test_valid_data_for_type(upb_fieldtype_t type, |
|
const buffer& enc33, const buffer& enc66) { |
|
uint32_t fieldnum = type; |
|
uint32_t rep_fieldnum = rep_fn(type); |
|
int wire_type = upb_decoder_types[type].native_wire_type; |
|
|
|
// Non-repeated |
|
assert_successful_parse( |
|
cat( tag(fieldnum, wire_type), enc33, |
|
tag(fieldnum, wire_type), enc66 ), |
|
LINE("<") |
|
LINE("%u:33") |
|
LINE("%u:66") |
|
LINE(">"), fieldnum, fieldnum); |
|
|
|
// Non-packed repeated. |
|
assert_successful_parse( |
|
cat( tag(rep_fieldnum, wire_type), enc33, |
|
tag(rep_fieldnum, wire_type), enc66 ), |
|
LINE("<") |
|
LINE("%u:[") |
|
LINE(" %u:33") |
|
LINE(" %u:66") |
|
LINE("]") |
|
LINE(">"), rep_fieldnum, rep_fieldnum, rep_fieldnum); |
|
|
|
// Packed repeated. |
|
assert_successful_parse( |
|
cat( tag(rep_fieldnum, UPB_WIRE_TYPE_DELIMITED), |
|
delim(cat( enc33, enc66 )) ), |
|
LINE("<") |
|
LINE("%u:[") |
|
LINE(" %u:33") |
|
LINE(" %u:66") |
|
LINE("]") |
|
LINE(">"), rep_fieldnum, rep_fieldnum, rep_fieldnum); |
|
} |
|
|
|
void test_valid_data_for_signed_type(upb_fieldtype_t type, |
|
const buffer& enc33, const buffer& enc66) { |
|
uint32_t fieldnum = type; |
|
uint32_t rep_fieldnum = rep_fn(type); |
|
int wire_type = upb_decoder_types[type].native_wire_type; |
|
|
|
// Non-repeated |
|
assert_successful_parse( |
|
cat( tag(fieldnum, wire_type), enc33, |
|
tag(fieldnum, wire_type), enc66 ), |
|
LINE("<") |
|
LINE("%u:33") |
|
LINE("%u:-66") |
|
LINE(">"), fieldnum, fieldnum); |
|
|
|
// Non-packed repeated. |
|
assert_successful_parse( |
|
cat( tag(rep_fieldnum, wire_type), enc33, |
|
tag(rep_fieldnum, wire_type), enc66 ), |
|
LINE("<") |
|
LINE("%u:[") |
|
LINE(" %u:33") |
|
LINE(" %u:-66") |
|
LINE("]") |
|
LINE(">"), rep_fieldnum, rep_fieldnum, rep_fieldnum); |
|
|
|
// Packed repeated. |
|
assert_successful_parse( |
|
cat( tag(rep_fieldnum, UPB_WIRE_TYPE_DELIMITED), |
|
delim(cat( enc33, enc66 )) ), |
|
LINE("<") |
|
LINE("%u:[") |
|
LINE(" %u:33") |
|
LINE(" %u:-66") |
|
LINE("]") |
|
LINE(">"), rep_fieldnum, rep_fieldnum, rep_fieldnum); |
|
} |
|
|
|
// Test that invalid protobufs are properly detected (without crashing) and |
|
// have an error reported. Field numbers match registered handlers above. |
|
void test_invalid() { |
|
test_premature_eof_for_type(UPB_TYPE(DOUBLE)); |
|
test_premature_eof_for_type(UPB_TYPE(FLOAT)); |
|
test_premature_eof_for_type(UPB_TYPE(INT64)); |
|
test_premature_eof_for_type(UPB_TYPE(UINT64)); |
|
test_premature_eof_for_type(UPB_TYPE(INT32)); |
|
test_premature_eof_for_type(UPB_TYPE(FIXED64)); |
|
test_premature_eof_for_type(UPB_TYPE(FIXED32)); |
|
test_premature_eof_for_type(UPB_TYPE(BOOL)); |
|
test_premature_eof_for_type(UPB_TYPE(STRING)); |
|
test_premature_eof_for_type(UPB_TYPE(BYTES)); |
|
test_premature_eof_for_type(UPB_TYPE(UINT32)); |
|
test_premature_eof_for_type(UPB_TYPE(ENUM)); |
|
test_premature_eof_for_type(UPB_TYPE(SFIXED32)); |
|
test_premature_eof_for_type(UPB_TYPE(SFIXED64)); |
|
test_premature_eof_for_type(UPB_TYPE(SINT32)); |
|
test_premature_eof_for_type(UPB_TYPE(SINT64)); |
|
|
|
// EOF inside a tag's varint. |
|
assert_does_not_parse_at_eof( buffer("\x80") ); |
|
|
|
// EOF inside a known group. |
|
assert_does_not_parse_at_eof( tag(4, UPB_WIRE_TYPE_START_GROUP) ); |
|
|
|
// EOF inside an unknown group. |
|
assert_does_not_parse_at_eof( tag(UNKNOWN_FIELD, UPB_WIRE_TYPE_START_GROUP) ); |
|
|
|
// End group that we are not currently in. |
|
assert_does_not_parse( tag(4, UPB_WIRE_TYPE_END_GROUP) ); |
|
|
|
// Field number is 0. |
|
assert_does_not_parse( |
|
cat( tag(0, UPB_WIRE_TYPE_DELIMITED), varint(0) )); |
|
|
|
// Field number is too large. |
|
assert_does_not_parse( |
|
cat( tag(UPB_MAX_FIELDNUMBER + 1, UPB_WIRE_TYPE_DELIMITED), |
|
varint(0) )); |
|
|
|
// Test exceeding the resource limit of stack depth. |
|
buffer buf; |
|
for (int i = 0; i < UPB_MAX_NESTING; i++) { |
|
buf.assign(submsg(UPB_TYPE(MESSAGE), buf)); |
|
} |
|
assert_does_not_parse(buf); |
|
} |
|
|
|
void test_valid() { |
|
test_valid_data_for_signed_type(UPB_TYPE(DOUBLE), dbl(33), dbl(-66)); |
|
test_valid_data_for_signed_type(UPB_TYPE(FLOAT), flt(33), flt(-66)); |
|
test_valid_data_for_signed_type(UPB_TYPE(INT64), varint(33), varint(-66)); |
|
test_valid_data_for_signed_type(UPB_TYPE(INT32), varint(33), varint(-66)); |
|
test_valid_data_for_signed_type(UPB_TYPE(ENUM), varint(33), varint(-66)); |
|
test_valid_data_for_signed_type(UPB_TYPE(SFIXED32), uint32(33), uint32(-66)); |
|
test_valid_data_for_signed_type(UPB_TYPE(SFIXED64), uint64(33), uint64(-66)); |
|
test_valid_data_for_signed_type(UPB_TYPE(SINT32), zz32(33), zz32(-66)); |
|
test_valid_data_for_signed_type(UPB_TYPE(SINT64), zz64(33), zz64(-66)); |
|
|
|
test_valid_data_for_type(UPB_TYPE(UINT64), varint(33), varint(66)); |
|
test_valid_data_for_type(UPB_TYPE(UINT32), varint(33), varint(66)); |
|
test_valid_data_for_type(UPB_TYPE(FIXED64), uint64(33), uint64(66)); |
|
test_valid_data_for_type(UPB_TYPE(FIXED32), uint32(33), uint32(66)); |
|
|
|
// Test implicit startseq/endseq. |
|
uint32_t repfl_fn = rep_fn(UPB_TYPE(FLOAT)); |
|
uint32_t repdb_fn = rep_fn(UPB_TYPE(DOUBLE)); |
|
assert_successful_parse( |
|
cat( tag(repfl_fn, UPB_WIRE_TYPE_32BIT), flt(33), |
|
tag(repdb_fn, UPB_WIRE_TYPE_64BIT), dbl(66) ), |
|
LINE("<") |
|
LINE("%u:[") |
|
LINE(" %u:33") |
|
LINE("]") |
|
LINE("%u:[") |
|
LINE(" %u:66") |
|
LINE("]") |
|
LINE(">"), repfl_fn, repfl_fn, repdb_fn, repdb_fn); |
|
|
|
// Submessage tests. |
|
uint32_t msg_fn = UPB_TYPE(MESSAGE); |
|
assert_successful_parse( |
|
submsg(msg_fn, submsg(msg_fn, submsg(msg_fn, buffer()))), |
|
LINE("<") |
|
LINE("%u:{") |
|
LINE(" <") |
|
LINE(" %u:{") |
|
LINE(" <") |
|
LINE(" %u:{") |
|
LINE(" <") |
|
LINE(" >") |
|
LINE(" }") |
|
LINE(" >") |
|
LINE(" }") |
|
LINE(" >") |
|
LINE("}") |
|
LINE(">"), msg_fn, msg_fn, msg_fn); |
|
|
|
uint32_t repm_fn = rep_fn(UPB_TYPE(MESSAGE)); |
|
assert_successful_parse( |
|
submsg(repm_fn, submsg(repm_fn, buffer())), |
|
LINE("<") |
|
LINE("%u:[") |
|
LINE(" %u:{") |
|
LINE(" <") |
|
LINE(" %u:[") |
|
LINE(" %u:{") |
|
LINE(" <") |
|
LINE(" >") |
|
LINE(" }") |
|
LINE(" ]") |
|
LINE(" >") |
|
LINE(" }") |
|
LINE("]") |
|
LINE(">"), repm_fn, repm_fn, repm_fn, repm_fn); |
|
|
|
// Staying within the stack limit should work properly. |
|
buffer buf; |
|
buffer textbuf; |
|
int total = UPB_MAX_NESTING - 1; |
|
for (int i = 0; i < total; i++) { |
|
buf.assign(submsg(UPB_TYPE(MESSAGE), buf)); |
|
indentbuf(&textbuf, i); |
|
textbuf.append("<\n"); |
|
indentbuf(&textbuf, i); |
|
textbuf.appendf("%u:{\n", UPB_TYPE(MESSAGE)); |
|
} |
|
indentbuf(&textbuf, total); |
|
textbuf.append("<\n"); |
|
indentbuf(&textbuf, total); |
|
textbuf.append(">\n"); |
|
for (int i = 0; i < total; i++) { |
|
indentbuf(&textbuf, total - i - 1); |
|
textbuf.append("}\n"); |
|
indentbuf(&textbuf, total - i - 1); |
|
textbuf.append(">\n"); |
|
} |
|
assert_successful_parse(buf, "%s", textbuf.buf()); |
|
} |
|
|
|
void run_tests() { |
|
test_invalid(); |
|
test_valid(); |
|
} |
|
|
|
int main() { |
|
for (int i = 0; i < UPB_MAX_NESTING; i++) { |
|
closures[i] = i; |
|
} |
|
// Construct decoder plan. |
|
upb_handlers *h = upb_handlers_new(); |
|
reghandlers(upb_handlers_newmhandlers(h)); |
|
|
|
// Create an empty handlers to make sure that the decoder can handle empty |
|
// messages. |
|
upb_handlers_newmhandlers(h); |
|
|
|
// Test without JIT. |
|
plan = upb_decoderplan_new(h, false); |
|
run_tests(); |
|
upb_decoderplan_unref(plan); |
|
|
|
// Test JIT. |
|
plan = upb_decoderplan_new(h, true); |
|
#ifdef UPB_USE_JIT_X64 |
|
ASSERT(upb_decoderplan_hasjitcode(plan)); |
|
#else |
|
ASSERT(!upb_decoderplan_hasjitcode(plan)); |
|
#endif |
|
run_tests(); |
|
upb_decoderplan_unref(plan); |
|
|
|
plan = NULL; |
|
printf("All tests passed, %d assertions.\n", num_assertions); |
|
upb_handlers_unref(h); |
|
return 0; |
|
}
|
|
|