/
githubmirror
/
libcbor
Обзор
Документация
Войти
/
githubmirror
/
libcbor
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/cbor/serialization.c
403 строки
15 KB
Pavel Kalvoda
Fix NULL dereference when serializing/measuring a tag with no item set (#420)
06 апр 2026, 22:35
Не верифицирован
06 апр 2026, 22:35
90d5771
Код
Авторство
О чём код?
/* * Copyright (c) 2014-2020 Pavel Kalvoda <me@pavelkalvoda.com> * * libcbor is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "serialization.h" #include <string.h> #include "cbor/arrays.h" #include "cbor/bytestrings.h" #include "cbor/floats_ctrls.h" #include "cbor/ints.h" #include "cbor/maps.h" #include "cbor/strings.h" #include "cbor/tags.h" #include "encoding.h" #include "internal/memory_utils.h" size_t cbor_serialize(const cbor_item_t* item, unsigned char* buffer, size_t buffer_size) { CBOR_ASSERT_VALID_TYPE(cbor_typeof(item)); switch (cbor_typeof(item)) { case CBOR_TYPE_UINT: return cbor_serialize_uint(item, buffer, buffer_size); case CBOR_TYPE_NEGINT: return cbor_serialize_negint(item, buffer, buffer_size); case CBOR_TYPE_BYTESTRING: return cbor_serialize_bytestring(item, buffer, buffer_size); case CBOR_TYPE_STRING: return cbor_serialize_string(item, buffer, buffer_size); case CBOR_TYPE_ARRAY: return cbor_serialize_array(item, buffer, buffer_size); case CBOR_TYPE_MAP: return cbor_serialize_map(item, buffer, buffer_size); case CBOR_TYPE_TAG: return cbor_serialize_tag(item, buffer, buffer_size); case CBOR_TYPE_FLOAT_CTRL: return cbor_serialize_float_ctrl(item, buffer, buffer_size); default: // LCOV_EXCL_START _CBOR_UNREACHABLE; return 0; // LCOV_EXCL_STOP } } /** Largest integer that can be encoded as embedded in the item leading byte. */ const uint64_t kMaxEmbeddedInt = 23; /** How many bytes will a tag for a nested item of a given `size` take when * encoded.*/ size_t _cbor_encoded_header_size(uint64_t size) { if (size <= kMaxEmbeddedInt) return 1; else if (size <= UINT8_MAX) return 2; else if (size <= UINT16_MAX) return 3; else if (size <= UINT32_MAX) return 5; else return 9; } size_t cbor_serialized_size(const cbor_item_t* item) { CBOR_ASSERT_VALID_TYPE(cbor_typeof(item)); switch (cbor_typeof(item)) { case CBOR_TYPE_UINT: case CBOR_TYPE_NEGINT: CBOR_ASSERT(cbor_int_get_width(item) >= CBOR_INT_8 && cbor_int_get_width(item) <= CBOR_INT_64); switch (cbor_int_get_width(item)) { case CBOR_INT_8: if (cbor_get_uint8(item) <= kMaxEmbeddedInt) return 1; return 2; case CBOR_INT_16: return 3; case CBOR_INT_32: return 5; case CBOR_INT_64: return 9; default: // LCOV_EXCL_START _CBOR_UNREACHABLE; return 0; // LCOV_EXCL_STOP } // Note: We do not _cbor_safe_signaling_add zero-length definite strings, // they would cause zeroes to propagate. All other items are at least one // byte. case CBOR_TYPE_BYTESTRING: { if (cbor_bytestring_is_definite(item)) { size_t header_size = _cbor_encoded_header_size(cbor_bytestring_length(item)); if (cbor_bytestring_length(item) == 0) return header_size; return _cbor_safe_signaling_add(header_size, cbor_bytestring_length(item)); } size_t indef_bytestring_size = 2; // Leading byte + break cbor_item_t** chunks = cbor_bytestring_chunks_handle(item); for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++) { indef_bytestring_size = _cbor_safe_signaling_add( indef_bytestring_size, cbor_serialized_size(chunks[i])); } return indef_bytestring_size; } case CBOR_TYPE_STRING: { if (cbor_string_is_definite(item)) { size_t header_size = _cbor_encoded_header_size(cbor_string_length(item)); if (cbor_string_length(item) == 0) return header_size; return _cbor_safe_signaling_add(header_size, cbor_string_length(item)); } size_t indef_string_size = 2; // Leading byte + break cbor_item_t** chunks = cbor_string_chunks_handle(item); for (size_t i = 0; i < cbor_string_chunk_count(item); i++) { indef_string_size = _cbor_safe_signaling_add( indef_string_size, cbor_serialized_size(chunks[i])); } return indef_string_size; } case CBOR_TYPE_ARRAY: { size_t array_size = cbor_array_is_definite(item) ? _cbor_encoded_header_size(cbor_array_size(item)) : 2; // Leading byte + break cbor_item_t** items = cbor_array_handle(item); for (size_t i = 0; i < cbor_array_size(item); i++) { array_size = _cbor_safe_signaling_add(array_size, cbor_serialized_size(items[i])); } return array_size; } case CBOR_TYPE_MAP: { size_t map_size = cbor_map_is_definite(item) ? _cbor_encoded_header_size(cbor_map_size(item)) : 2; // Leading byte + break struct cbor_pair* items = cbor_map_handle(item); for (size_t i = 0; i < cbor_map_size(item); i++) { map_size = _cbor_safe_signaling_add( map_size, _cbor_safe_signaling_add(cbor_serialized_size(items[i].key), cbor_serialized_size(items[i].value))); } return map_size; } case CBOR_TYPE_TAG: { cbor_item_t* tagged = cbor_tag_item(item); if (tagged == NULL) return 0; return _cbor_safe_signaling_add( _cbor_encoded_header_size(cbor_tag_value(item)), cbor_serialized_size(cbor_move(tagged))); } case CBOR_TYPE_FLOAT_CTRL: CBOR_ASSERT(cbor_float_get_width(item) >= CBOR_FLOAT_0 && cbor_float_get_width(item) <= CBOR_FLOAT_64); switch (cbor_float_get_width(item)) { case CBOR_FLOAT_0: return _cbor_encoded_header_size(cbor_ctrl_value(item)); case CBOR_FLOAT_16: return 3; case CBOR_FLOAT_32: return 5; case CBOR_FLOAT_64: return 9; default: // LCOV_EXCL_START _CBOR_UNREACHABLE; return 0; // LCOV_EXCL_STOP } default: // LCOV_EXCL_START _CBOR_UNREACHABLE; return 0; // LCOV_EXCL_STOP } } size_t cbor_serialize_alloc(const cbor_item_t* item, unsigned char** buffer, size_t* buffer_size) { *buffer = NULL; size_t serialized_size = cbor_serialized_size(item); if (serialized_size == 0) { if (buffer_size != NULL) *buffer_size = 0; return 0; } *buffer = _cbor_malloc(serialized_size); if (*buffer == NULL) { if (buffer_size != NULL) *buffer_size = 0; return 0; } size_t written = cbor_serialize(item, *buffer, serialized_size); CBOR_ASSERT(written == serialized_size); if (buffer_size != NULL) *buffer_size = serialized_size; return written; } size_t cbor_serialize_uint(const cbor_item_t* item, unsigned char* buffer, size_t buffer_size) { CBOR_ASSERT(cbor_isa_uint(item)); CBOR_ASSERT(cbor_int_get_width(item) >= CBOR_INT_8 && cbor_int_get_width(item) <= CBOR_INT_64); // cppcheck-suppress missingReturn switch (cbor_int_get_width(item)) { case CBOR_INT_8: return cbor_encode_uint8(cbor_get_uint8(item), buffer, buffer_size); case CBOR_INT_16: return cbor_encode_uint16(cbor_get_uint16(item), buffer, buffer_size); case CBOR_INT_32: return cbor_encode_uint32(cbor_get_uint32(item), buffer, buffer_size); case CBOR_INT_64: return cbor_encode_uint64(cbor_get_uint64(item), buffer, buffer_size); default: // LCOV_EXCL_START _CBOR_UNREACHABLE; return 0; // LCOV_EXCL_STOP } } size_t cbor_serialize_negint(const cbor_item_t* item, unsigned char* buffer, size_t buffer_size) { CBOR_ASSERT(cbor_isa_negint(item)); CBOR_ASSERT(cbor_int_get_width(item) >= CBOR_INT_8 && cbor_int_get_width(item) <= CBOR_INT_64); // cppcheck-suppress missingReturn switch (cbor_int_get_width(item)) { case CBOR_INT_8: return cbor_encode_negint8(cbor_get_uint8(item), buffer, buffer_size); case CBOR_INT_16: return cbor_encode_negint16(cbor_get_uint16(item), buffer, buffer_size); case CBOR_INT_32: return cbor_encode_negint32(cbor_get_uint32(item), buffer, buffer_size); case CBOR_INT_64: return cbor_encode_negint64(cbor_get_uint64(item), buffer, buffer_size); default: // LCOV_EXCL_START _CBOR_UNREACHABLE; return 0; // LCOV_EXCL_STOP } } size_t cbor_serialize_bytestring(const cbor_item_t* item, unsigned char* buffer, size_t buffer_size) { CBOR_ASSERT(cbor_isa_bytestring(item)); if (cbor_bytestring_is_definite(item)) { size_t length = cbor_bytestring_length(item); size_t written = cbor_encode_bytestring_start(length, buffer, buffer_size); if (written > 0 && (buffer_size - written >= length)) { memcpy(buffer + written, cbor_bytestring_handle(item), length); return written + length; } return 0; } else { CBOR_ASSERT(cbor_bytestring_is_indefinite(item)); size_t chunk_count = cbor_bytestring_chunk_count(item); size_t written = cbor_encode_indef_bytestring_start(buffer, buffer_size); if (written == 0) return 0; cbor_item_t** chunks = cbor_bytestring_chunks_handle(item); for (size_t i = 0; i < chunk_count; i++) { size_t chunk_written = cbor_serialize_bytestring( chunks[i], buffer + written, buffer_size - written); if (chunk_written == 0) return 0; written += chunk_written; } size_t break_written = cbor_encode_break(buffer + written, buffer_size - written); if (break_written == 0) return 0; return written + break_written; } } size_t cbor_serialize_string(const cbor_item_t* item, unsigned char* buffer, size_t buffer_size) { CBOR_ASSERT(cbor_isa_string(item)); if (cbor_string_is_definite(item)) { size_t length = cbor_string_length(item); size_t written = cbor_encode_string_start(length, buffer, buffer_size); if (written && (buffer_size - written >= length)) { memcpy(buffer + written, cbor_string_handle(item), length); return written + length; } return 0; } else { CBOR_ASSERT(cbor_string_is_indefinite(item)); size_t chunk_count = cbor_string_chunk_count(item); size_t written = cbor_encode_indef_string_start(buffer, buffer_size); if (written == 0) return 0; cbor_item_t** chunks = cbor_string_chunks_handle(item); for (size_t i = 0; i < chunk_count; i++) { size_t chunk_written = cbor_serialize_string(chunks[i], buffer + written, buffer_size - written); if (chunk_written == 0) return 0; written += chunk_written; } size_t break_written = cbor_encode_break(buffer + written, buffer_size - written); if (break_written == 0) return 0; return written + break_written; } } size_t cbor_serialize_array(const cbor_item_t* item, unsigned char* buffer, size_t buffer_size) { CBOR_ASSERT(cbor_isa_array(item)); size_t size = cbor_array_size(item), written = 0; cbor_item_t** handle = cbor_array_handle(item); if (cbor_array_is_definite(item)) { written = cbor_encode_array_start(size, buffer, buffer_size); } else { CBOR_ASSERT(cbor_array_is_indefinite(item)); written = cbor_encode_indef_array_start(buffer, buffer_size); } if (written == 0) return 0; for (size_t i = 0; i < size; i++) { size_t item_written = cbor_serialize(*(handle++), buffer + written, buffer_size - written); if (item_written == 0) return 0; written += item_written; } if (cbor_array_is_definite(item)) { return written; } else { CBOR_ASSERT(cbor_array_is_indefinite(item)); size_t break_written = cbor_encode_break(buffer + written, buffer_size - written); if (break_written == 0) return 0; return written + break_written; } } size_t cbor_serialize_map(const cbor_item_t* item, unsigned char* buffer, size_t buffer_size) { CBOR_ASSERT(cbor_isa_map(item)); size_t size = cbor_map_size(item), written = 0; struct cbor_pair* handle = cbor_map_handle(item); if (cbor_map_is_definite(item)) { written = cbor_encode_map_start(size, buffer, buffer_size); } else { CBOR_ASSERT(cbor_map_is_indefinite(item)); written = cbor_encode_indef_map_start(buffer, buffer_size); } if (written == 0) return 0; for (size_t i = 0; i < size; i++) { size_t item_written = cbor_serialize(handle->key, buffer + written, buffer_size - written); if (item_written == 0) { return 0; } written += item_written; item_written = cbor_serialize((handle++)->value, buffer + written, buffer_size - written); if (item_written == 0) return 0; written += item_written; } if (cbor_map_is_definite(item)) { return written; } else { CBOR_ASSERT(cbor_map_is_indefinite(item)); size_t break_written = cbor_encode_break(buffer + written, buffer_size - written); if (break_written == 0) return 0; return written + break_written; } } size_t cbor_serialize_tag(const cbor_item_t* item, unsigned char* buffer, size_t buffer_size) { CBOR_ASSERT(cbor_isa_tag(item)); size_t written = cbor_encode_tag(cbor_tag_value(item), buffer, buffer_size); if (written == 0) return 0; cbor_item_t* tagged = cbor_tag_item(item); if (tagged == NULL) return 0; size_t item_written = cbor_serialize(cbor_move(tagged), buffer + written, buffer_size - written); if (item_written == 0) return 0; return written + item_written; } size_t cbor_serialize_float_ctrl(const cbor_item_t* item, unsigned char* buffer, size_t buffer_size) { CBOR_ASSERT(cbor_isa_float_ctrl(item)); CBOR_ASSERT(cbor_float_get_width(item) >= CBOR_FLOAT_0 && cbor_float_get_width(item) <= CBOR_FLOAT_64); // cppcheck-suppress missingReturn switch (cbor_float_get_width(item)) { case CBOR_FLOAT_0: /* CTRL - special treatment */ return cbor_encode_ctrl(cbor_ctrl_value(item), buffer, buffer_size); case CBOR_FLOAT_16: return cbor_encode_half(cbor_float_get_float2(item), buffer, buffer_size); case CBOR_FLOAT_32: return cbor_encode_single(cbor_float_get_float4(item), buffer, buffer_size); case CBOR_FLOAT_64: return cbor_encode_double(cbor_float_get_float8(item), buffer, buffer_size); default: // LCOV_EXCL_START _CBOR_UNREACHABLE; return 0; // LCOV_EXCL_STOP } }