/* * 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. */ #include #include #include #include #include #include "upb/handlers.h" #include "upb/pb/decoder.h" #include "upb/pb/varint.h" #include "upb/upb.h" #include "upb_test.h" typedef struct { char *buf; size_t len; } buffer; // Mem is initialized to NULL. buffer *buffer_new(size_t len) { buffer *buf = malloc(sizeof(*buf)); buf->buf = malloc(len); buf->len = len; memset(buf->buf, 0, buf->len); return buf; } buffer *buffer_new2(const void *data, size_t len) { buffer *buf = buffer_new(len); memcpy(buf->buf, data, len); return buf; } buffer *buffer_new3(const char *data) { return buffer_new2(data, strlen(data)); } buffer *buffer_dup(buffer *buf) { return buffer_new2(buf->buf, buf->len); } void buffer_free(buffer *buf) { free(buf->buf); free(buf); } void buffer_appendf(buffer *buf, const char *fmt, ...) { va_list args; va_start(args, fmt); size_t size = buf->len; buf->len += upb_vrprintf(&buf->buf, &size, buf->len, fmt, args); va_end(args); } void buffer_cat(buffer *buf, buffer *buf2) { size_t newlen = buf->len + buf2->len; buf->buf = realloc(buf->buf, newlen); memcpy(buf->buf + buf->len, buf2->buf, buf2->len); buf->len = newlen; buffer_free(buf2); } bool buffer_eql(buffer *buf, buffer *buf2) { return buf->len == buf2->len && memcmp(buf->buf, buf2->buf, buf->len) == 0; } /* Routines for building arbitrary protos *************************************/ buffer *cat(buffer *arg1, ...) { va_list ap; buffer *arg; va_start(ap, arg1); while ((arg = va_arg(ap, buffer*)) != NULL) { buffer_cat(arg1, arg); } va_end(ap); return arg1; } buffer *varint(uint64_t x) { buffer *buf = buffer_new(UPB_PB_VARINT_MAX_LEN + 1); buf->len = upb_vencode64(x, buf->buf); return buf; } // TODO: proper byte-swapping for big-endian machines. buffer *fixed32(void *data) { return buffer_new2(data, 4); } buffer *fixed64(void *data) { return buffer_new2(data, 8); } buffer *delim(buffer *buf) { return cat( varint(buf->len), buf, NULL ); } 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, buffer *buf) { return cat( tag(fn, UPB_WIRE_TYPE_DELIMITED), delim(buf), NULL ); } /* 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. #define VALUE_HANDLER(member, fmt) \ upb_flow_t value_ ## member(void *closure, upb_value fval, upb_value val) { \ buffer_appendf(closure, "%" PRIu32 ":%" fmt "; ", \ 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) { buffer_appendf(closure, "%" PRIu32 ":%s; ", 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. char *str = upb_byteregion_strdup(upb_value_getbyteregion(val)); buffer_appendf(closure, "%" PRIu32 ":%s; ", upb_value_getuint32(fval), str); free(str); return UPB_CONTINUE; } upb_sflow_t startsubmsg(void *closure, upb_value fval) { buffer_appendf(closure, "%" PRIu32 ":{ ", upb_value_getuint32(fval)); return UPB_CONTINUE_WITH(closure); } upb_flow_t endsubmsg(void *closure, upb_value fval) { (void)fval; buffer_appendf(closure, "} "); return UPB_CONTINUE; } upb_sflow_t startseq(void *closure, upb_value fval) { buffer_appendf(closure, "%" PRIu32 ":[ ", upb_value_getuint32(fval)); return UPB_CONTINUE_WITH(closure); } upb_flow_t endseq(void *closure, upb_value fval) { (void)fval; buffer_appendf(closure, "] "); return UPB_CONTINUE; } 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) { // 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 = _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 = _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 = _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; void run_decoder(buffer *proto, 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 < proto->len; j++) { upb_seamsrc_resetseams(&src, i, j); upb_byteregion *input = upb_seamsrc_allbytes(&src); buffer *output = buffer_new(0); upb_decoder_resetinput(&d, input, output); 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(success == UPB_OK); // 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 (!buffer_eql(output, expected_output)) { fprintf(stderr, "Text mismatch: '%s' vs '%s'\n", output->buf, expected_output->buf); } ASSERT(strcmp(output->buf, expected_output->buf) == 0); } else { ASSERT(success == UPB_ERROR); } buffer_free(output); } } upb_seamsrc_uninit(&src); upb_decoder_uninit(&d); buffer_free(proto); } void assert_successful_parse_at_eof(buffer *proto, const char *expected_fmt, va_list args) { buffer *expected_text = buffer_new(0); size_t size = expected_text->len; expected_text->len += upb_vrprintf(&expected_text->buf, &size, expected_text->len, expected_fmt, args); run_decoder(proto, expected_text); buffer_free(expected_text); } void assert_does_not_parse_at_eof(buffer *proto) { run_decoder(proto, NULL); } void assert_successful_parse(buffer *proto, const char *expected_fmt, ...) { // 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. va_list args, args2; va_start(args, expected_fmt); va_copy(args2, args); assert_successful_parse_at_eof(buffer_dup(proto), expected_fmt, args); assert_successful_parse_at_eof( cat( proto, tag(NOP_FIELD, UPB_WIRE_TYPE_DELIMITED), delim(buffer_new(30)), NULL ), expected_fmt, args2); va_end(args); va_end(args2); } void assert_does_not_parse(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(buffer_dup(proto)); assert_does_not_parse_at_eof( cat( proto, tag(NOP_FIELD, UPB_WIRE_TYPE_DELIMITED), delim( buffer_new(30)), NULL )); } /* The actual tests ***********************************************************/ void test_premature_eof_for_type(upb_fieldtype_t type) { // Incomplete values for each wire type. static const char *incompletes[] = { "\x80", // UPB_WIRE_TYPE_VARINT "abcdefg", // UPB_WIRE_TYPE_64BIT "\x80", // UPB_WIRE_TYPE_DELIMITED (partial length) NULL, // UPB_WIRE_TYPE_START_GROUP (no value required) NULL, // UPB_WIRE_TYPE_END_GROUP (no value required) "abc" // UPB_WIRE_TYPE_32BIT }; uint32_t fieldnum = type; uint32_t rep_fieldnum = rep_fn(type); int wire_type = upb_types[type].native_wire_type; const char *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), buffer_new3(incomplete), NULL )); // EOF inside a known repeated value. assert_does_not_parse_at_eof( cat( tag(rep_fieldnum, wire_type), buffer_new3(incomplete), NULL )); // EOF inside an unknown value. assert_does_not_parse_at_eof( cat( tag(UNKNOWN_FIELD, wire_type), buffer_new3(incomplete), NULL )); 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), NULL )); // 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), NULL )); // EOF in the middle of delimited data for unknown value. assert_does_not_parse_at_eof( cat( tag(UNKNOWN_FIELD, wire_type), varint(1), NULL )); 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), buffer_new3(incompletes[UPB_WIRE_TYPE_VARINT]), NULL ); assert_does_not_parse( cat( tag(fieldnum, UPB_WIRE_TYPE_DELIMITED), varint(incomplete_submsg->len), incomplete_submsg, NULL )); } } else { // Packed region ends in the middle of a value. assert_does_not_parse( cat( tag(rep_fieldnum, UPB_WIRE_TYPE_DELIMITED), varint(strlen(incomplete)), buffer_new3(incomplete), NULL )); // EOF in the middle of packed region. assert_does_not_parse_at_eof( cat( tag(rep_fieldnum, UPB_WIRE_TYPE_DELIMITED), varint(1), NULL )); } } // "33" and "66" are just two random values that all numeric types can // represent. void test_valid_data_for_type(upb_fieldtype_t type, buffer *enc33, buffer *enc66) { uint32_t fieldnum = type; uint32_t rep_fieldnum = rep_fn(type); int wire_type = upb_types[type].native_wire_type; // Non-repeated assert_successful_parse( cat( tag(fieldnum, wire_type), buffer_dup(enc33), tag(fieldnum, wire_type), buffer_dup(enc66), NULL ), "%u:33; %u:66; ", fieldnum, fieldnum); // Non-packed repeated. assert_successful_parse( cat( tag(rep_fieldnum, wire_type), buffer_dup(enc33), tag(rep_fieldnum, wire_type), buffer_dup(enc66), NULL ), "%u:[ %u:33; %u:66; ] ", rep_fieldnum, rep_fieldnum, rep_fieldnum); // Packed repeated. assert_successful_parse( cat( tag(rep_fieldnum, UPB_WIRE_TYPE_DELIMITED), delim(cat( buffer_dup(enc33), buffer_dup(enc66), NULL )), NULL ), "%u:[ %u:33; %u:66; ] ", rep_fieldnum, rep_fieldnum, rep_fieldnum); buffer_free(enc33); buffer_free(enc66); } void test_valid_data_for_signed_type(upb_fieldtype_t type, buffer *enc33, buffer *enc66) { uint32_t fieldnum = type; uint32_t rep_fieldnum = rep_fn(type); int wire_type = upb_types[type].native_wire_type; // Non-repeated assert_successful_parse( cat( tag(fieldnum, wire_type), buffer_dup(enc33), tag(fieldnum, wire_type), buffer_dup(enc66), NULL ), "%u:33; %u:-66; ", fieldnum, fieldnum); // Non-packed repeated. assert_successful_parse( cat( tag(rep_fieldnum, wire_type), buffer_dup(enc33), tag(rep_fieldnum, wire_type), buffer_dup(enc66), NULL ), "%u:[ %u:33; %u:-66; ] ", rep_fieldnum, rep_fieldnum, rep_fieldnum); // Packed repeated. assert_successful_parse( cat( tag(rep_fieldnum, UPB_WIRE_TYPE_DELIMITED), delim(cat( buffer_dup(enc33), buffer_dup(enc66), NULL )), NULL ), "%u:[ %u:33; %u:-66; ] ", rep_fieldnum, rep_fieldnum, rep_fieldnum); buffer_free(enc33); buffer_free(enc66); } // 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_new3("\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), NULL )); // Field number is too large. assert_does_not_parse( cat( tag(UPB_MAX_FIELDNUMBER + 1, UPB_WIRE_TYPE_DELIMITED), varint(0), NULL )); // Test exceeding the resource limit of stack depth. buffer *buf = buffer_new3(""); for (int i = 0; i < UPB_MAX_NESTING; i++) { buf = submsg(UPB_TYPE(MESSAGE), buf); } assert_does_not_parse(buf); // Staying within the stack limit should work properly. buf = buffer_new3(""); buffer *textbuf = buffer_new3(""); int total = UPB_MAX_NESTING - 1; for (int i = 0; i < total; i++) { buf = submsg(UPB_TYPE(MESSAGE), buf); buffer_appendf(textbuf, "%u:{ ", UPB_TYPE(MESSAGE)); } for (int i = 0; i < total; i++) { buffer_appendf(textbuf, "} "); } assert_successful_parse(buf, "%s", textbuf->buf); buffer_free(textbuf); } 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)); // Submessage tests. uint32_t msg_fn = UPB_TYPE(MESSAGE); assert_successful_parse( submsg(msg_fn, submsg(msg_fn, submsg(msg_fn, buffer_new3("")))), "%u:{ %u:{ %u:{ } } } ", 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_new3(""))), "%u:[ %u:{ %u:[ %u:{ } ] } ] ", repm_fn, repm_fn, repm_fn, repm_fn); } void run_tests() { test_invalid(); test_valid(); } int main() { // Construct decoder plan. upb_handlers *h = upb_handlers_new(); reghandlers(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); run_tests(); upb_decoderplan_unref(plan); plan = NULL; printf("All tests passed, %d assertions.\n", num_assertions); upb_handlers_unref(h); return 0; }