/
githubmirror
/
libcbor
Обзор
Документация
Войти
/
githubmirror
/
libcbor
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/cbor/common.h
430 строк
14 KB
Pavel Kalvoda
Document large-allocation risk in cbor_load and clarify test-only CMake flags (#422)
04 апр 2026, 13:58
Не верифицирован
04 апр 2026, 13:58
934865c
Код
Авторство
О чём код?
/* * 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. */ #ifndef LIBCBOR_COMMON_H #define LIBCBOR_COMMON_H #include <assert.h> #include <stdbool.h> #include <stddef.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include "cbor/cbor_export.h" #include "cbor/configuration.h" #include "data.h" #ifdef __cplusplus extern "C" { /** * C99 is not a subset of C++ -- 'restrict' qualifier is not a part of the * language. This is a workaround to keep it in C headers -- compilers allow * linking non-restrict signatures with restrict implementations. * * If you know a nicer way, please do let me know. */ #define CBOR_RESTRICT_POINTER #else // MSVC + C++ workaround #define CBOR_RESTRICT_POINTER CBOR_RESTRICT_SPECIFIER #endif static const uint8_t cbor_major_version = CBOR_MAJOR_VERSION; static const uint8_t cbor_minor_version = CBOR_MINOR_VERSION; static const uint8_t cbor_patch_version = CBOR_PATCH_VERSION; #define CBOR_VERSION \ _CBOR_TO_STR(CBOR_MAJOR_VERSION) \ "." _CBOR_TO_STR(CBOR_MINOR_VERSION) "." _CBOR_TO_STR(CBOR_PATCH_VERSION) #define CBOR_HEX_VERSION \ ((CBOR_MAJOR_VERSION << 16) | (CBOR_MINOR_VERSION << 8) | CBOR_PATCH_VERSION) /* http://stackoverflow.com/questions/1644868/c-define-macro-for-debug-printing */ #ifdef DEBUG #include <stdio.h> #define _cbor_debug_print(fmt, ...) \ do { \ if (DEBUG) \ fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, __LINE__, __func__, \ __VA_ARGS__); \ } while (0) extern bool _cbor_enable_assert; // Like `assert`, but can be dynamically disabled in tests to allow testing // invalid behaviors. #define CBOR_ASSERT(e) assert(!_cbor_enable_assert || (e)) #define _CBOR_TEST_DISABLE_ASSERT(block) \ do { \ _cbor_enable_assert = false; \ block _cbor_enable_assert = true; \ } while (0) #else #define debug_print(fmt, ...) \ do { \ } while (0) #define CBOR_ASSERT(e) #define _CBOR_TEST_DISABLE_ASSERT(block) \ do { \ block \ } while (0) #endif #define CBOR_ASSERT_VALID_TYPE(item_type) \ CBOR_ASSERT(item_type >= CBOR_TYPE_UINT && item_type <= CBOR_TYPE_FLOAT_CTRL); #define _CBOR_TO_STR_(x) #x #define _CBOR_TO_STR(x) _CBOR_TO_STR_(x) /* enables proper double expansion */ #ifdef CBOR_HAS_NODISCARD_ATTRIBUTE #define CBOR_NODISCARD [[nodiscard]] #else #define CBOR_NODISCARD #endif #ifdef __GNUC__ #define _CBOR_UNUSED __attribute__((__unused__)) // Fall back to __attribute__((warn_unused_result)) if we don't have // [[nodiscard]] #ifdef CBOR_HAS_NODISCARD_ATTRIBUTE #define _CBOR_NODISCARD CBOR_NODISCARD #else #define _CBOR_NODISCARD __attribute__((warn_unused_result)) #endif #elif defined(_MSC_VER) #define _CBOR_UNUSED __pragma(warning(suppress : 4100 4101)) #define _CBOR_NODISCARD #else #define _CBOR_UNUSED #define _CBOR_NODISCARD #endif #ifdef CBOR_HAS_BUILTIN_UNREACHABLE #define _CBOR_UNREACHABLE __builtin_unreachable() #else #define _CBOR_UNREACHABLE #endif typedef void* (*_cbor_malloc_t)(size_t); typedef void* (*_cbor_realloc_t)(void*, size_t); typedef void (*_cbor_free_t)(void*); CBOR_EXPORT extern _cbor_malloc_t _cbor_malloc; CBOR_EXPORT extern _cbor_realloc_t _cbor_realloc; CBOR_EXPORT extern _cbor_free_t _cbor_free; // Macro to short-circuit builder functions when memory allocation fails #define _CBOR_NOTNULL(cbor_item) \ do { \ if (cbor_item == NULL) { \ return NULL; \ } \ } while (0) // Macro to short-circuit builders when memory allocation of nested data // fails #define _CBOR_DEPENDENT_NOTNULL(cbor_item, pointer) \ do { \ if (pointer == NULL) { \ _cbor_free(cbor_item); \ return NULL; \ } \ } while (0) /** Sets the memory management routines to use. * * By default, libcbor will use the standard library `malloc`, `realloc`, * and `free`. * * A custom allocator is the intended mechanism for limiting memory * consumption when parsing untrusted CBOR data. Because definite-length * collections pre-allocate storage sized by the declared element count, * a crafted input can request a very large allocation before any element * data is read. A capping `custom_malloc` that returns NULL above a chosen * threshold will cause `cbor_load` to return `CBOR_ERR_MEMERROR` instead of * attempting the allocation. See `examples/capped_alloc.c` for a * self-contained demonstration. * * \rst * .. warning:: * This function modifies the global state and should * therefore be used accordingly. Changing the memory handlers while * allocated items exist will result in a ``free``/``malloc`` mismatch. * This function is not thread safe with respect to both itself and all * the other *libcbor* functions that work with the heap. * * .. note:: * `realloc` implementation must correctly support `NULL` * reallocation (see e.g. http://en.cppreference.com/w/c/memory/realloc) * * \endrst * * @param custom_malloc malloc implementation * @param custom_realloc realloc implementation * @param custom_free free implementation */ CBOR_EXPORT void cbor_set_allocs(_cbor_malloc_t custom_malloc, _cbor_realloc_t custom_realloc, _cbor_free_t custom_free); /* * ============================================================================ * Type manipulation * ============================================================================ */ /** Get the type of the item * * @param item * @return The type */ _CBOR_NODISCARD CBOR_EXPORT cbor_type cbor_typeof( const cbor_item_t* item); /* Will be inlined iff link-time opt is enabled */ /* Standard CBOR Major item types */ /** Does the item have the appropriate major type? * @param item the item * @return Is the item an #CBOR_TYPE_UINT? */ _CBOR_NODISCARD CBOR_EXPORT bool cbor_isa_uint(const cbor_item_t* item); /** Does the item have the appropriate major type? * @param item the item * @return Is the item a #CBOR_TYPE_NEGINT? */ _CBOR_NODISCARD CBOR_EXPORT bool cbor_isa_negint(const cbor_item_t* item); /** Does the item have the appropriate major type? * @param item the item * @return Is the item a #CBOR_TYPE_BYTESTRING? */ _CBOR_NODISCARD CBOR_EXPORT bool cbor_isa_bytestring(const cbor_item_t* item); /** Does the item have the appropriate major type? * @param item the item * @return Is the item a #CBOR_TYPE_STRING? */ _CBOR_NODISCARD CBOR_EXPORT bool cbor_isa_string(const cbor_item_t* item); /** Does the item have the appropriate major type? * @param item the item * @return Is the item an #CBOR_TYPE_ARRAY? */ _CBOR_NODISCARD CBOR_EXPORT bool cbor_isa_array(const cbor_item_t* item); /** Does the item have the appropriate major type? * @param item the item * @return Is the item a #CBOR_TYPE_MAP? */ _CBOR_NODISCARD CBOR_EXPORT bool cbor_isa_map(const cbor_item_t* item); /** Does the item have the appropriate major type? * @param item the item * @return Is the item a #CBOR_TYPE_TAG? */ _CBOR_NODISCARD CBOR_EXPORT bool cbor_isa_tag(const cbor_item_t* item); /** Does the item have the appropriate major type? * @param item the item * @return Is the item a #CBOR_TYPE_FLOAT_CTRL? */ _CBOR_NODISCARD CBOR_EXPORT bool cbor_isa_float_ctrl(const cbor_item_t* item); /* Practical types with respect to their semantics (but not tag values) */ /** Is the item an integer, either positive or negative? * @param item the item * @return Is the item an integer, either positive or negative? */ _CBOR_NODISCARD CBOR_EXPORT bool cbor_is_int(const cbor_item_t* item); /** Is the item an a floating point number? * @param item the item * @return Is the item a floating point number? */ _CBOR_NODISCARD CBOR_EXPORT bool cbor_is_float(const cbor_item_t* item); /** Is the item an a boolean? * @param item the item * @return Is the item a boolean? */ _CBOR_NODISCARD CBOR_EXPORT bool cbor_is_bool(const cbor_item_t* item); /** Does this item represent `null` * * \rst * .. warning:: * This is in no way related to the value of the pointer. * Passing a null pointer will most likely result in a crash. * \endrst * * @param item the item * @return Is the item (CBOR logical) null? */ _CBOR_NODISCARD CBOR_EXPORT bool cbor_is_null(const cbor_item_t* item); /** Does this item represent `undefined` * * \rst * .. warning:: * Care must be taken to distinguish nulls and undefined values in C. * \endrst * * @param item the item * @return Is the item (CBOR logical) undefined? */ _CBOR_NODISCARD CBOR_EXPORT bool cbor_is_undef(const cbor_item_t* item); /* * ============================================================================ * Memory management * ============================================================================ */ /** Increases the item's reference count by one * * Constant complexity; items referring to this one or items being * referred to are not updated. * * This function can be used to extend reference counting to client code. * * @param item Reference to an item * @return The input \p item */ CBOR_EXPORT cbor_item_t* cbor_incref(cbor_item_t* item); /** Decreases the item's reference count by one, deallocating the item if * needed * * In case the item is deallocated, the reference count of all items this * item references will also be #cbor_decref 'ed recursively. * * @param item Reference to an item. Will be set to `NULL` if deallocated */ CBOR_EXPORT void cbor_decref(cbor_item_t** item); /** Decreases the item's reference count by one, deallocating the item if * needed * * Convenience wrapper for #cbor_decref when its set-to-null behavior is * not needed * * @param item Reference to an item */ CBOR_EXPORT void cbor_intermediate_decref(cbor_item_t* item); /** Get the item's reference count * * \rst * .. warning:: * This does *not* account for transitive references. * \endrst * * @todo Add some inline examples for reference counting * * @param item the item * @return the reference count */ _CBOR_NODISCARD CBOR_EXPORT size_t cbor_refcount(const cbor_item_t* item); /** Provides CPP-like move construct * * Decreases the reference count by one, but does not deallocate the item * even if its refcount reaches zero. This is useful for passing * intermediate values to functions that increase reference count. Should * only be used with functions that `incref` their arguments. * * \rst * .. warning:: * If the item is moved without correctly increasing the * reference count afterwards, the memory will be leaked. * \endrst * * @param item Reference to an item * @return the item with reference count decreased by one */ _CBOR_NODISCARD CBOR_EXPORT cbor_item_t* cbor_move(cbor_item_t* item); /** Compares two items for structural (encoding-level) equality * * Two items are structurally equal when they would produce identical bytes * if serialized by a correct CBOR encoder that preserves the in-memory * representation. Every aspect of the encoding counts: * * - **Major type**: `CBOR_TYPE_UINT` and `CBOR_TYPE_NEGINT` are distinct even * if the argument bytes happen to be the same. * - **Integer encoding width**: `cbor_build_uint8(1)` and * `cbor_build_uint16(1)` are *not* structurally equal, even though both * represent the integer 1. Use the integer value accessors (`cbor_get_uint64` * etc.) if you need value-level comparison. * - **Definite vs. indefinite length**: a definite-length array and an * indefinite-length array with the same elements are *not* structurally * equal. * - **Chunk boundaries**: two indefinite bytestrings or strings that carry the * same bytes but split them across a different number of chunks are *not* * structurally equal. * - **Map entry order**: maps are compared positionally — entry at index i in * \p item1 must match entry at index i in \p item2. Maps that carry the same * key-value pairs in a different order are *not* structurally equal. (CBOR * maps are unordered in the data model; for data-model equality see RFC 8949 * §5.6.1.) * - **Float encoding width**: `cbor_build_float2(1.5)` and * `cbor_build_float4(1.5)` are *not* structurally equal. * - **NaN payload**: floating-point NaN values with different bit patterns are * *not* structurally equal. * - **Tag number**: tags with different tag numbers are *not* structurally * equal. * * Runs in time linear in the encoded byte size of the items and performs no * additional memory allocations. * * \rst * .. note:: * This function implements *structural* equality, not the data-model equality * defined by RFC 8949. In the CBOR data model, encoding width is invisible * and maps are unordered sets of key-value pairs. If you need data-model * equality (e.g., ``uint8(1) == uint64(1)``), you must implement it on top * of this library. * \endrst * * Neither \p item1 nor \p item2 may be `NULL`. Passing `NULL` is a * programming error and will trigger an assertion failure in debug builds. * * @param item1 First item; must not be `NULL` * @param item2 Second item; must not be `NULL` * @return `true` if \p item1 and \p item2 are structurally equal */ _CBOR_NODISCARD CBOR_EXPORT bool cbor_structurally_equal(const cbor_item_t* item1, const cbor_item_t* item2); #ifdef __cplusplus } #endif #endif // LIBCBOR_COMMON_H