X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=pb_decode.c;h=b7a0093ed0a2f0bcef43805dccb25cad4d01d4f3;hb=fa45589d5736d2ce7f37ddd03daad2c638e38848;hp=26d7c2b7814b51b8765c024b7be7b2bc3552b3a8;hpb=418f7d88b3f58603fe03d0060b8aaba905ca56c8;p=apps%2Fagl-service-can-low-level.git diff --git a/pb_decode.c b/pb_decode.c index 26d7c2b7..b7a0093e 100644 --- a/pb_decode.c +++ b/pb_decode.c @@ -23,15 +23,17 @@ typedef bool (*pb_decoder_t)(pb_istream_t *stream, const pb_field_t *field, void *dest) checkreturn; -static bool checkreturn buf_read(pb_istream_t *stream, uint8_t *buf, size_t count); +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest); -static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, uint8_t *buf, size_t *size); +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size); static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter); static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter); static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter); +static void iter_from_extension(pb_field_iter_t *iter, pb_extension_t *extension); static bool checkreturn default_extension_decoder(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type); static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_field_iter_t *iter); static bool checkreturn find_extension_field(pb_field_iter_t *iter); +static void pb_field_set_to_default(pb_field_iter_t *iter); static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_struct); static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest); static bool checkreturn pb_dec_uvarint(pb_istream_t *stream, const pb_field_t *field, void *dest); @@ -46,6 +48,7 @@ static bool checkreturn pb_skip_string(pb_istream_t *stream); #ifdef PB_ENABLE_MALLOC static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size); +static bool checkreturn pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *iter); static void pb_release_single_field(const pb_field_iter_t *iter); #endif @@ -69,10 +72,10 @@ static const pb_decoder_t PB_DECODERS[PB_LTYPES_COUNT] = { * pb_istream_t implementation * *******************************/ -static bool checkreturn buf_read(pb_istream_t *stream, uint8_t *buf, size_t count) +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) { - uint8_t *source = (uint8_t*)stream->state; - stream->state = source + count; + const pb_byte_t *source = (const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + count; if (buf != NULL) { @@ -83,13 +86,13 @@ static bool checkreturn buf_read(pb_istream_t *stream, uint8_t *buf, size_t coun return true; } -bool checkreturn pb_read(pb_istream_t *stream, uint8_t *buf, size_t count) +bool checkreturn pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) { #ifndef PB_BUFFER_ONLY if (buf == NULL && stream->callback != buf_read) { /* Skip input bytes */ - uint8_t tmp[16]; + pb_byte_t tmp[16]; while (count > 16) { if (!pb_read(stream, tmp, 16)) @@ -119,7 +122,7 @@ bool checkreturn pb_read(pb_istream_t *stream, uint8_t *buf, size_t count) /* Read a single byte from input stream. buf may not be NULL. * This is an optimization for the varint decoding. */ -static bool checkreturn pb_readbyte(pb_istream_t *stream, uint8_t *buf) +static bool checkreturn pb_readbyte(pb_istream_t *stream, pb_byte_t *buf) { if (stream->bytes_left == 0) PB_RETURN_ERROR(stream, "end-of-stream"); @@ -128,8 +131,8 @@ static bool checkreturn pb_readbyte(pb_istream_t *stream, uint8_t *buf) if (!stream->callback(stream, buf, 1)) PB_RETURN_ERROR(stream, "io error"); #else - *buf = *(uint8_t*)stream->state; - stream->state = (uint8_t*)stream->state + 1; + *buf = *(const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + 1; #endif stream->bytes_left--; @@ -137,15 +140,23 @@ static bool checkreturn pb_readbyte(pb_istream_t *stream, uint8_t *buf) return true; } -pb_istream_t pb_istream_from_buffer(uint8_t *buf, size_t bufsize) +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t bufsize) { pb_istream_t stream; + /* Cast away the const from buf without a compiler error. We are + * careful to use it only in a const manner in the callbacks. + */ + union { + void *state; + const void *c_state; + } state; #ifdef PB_BUFFER_ONLY stream.callback = NULL; #else stream.callback = &buf_read; #endif - stream.state = buf; + state.c_state = buf; + stream.state = state.state; stream.bytes_left = bufsize; #ifndef PB_NO_ERRMSG stream.errmsg = NULL; @@ -159,7 +170,7 @@ pb_istream_t pb_istream_from_buffer(uint8_t *buf, size_t bufsize) static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) { - uint8_t byte; + pb_byte_t byte; uint32_t result; if (!pb_readbyte(stream, &byte)) @@ -173,7 +184,7 @@ static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) else { /* Multibyte case */ - uint8_t bitpos = 7; + uint_fast8_t bitpos = 7; result = byte & 0x7F; do @@ -185,7 +196,7 @@ static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) return false; result |= (uint32_t)(byte & 0x7F) << bitpos; - bitpos = (uint8_t)(bitpos + 7); + bitpos = (uint_fast8_t)(bitpos + 7); } while (byte & 0x80); } @@ -195,8 +206,8 @@ static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest) { - uint8_t byte; - uint8_t bitpos = 0; + pb_byte_t byte; + uint_fast8_t bitpos = 0; uint64_t result = 0; do @@ -208,7 +219,7 @@ bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest) return false; result |= (uint64_t)(byte & 0x7F) << bitpos; - bitpos = (uint8_t)(bitpos + 7); + bitpos = (uint_fast8_t)(bitpos + 7); } while (byte & 0x80); *dest = result; @@ -217,7 +228,7 @@ bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest) bool checkreturn pb_skip_varint(pb_istream_t *stream) { - uint8_t byte; + pb_byte_t byte; do { if (!pb_read(stream, &byte, 1)) @@ -276,7 +287,7 @@ bool checkreturn pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type) /* Read a raw value to buffer, for the purpose of passing it to callback as * a substream. Size is maximum size on call, and actual size on return. */ -static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, uint8_t *buf, size_t *size) +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size) { size_t max_size = *size; switch (wire_type) @@ -364,7 +375,7 @@ static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t while (substream.bytes_left > 0 && *size < iter->pos->array_size) { - void *pItem = (uint8_t*)iter->pData + iter->pos->data_size * (*size); + void *pItem = (char*)iter->pData + iter->pos->data_size * (*size); if (!func(&substream, iter->pos, pItem)) { status = false; @@ -383,7 +394,7 @@ static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t { /* Repeated field */ pb_size_t *size = (pb_size_t*)iter->pSize; - void *pItem = (uint8_t*)iter->pData + iter->pos->data_size * (*size); + void *pItem = (char*)iter->pData + iter->pos->data_size * (*size); if (*size >= iter->pos->array_size) PB_RETURN_ERROR(stream, "array overflow"); @@ -391,6 +402,17 @@ static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t return func(stream, iter->pos, pItem); } + case PB_HTYPE_ONEOF: + *(pb_size_t*)iter->pSize = iter->pos->tag; + if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE) + { + /* We memset to zero so that any callbacks are set to NULL. + * Then set any default values. */ + memset(iter->pData, 0, iter->pos->data_size); + pb_message_set_to_defaults((const pb_field_t*)iter->pos->ptr, iter->pData); + } + return func(stream, iter->pos, iter->pData); + default: PB_RETURN_ERROR(stream, "invalid field type"); } @@ -468,6 +490,7 @@ static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_ { case PB_HTYPE_REQUIRED: case PB_HTYPE_OPTIONAL: + case PB_HTYPE_ONEOF: if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE && *(void**)iter->pData != NULL) { @@ -475,6 +498,11 @@ static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_ pb_release_single_field(iter); } + if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + *(pb_size_t*)iter->pSize = iter->pos->tag; + } + if (PB_LTYPE(type) == PB_LTYPE_STRING || PB_LTYPE(type) == PB_LTYPE_BYTES) { @@ -520,7 +548,7 @@ static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_ } /* Decode the array entry */ - pItem = *(uint8_t**)iter->pData + iter->pos->data_size * (*size); + pItem = *(char**)iter->pData + iter->pos->data_size * (*size); initialize_pointer_field(pItem, iter); if (!func(&substream, iter->pos, pItem)) { @@ -556,11 +584,11 @@ static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_ if (!allocate_field(stream, iter->pData, iter->pos->data_size, *size)) return false; - pItem = *(uint8_t**)iter->pData + iter->pos->data_size * (*size - 1); + pItem = *(char**)iter->pData + iter->pos->data_size * (*size - 1); initialize_pointer_field(pItem, iter); return func(stream, iter->pos, pItem); } - + default: PB_RETURN_ERROR(stream, "invalid field type"); } @@ -603,7 +631,7 @@ static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type * which in turn allows to use same callback for packed and * not-packed fields. */ pb_istream_t substream; - uint8_t buffer[10]; + pb_byte_t buffer[10]; size_t size = sizeof(buffer); if (!read_raw_value(stream, wire_type, buffer, &size)) @@ -616,6 +644,16 @@ static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter) { +#ifdef PB_ENABLE_MALLOC + /* When decoding an oneof field, check if there is old data that must be + * released first. */ + if (PB_HTYPE(iter->pos->type) == PB_HTYPE_ONEOF) + { + if (!pb_release_union_field(stream, iter)) + return false; + } +#endif + switch (PB_ATYPE(iter->pos->type)) { case PB_ATYPE_STATIC: @@ -632,32 +670,38 @@ static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_t } } -/* Default handler for extension fields. Expects a pb_field_t structure - * in extension->type->arg. */ -static bool checkreturn default_extension_decoder(pb_istream_t *stream, - pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type) +static void iter_from_extension(pb_field_iter_t *iter, pb_extension_t *extension) { - const pb_field_t *field = (const pb_field_t*)extension->type->arg; - pb_field_iter_t iter; - - if (field->tag != tag) - return true; - /* Fake a field iterator for the extension field. * It is not actually safe to advance this iterator, but decode_field * will not even try to. */ - (void)pb_field_iter_begin(&iter, field, extension->dest); - iter.pData = extension->dest; - iter.pSize = &extension->found; + const pb_field_t *field = (const pb_field_t*)extension->type->arg; + (void)pb_field_iter_begin(iter, field, extension->dest); + iter->pData = extension->dest; + iter->pSize = &extension->found; if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) { /* For pointer extensions, the pointer is stored directly * in the extension structure. This avoids having an extra * indirection. */ - iter.pData = &extension->dest; + iter->pData = &extension->dest; } +} + +/* Default handler for extension fields. Expects a pb_field_t structure + * in extension->type->arg. */ +static bool checkreturn default_extension_decoder(pb_istream_t *stream, + pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type) +{ + const pb_field_t *field = (const pb_field_t*)extension->type->arg; + pb_field_iter_t iter; + + if (field->tag != tag) + return true; + iter_from_extension(&iter, extension); + extension->found = true; return decode_field(stream, wire_type, &iter); } @@ -703,64 +747,88 @@ static bool checkreturn find_extension_field(pb_field_iter_t *iter) } /* Initialize message fields to default values, recursively */ -static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_struct) +static void pb_field_set_to_default(pb_field_iter_t *iter) { - pb_field_iter_t iter; - - if (!pb_field_iter_begin(&iter, fields, dest_struct)) - return; /* Empty message type */ + pb_type_t type; + type = iter->pos->type; - do + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) { - pb_type_t type; - type = iter.pos->type; - - if (PB_ATYPE(type) == PB_ATYPE_STATIC) + pb_extension_t *ext = *(pb_extension_t* const *)iter->pData; + while (ext != NULL) { - if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL) - { - /* Set has_field to false. Still initialize the optional field - * itself also. */ - *(bool*)iter.pSize = false; - } - else if (PB_HTYPE(type) == PB_HTYPE_REPEATED) - { - /* Set array count to 0, no need to initialize contents. */ - *(pb_size_t*)iter.pSize = 0; - continue; - } - - if (PB_LTYPE(iter.pos->type) == PB_LTYPE_SUBMESSAGE) + pb_field_iter_t ext_iter; + ext->found = false; + iter_from_extension(&ext_iter, ext); + pb_field_set_to_default(&ext_iter); + ext = ext->next; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_STATIC) + { + bool init_data = true; + if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL) + { + /* Set has_field to false. Still initialize the optional field + * itself also. */ + *(bool*)iter->pSize = false; + } + else if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + /* REPEATED: Set array count to 0, no need to initialize contents. + ONEOF: Set which_field to 0. */ + *(pb_size_t*)iter->pSize = 0; + init_data = false; + } + + if (init_data) + { + if (PB_LTYPE(iter->pos->type) == PB_LTYPE_SUBMESSAGE) { /* Initialize submessage to defaults */ - pb_message_set_to_defaults((const pb_field_t *) iter.pos->ptr, iter.pData); + pb_message_set_to_defaults((const pb_field_t *) iter->pos->ptr, iter->pData); } - else if (iter.pos->ptr != NULL) + else if (iter->pos->ptr != NULL) { /* Initialize to default value */ - memcpy(iter.pData, iter.pos->ptr, iter.pos->data_size); + memcpy(iter->pData, iter->pos->ptr, iter->pos->data_size); } else { /* Initialize to zeros */ - memset(iter.pData, 0, iter.pos->data_size); - } - } - else if (PB_ATYPE(type) == PB_ATYPE_POINTER) - { - /* Initialize the pointer to NULL. */ - *(void**)iter.pData = NULL; - - /* Initialize array count to 0. */ - if (PB_HTYPE(type) == PB_HTYPE_REPEATED) - { - *(pb_size_t*)iter.pSize = 0; + memset(iter->pData, 0, iter->pos->data_size); } } - else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + } + else if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL. */ + *(void**)iter->pData = NULL; + + /* Initialize array count to 0. */ + if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) { - /* Don't overwrite callback */ + *(pb_size_t*)iter->pSize = 0; } + } + else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + { + /* Don't overwrite callback */ + } +} + +static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_struct) +{ + pb_field_iter_t iter; + + if (!pb_field_iter_begin(&iter, fields, dest_struct)) + return; /* Empty message type */ + + do + { + pb_field_set_to_default(&iter); } while (pb_field_iter_next(&iter)); } @@ -770,7 +838,8 @@ static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_str bool checkreturn pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct) { - uint8_t fields_seen[(PB_MAX_REQUIRED_FIELDS + 7) / 8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint32_t fields_seen[(PB_MAX_REQUIRED_FIELDS + 31) / 32] = {0, 0}; + const uint32_t allbits = ~(uint32_t)0; uint32_t extension_range_start = 0; pb_field_iter_t iter; @@ -826,7 +895,8 @@ bool checkreturn pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[ if (PB_HTYPE(iter.pos->type) == PB_HTYPE_REQUIRED && iter.required_field_index < PB_MAX_REQUIRED_FIELDS) { - fields_seen[iter.required_field_index >> 3] |= (uint8_t)(1 << (iter.required_field_index & 7)); + uint32_t tmp = ((uint32_t)1 << (iter.required_field_index & 31)); + fields_seen[iter.required_field_index >> 5] |= tmp; } if (!decode_field(stream, wire_type, &iter)) @@ -851,16 +921,19 @@ bool checkreturn pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[ if (PB_HTYPE(last_type) == PB_HTYPE_REQUIRED && iter.pos->tag != 0) req_field_count++; - /* Check the whole bytes */ - for (i = 0; i < (req_field_count >> 3); i++) + if (req_field_count > 0) { - if (fields_seen[i] != 0xFF) + /* Check the whole words */ + for (i = 0; i < (req_field_count >> 5); i++) + { + if (fields_seen[i] != allbits) + PB_RETURN_ERROR(stream, "missing required field"); + } + + /* Check the remaining bits */ + if (fields_seen[req_field_count >> 5] != (allbits >> (32 - (req_field_count & 31)))) PB_RETURN_ERROR(stream, "missing required field"); } - - /* Check the remaining bits */ - if (fields_seen[req_field_count >> 3] != (0xFF >> (8 - (req_field_count & 7)))) - PB_RETURN_ERROR(stream, "missing required field"); } return true; @@ -894,11 +967,86 @@ bool pb_decode_delimited(pb_istream_t *stream, const pb_field_t fields[], void * } #ifdef PB_ENABLE_MALLOC +/* Given an oneof field, if there has already been a field inside this oneof, + * release it before overwriting with a different one. */ +static bool pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *iter) +{ + pb_size_t old_tag = *(pb_size_t*)iter->pSize; /* Previous which_ value */ + pb_size_t new_tag = iter->pos->tag; /* New which_ value */ + + if (old_tag == 0) + return true; /* Ok, no old data in union */ + + if (old_tag == new_tag) + return true; /* Ok, old data is of same type => merge */ + + /* Release old data. The find can fail if the message struct contains + * invalid data. */ + if (!pb_field_iter_find(iter, old_tag)) + PB_RETURN_ERROR(stream, "invalid union tag"); + + pb_release_single_field(iter); + + /* Restore iterator to where it should be. + * This shouldn't fail unless the pb_field_t structure is corrupted. */ + if (!pb_field_iter_find(iter, new_tag)) + PB_RETURN_ERROR(stream, "iterator error"); + + return true; +} + static void pb_release_single_field(const pb_field_iter_t *iter) { pb_type_t type; type = iter->pos->type; + if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + if (*(pb_size_t*)iter->pSize != iter->pos->tag) + return; /* This is not the current field in the union */ + } + + /* Release anything contained inside an extension or submsg. + * This has to be done even if the submsg itself is statically + * allocated. */ + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + /* Release fields from all extensions in the linked list */ + pb_extension_t *ext = *(pb_extension_t**)iter->pData; + while (ext != NULL) + { + pb_field_iter_t ext_iter; + iter_from_extension(&ext_iter, ext); + pb_release_single_field(&ext_iter); + ext = ext->next; + } + } + else if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE) + { + /* Release fields in submessage or submsg array */ + void *pItem = iter->pData; + pb_size_t count = 1; + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + pItem = *(void**)iter->pData; + } + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + count = *(pb_size_t*)iter->pSize; + } + + if (pItem) + { + while (count--) + { + pb_release((const pb_field_t*)iter->pos->ptr, pItem); + pItem = (char*)pItem + iter->pos->data_size; + } + } + } + if (PB_ATYPE(type) == PB_ATYPE_POINTER) { if (PB_HTYPE(type) == PB_HTYPE_REPEATED && @@ -913,28 +1061,12 @@ static void pb_release_single_field(const pb_field_iter_t *iter) pb_free(*pItem); *pItem++ = NULL; } - *(pb_size_t*)iter->pSize = 0; } - else if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE) + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) { - /* Release fields in submessages */ - void *pItem = *(void**)iter->pData; - if (pItem) - { - pb_size_t count = 1; - - if (PB_HTYPE(type) == PB_HTYPE_REPEATED) - { - count = *(pb_size_t*)iter->pSize; - *(pb_size_t*)iter->pSize = 0; - } - - while (count--) - { - pb_release((const pb_field_t*)iter->pos->ptr, pItem); - pItem = (uint8_t*)pItem + iter->pos->data_size; - } - } + /* We are going to release the array, so set the size to 0 */ + *(pb_size_t*)iter->pSize = 0; } /* Release main item */ @@ -947,6 +1079,9 @@ void pb_release(const pb_field_t fields[], void *dest_struct) { pb_field_iter_t iter; + if (!dest_struct) + return; /* Ignore NULL pointers, similar to free() */ + if (!pb_field_iter_begin(&iter, fields, dest_struct)) return; /* Empty message type */ @@ -975,92 +1110,109 @@ bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest) bool pb_decode_fixed32(pb_istream_t *stream, void *dest) { - #ifdef __BIG_ENDIAN__ - uint8_t *bytes = (uint8_t*)dest; - uint8_t lebytes[4]; - - if (!pb_read(stream, lebytes, 4)) + pb_byte_t bytes[4]; + + if (!pb_read(stream, bytes, 4)) return false; - bytes[0] = lebytes[3]; - bytes[1] = lebytes[2]; - bytes[2] = lebytes[1]; - bytes[3] = lebytes[0]; + *(uint32_t*)dest = ((uint32_t)bytes[0] << 0) | + ((uint32_t)bytes[1] << 8) | + ((uint32_t)bytes[2] << 16) | + ((uint32_t)bytes[3] << 24); return true; - #else - return pb_read(stream, (uint8_t*)dest, 4); - #endif } bool pb_decode_fixed64(pb_istream_t *stream, void *dest) { - #ifdef __BIG_ENDIAN__ - uint8_t *bytes = (uint8_t*)dest; - uint8_t lebytes[8]; - - if (!pb_read(stream, lebytes, 8)) + pb_byte_t bytes[8]; + + if (!pb_read(stream, bytes, 8)) return false; - bytes[0] = lebytes[7]; - bytes[1] = lebytes[6]; - bytes[2] = lebytes[5]; - bytes[3] = lebytes[4]; - bytes[4] = lebytes[3]; - bytes[5] = lebytes[2]; - bytes[6] = lebytes[1]; - bytes[7] = lebytes[0]; + *(uint64_t*)dest = ((uint64_t)bytes[0] << 0) | + ((uint64_t)bytes[1] << 8) | + ((uint64_t)bytes[2] << 16) | + ((uint64_t)bytes[3] << 24) | + ((uint64_t)bytes[4] << 32) | + ((uint64_t)bytes[5] << 40) | + ((uint64_t)bytes[6] << 48) | + ((uint64_t)bytes[7] << 56); + return true; - #else - return pb_read(stream, (uint8_t*)dest, 8); - #endif } static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest) { uint64_t value; + int64_t svalue; + int64_t clamped; if (!pb_decode_varint(stream, &value)) return false; + /* See issue 97: Google's C++ protobuf allows negative varint values to + * be cast as int32_t, instead of the int64_t that should be used when + * encoding. Previous nanopb versions had a bug in encoding. In order to + * not break decoding of such messages, we cast <=32 bit fields to + * int32_t first to get the sign correct. + */ + if (field->data_size == 8) + svalue = (int64_t)value; + else + svalue = (int32_t)value; + switch (field->data_size) { - case 1: *(int8_t*)dest = (int8_t)value; break; - case 2: *(int16_t*)dest = (int16_t)value; break; - case 4: *(int32_t*)dest = (int32_t)value; break; - case 8: *(int64_t*)dest = (int64_t)value; break; + case 1: clamped = *(int8_t*)dest = (int8_t)svalue; break; + case 2: clamped = *(int16_t*)dest = (int16_t)svalue; break; + case 4: clamped = *(int32_t*)dest = (int32_t)svalue; break; + case 8: clamped = *(int64_t*)dest = svalue; break; default: PB_RETURN_ERROR(stream, "invalid data_size"); } + + if (clamped != svalue) + PB_RETURN_ERROR(stream, "integer too large"); return true; } static bool checkreturn pb_dec_uvarint(pb_istream_t *stream, const pb_field_t *field, void *dest) { - uint64_t value; + uint64_t value, clamped; if (!pb_decode_varint(stream, &value)) return false; switch (field->data_size) { - case 4: *(uint32_t*)dest = (uint32_t)value; break; - case 8: *(uint64_t*)dest = value; break; + case 1: clamped = *(uint_least8_t*)dest = (uint_least8_t)value; break; + case 2: clamped = *(uint_least16_t*)dest = (uint_least16_t)value; break; + case 4: clamped = *(uint32_t*)dest = (uint32_t)value; break; + case 8: clamped = *(uint64_t*)dest = value; break; default: PB_RETURN_ERROR(stream, "invalid data_size"); } + if (clamped != value) + PB_RETURN_ERROR(stream, "integer too large"); + return true; } static bool checkreturn pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest) { - int64_t value; + int64_t value, clamped; if (!pb_decode_svarint(stream, &value)) return false; switch (field->data_size) { - case 4: *(int32_t*)dest = (int32_t)value; break; - case 8: *(int64_t*)dest = value; break; + case 1: clamped = *(int_least8_t*)dest = (int_least8_t)value; break; + case 2: clamped = *(int_least16_t*)dest = (int_least16_t)value; break; + case 4: clamped = *(int32_t*)dest = (int32_t)value; break; + case 8: clamped = *(int64_t*)dest = value; break; default: PB_RETURN_ERROR(stream, "invalid data_size"); } + + if (clamped != value) + PB_RETURN_ERROR(stream, "integer too large"); return true; } @@ -1144,8 +1296,8 @@ static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *fi PB_RETURN_ERROR(stream, "string overflow"); } - status = pb_read(stream, (uint8_t*)dest, size); - *((uint8_t*)dest + size) = 0; + status = pb_read(stream, (pb_byte_t*)dest, size); + *((pb_byte_t*)dest + size) = 0; return status; }