diff options
Diffstat (limited to 'pb_decode.c')
-rw-r--r-- | pb_decode.c | 85 |
1 files changed, 68 insertions, 17 deletions
diff --git a/pb_decode.c b/pb_decode.c index 2c652f1..4efe8a3 100644 --- a/pb_decode.c +++ b/pb_decode.c @@ -34,6 +34,7 @@ static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_ 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_bool(pb_istream_t *stream, const pb_field_t *field, void *dest); static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest); static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof); static bool checkreturn pb_dec_uvarint(pb_istream_t *stream, const pb_field_t *field, void *dest); @@ -65,6 +66,7 @@ static void pb_release_single_field(const pb_field_iter_t *iter); * Order in the array must match pb_action_t LTYPE numbering. */ static const pb_decoder_t PB_DECODERS[PB_LTYPES_COUNT] = { + &pb_dec_bool, &pb_dec_varint, &pb_dec_uvarint, &pb_dec_svarint, @@ -99,6 +101,9 @@ static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t co bool checkreturn pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) { + if (count == 0) + return true; + #ifndef PB_BUFFER_ONLY if (buf == NULL && stream->callback != buf_read) { @@ -459,14 +464,17 @@ static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t } case PB_HTYPE_ONEOF: - *(pb_size_t*)iter->pSize = iter->pos->tag; - if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE) + if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE && + *(pb_size_t*)iter->pSize != iter->pos->tag) { /* We memset to zero so that any callbacks are set to NULL. - * Then set any default values. */ + * This is because the callbacks might otherwise have values + * from some other union field. */ memset(iter->pData, 0, iter->pos->data_size); pb_message_set_to_defaults((const pb_field_t*)iter->pos->ptr, iter->pData); } + *(pb_size_t*)iter->pSize = iter->pos->tag; + return func(stream, iter->pos, iter->pData); default: @@ -486,6 +494,16 @@ static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t if (data_size == 0 || array_size == 0) PB_RETURN_ERROR(stream, "invalid size"); +#ifdef __AVR__ + /* Workaround for AVR libc bug 53284: http://savannah.nongnu.org/bugs/?53284 + * Realloc to size of 1 byte can cause corruption of the malloc structures. + */ + if (data_size == 1 && array_size == 1) + { + data_size = 2; + } +#endif + /* Check for multiplication overflows. * This code avoids the costly division if the sizes are small enough. * Multiplication is safe as long as only half of bits are set @@ -592,12 +610,25 @@ static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_ while (substream.bytes_left) { + if (*size == PB_SIZE_MAX) + { +#ifndef PB_NO_ERRMSG + stream->errmsg = "too many array entries"; +#endif + status = false; + break; + } + if ((size_t)*size + 1 > allocated_size) { /* Allocate more storage. This tries to guess the * number of remaining entries. Round the division * upwards. */ - allocated_size += (substream.bytes_left - 1) / iter->pos->data_size + 1; + size_t remain = (substream.bytes_left - 1) / iter->pos->data_size + 1; + if (remain < PB_SIZE_MAX - allocated_size) + allocated_size += remain; + else + allocated_size += 1; if (!allocate_field(&substream, iter->pData, iter->pos->data_size, allocated_size)) { @@ -615,15 +646,6 @@ static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_ break; } - if (*size == PB_SIZE_MAX) - { -#ifndef PB_NO_ERRMSG - stream->errmsg = "too many array entries"; -#endif - status = false; - break; - } - (*size)++; } if (!pb_close_string_substream(stream, &substream)) @@ -640,11 +662,11 @@ static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_ if (*size == PB_SIZE_MAX) PB_RETURN_ERROR(stream, "too many array entries"); - (*size)++; - if (!allocate_field(stream, iter->pData, iter->pos->data_size, *size)) + if (!allocate_field(stream, iter->pData, iter->pos->data_size, (size_t)(*size + 1))) return false; - pItem = *(char**)iter->pData + iter->pos->data_size * (*size - 1); + pItem = *(char**)iter->pData + iter->pos->data_size * (*size); + (*size)++; initialize_pointer_field(pItem, iter); return func(stream, iter->pos, pItem); } @@ -1128,7 +1150,14 @@ static bool pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *iter) * 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"); - + + if (PB_ATYPE(iter->pos->type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL to make sure it is valid + * even in case of error return. */ + *(void**)iter->pData = NULL; + } + return true; } @@ -1242,6 +1271,11 @@ void pb_release(const pb_field_t fields[], void *dest_struct) /* Field decoders */ +bool pb_decode_bool(pb_istream_t *stream, bool *dest) +{ + return pb_dec_bool(stream, NULL, (void*)dest); +} + bool pb_decode_svarint(pb_istream_t *stream, pb_int64_t *dest) { pb_uint64_t value; @@ -1291,6 +1325,17 @@ bool pb_decode_fixed64(pb_istream_t *stream, void *dest) } #endif +static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_t *field, void *dest) +{ + uint32_t value; + PB_UNUSED(field); + if (!pb_decode_varint32(stream, &value)) + return false; + + *(bool*)dest = (value != 0); + return true; +} + static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest) { pb_uint64_t value; @@ -1414,6 +1459,9 @@ static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *fie #ifndef PB_ENABLE_MALLOC PB_RETURN_ERROR(stream, "no malloc support"); #else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + if (!allocate_field(stream, dest, alloc_size, 1)) return false; bdest = *(pb_bytes_array_t**)dest; @@ -1449,6 +1497,9 @@ static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *fi #ifndef PB_ENABLE_MALLOC PB_RETURN_ERROR(stream, "no malloc support"); #else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + if (!allocate_field(stream, dest, alloc_size, 1)) return false; dest = *(void**)dest; |