diff --git a/CHANGELOG.md b/CHANGELOG.md index b84c92a2..0bc0a92f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ Next - [Fix a memory leak when the allocator fails when adding chunks to indefinite strings](https://github.com/PJK/libcbor/pull/246) - Potentially BUILD BREAKING: [Add nodiscard attributes to most functions](https://github.com/PJK/libcbor/pull/248) - **Warning**: This may cause new build warnings and (in rare cases, depending on your configuration) errors +- BREAKING: [Fix `cbor_copy` leaking memory and creating invalid items when the allocator fails](https://github.com/PJK/libcbor/pull/249). + - Previously, the failures were not handled in the interface. Now, `cbor_copy` may return `NULL` upon failure; clients should check the return value +- [Fix `cbor_build_tag` illegal memory behavior when the allocator fails](https://github.com/PJK/libcbor/pull/249) 0.9.0 (2021-11-14) diff --git a/src/cbor.c b/src/cbor.c index e5983ea4..9f7fede6 100644 --- a/src/cbor.c +++ b/src/cbor.c @@ -167,10 +167,24 @@ cbor_item_t *cbor_copy(cbor_item_t *item) { cbor_bytestring_length(item)); } else { cbor_item_t *res = cbor_new_indefinite_bytestring(); - for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++) - cbor_bytestring_add_chunk( - res, - cbor_move(cbor_copy(cbor_bytestring_chunks_handle(item)[i]))); + if (res == NULL) { + return NULL; + } + + for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++) { + cbor_item_t *chunk_copy = + cbor_copy(cbor_bytestring_chunks_handle(item)[i]); + if (chunk_copy == NULL) { + cbor_decref(&res); + return NULL; + } + if (!cbor_bytestring_add_chunk(res, chunk_copy)) { + cbor_decref(&chunk_copy); + cbor_decref(&res); + return NULL; + } + cbor_decref(&chunk_copy); + } return res; } case CBOR_TYPE_STRING: @@ -179,41 +193,97 @@ cbor_item_t *cbor_copy(cbor_item_t *item) { cbor_string_length(item)); } else { cbor_item_t *res = cbor_new_indefinite_string(); - for (size_t i = 0; i < cbor_string_chunk_count(item); i++) - cbor_string_add_chunk( - res, cbor_move(cbor_copy(cbor_string_chunks_handle(item)[i]))); + if (res == NULL) { + return NULL; + } + + for (size_t i = 0; i < cbor_string_chunk_count(item); i++) { + cbor_item_t *chunk_copy = + cbor_copy(cbor_string_chunks_handle(item)[i]); + if (chunk_copy == NULL) { + cbor_decref(&res); + return NULL; + } + if (!cbor_string_add_chunk(res, chunk_copy)) { + cbor_decref(&chunk_copy); + cbor_decref(&res); + return NULL; + } + cbor_decref(&chunk_copy); + } return res; } case CBOR_TYPE_ARRAY: { cbor_item_t *res; - if (cbor_array_is_definite(item)) + if (cbor_array_is_definite(item)) { res = cbor_new_definite_array(cbor_array_size(item)); - else + } else { res = cbor_new_indefinite_array(); + } + if (res == NULL) { + return NULL; + } - for (size_t i = 0; i < cbor_array_size(item); i++) - cbor_array_push( - res, cbor_move(cbor_copy(cbor_move(cbor_array_get(item, i))))); + for (size_t i = 0; i < cbor_array_size(item); i++) { + cbor_item_t *entry_copy = cbor_copy(cbor_move(cbor_array_get(item, i))); + if (entry_copy == NULL) { + cbor_decref(&res); + return NULL; + } + if (!cbor_array_push(res, entry_copy)) { + cbor_decref(&entry_copy); + cbor_decref(&res); + return NULL; + } + cbor_decref(&entry_copy); + } return res; } case CBOR_TYPE_MAP: { cbor_item_t *res; - if (cbor_map_is_definite(item)) + if (cbor_map_is_definite(item)) { res = cbor_new_definite_map(cbor_map_size(item)); - else + } else { res = cbor_new_indefinite_map(); + } + if (res == NULL) { + return NULL; + } struct cbor_pair *it = cbor_map_handle(item); - for (size_t i = 0; i < cbor_map_size(item); i++) - cbor_map_add(res, (struct cbor_pair){ - .key = cbor_move(cbor_copy(it[i].key)), - .value = cbor_move(cbor_copy(it[i].value))}); + for (size_t i = 0; i < cbor_map_size(item); i++) { + cbor_item_t *key_copy = cbor_copy(it[i].key); + if (key_copy == NULL) { + cbor_decref(&res); + return NULL; + } + cbor_item_t *value_copy = cbor_copy(it[i].value); + if (value_copy == NULL) { + cbor_decref(&res); + cbor_decref(&key_copy); + return NULL; + } + if (!cbor_map_add(res, (struct cbor_pair){.key = key_copy, + .value = value_copy})) { + cbor_decref(&res); + cbor_decref(&key_copy); + cbor_decref(&value_copy); + return NULL; + } + cbor_decref(&key_copy); + cbor_decref(&value_copy); + } return res; } - case CBOR_TYPE_TAG: - return cbor_build_tag( - cbor_tag_value(item), - cbor_move(cbor_copy(cbor_move(cbor_tag_item(item))))); + case CBOR_TYPE_TAG: { + cbor_item_t *item_copy = cbor_copy(cbor_move(cbor_tag_item(item))); + if (item_copy == NULL) { + return NULL; + } + cbor_item_t *tag = cbor_build_tag(cbor_tag_value(item), item_copy); + cbor_decref(&item_copy); + return tag; + } case CBOR_TYPE_FLOAT_CTRL: return _cbor_copy_float_ctrl(item); } diff --git a/src/cbor.h b/src/cbor.h index 015f1e6c..f3860107 100644 --- a/src/cbor.h +++ b/src/cbor.h @@ -46,12 +46,12 @@ extern "C" { _CBOR_NODISCARD CBOR_EXPORT cbor_item_t* cbor_load( cbor_data source, size_t source_size, struct cbor_load_result* result); -/** Deep copy of an item +/** Take a deep copy of an item * * All the reference counts in the new structure are set to one. * * @param item[borrow] item to copy - * @return **new** CBOR deep copy + * @return **new** CBOR deep copy or `NULL` on failure. */ _CBOR_NODISCARD CBOR_EXPORT cbor_item_t* cbor_copy(cbor_item_t* item); diff --git a/src/cbor/tags.c b/src/cbor/tags.c index 60b3e69e..67286117 100644 --- a/src/cbor/tags.c +++ b/src/cbor/tags.c @@ -38,6 +38,9 @@ void cbor_tag_set_item(cbor_item_t *item, cbor_item_t *tagged_item) { cbor_item_t *cbor_build_tag(uint64_t value, cbor_item_t *item) { cbor_item_t *res = cbor_new_tag(value); + if (res == NULL) { + return NULL; + } cbor_tag_set_item(res, item); return res; } diff --git a/test/copy_test.c b/test/copy_test.c index 99641f27..8261a9f6 100644 --- a/test/copy_test.c +++ b/test/copy_test.c @@ -13,6 +13,7 @@ #include "assertions.h" #include "cbor.h" +#include "test_allocator.h" cbor_item_t *item, *copy, *tmp; @@ -70,8 +71,8 @@ static void test_def_bytestring(void **_CBOR_UNUSED(_state)) { static void test_indef_bytestring(void **_CBOR_UNUSED(_state)) { item = cbor_new_indefinite_bytestring(); - cbor_bytestring_add_chunk( - item, cbor_move(cbor_build_bytestring((cbor_data) "abc", 3))); + assert_true(cbor_bytestring_add_chunk( + item, cbor_move(cbor_build_bytestring((cbor_data) "abc", 3)))); copy = cbor_copy(item); assert_int_equal(cbor_bytestring_chunk_count(item), @@ -93,7 +94,7 @@ static void test_def_string(void **_CBOR_UNUSED(_state)) { static void test_indef_string(void **_CBOR_UNUSED(_state)) { item = cbor_new_indefinite_string(); - cbor_string_add_chunk(item, cbor_move(cbor_build_string("abc"))); + assert_true(cbor_string_add_chunk(item, cbor_move(cbor_build_string("abc")))); copy = cbor_copy(item); assert_int_equal(cbor_string_chunk_count(item), @@ -107,7 +108,7 @@ static void test_indef_string(void **_CBOR_UNUSED(_state)) { static void test_def_array(void **_CBOR_UNUSED(_state)) { item = cbor_new_definite_array(1); - cbor_array_push(item, cbor_move(cbor_build_uint8(42))); + assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(42)))); assert_uint8(tmp = cbor_array_get(copy = cbor_copy(item), 0), 42); cbor_decref(&item); @@ -117,7 +118,7 @@ static void test_def_array(void **_CBOR_UNUSED(_state)) { static void test_indef_array(void **_CBOR_UNUSED(_state)) { item = cbor_new_indefinite_array(); - cbor_array_push(item, cbor_move(cbor_build_uint8(42))); + assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(42)))); assert_uint8(tmp = cbor_array_get(copy = cbor_copy(item), 0), 42); cbor_decref(&item); @@ -127,10 +128,10 @@ static void test_indef_array(void **_CBOR_UNUSED(_state)) { static void test_def_map(void **_CBOR_UNUSED(_state)) { item = cbor_new_definite_map(1); - cbor_map_add(item, (struct cbor_pair){ - .key = cbor_move(cbor_build_uint8(42)), - .value = cbor_move(cbor_build_uint8(43)), - }); + assert_true(cbor_map_add(item, (struct cbor_pair){ + .key = cbor_move(cbor_build_uint8(42)), + .value = cbor_move(cbor_build_uint8(43)), + })); assert_uint8(cbor_map_handle(copy = cbor_copy(item))[0].key, 42); @@ -140,10 +141,10 @@ static void test_def_map(void **_CBOR_UNUSED(_state)) { static void test_indef_map(void **_CBOR_UNUSED(_state)) { item = cbor_new_indefinite_map(); - cbor_map_add(item, (struct cbor_pair){ - .key = cbor_move(cbor_build_uint8(42)), - .value = cbor_move(cbor_build_uint8(43)), - }); + assert_true(cbor_map_add(item, (struct cbor_pair){ + .key = cbor_move(cbor_build_uint8(42)), + .value = cbor_move(cbor_build_uint8(43)), + })); assert_uint8(cbor_map_handle(copy = cbor_copy(item))[0].key, 42); @@ -187,19 +188,294 @@ static void test_floats(void **_CBOR_UNUSED(_state)) { cbor_decref(©); } +static void test_alloc_failure_simple(void **_CBOR_UNUSED(_state)) { + item = cbor_build_uint8(10); + + WITH_FAILING_MALLOC({ assert_null(cbor_copy(item)); }); + assert_int_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_bytestring_alloc_failure(void **_CBOR_UNUSED(_state)) { + item = cbor_new_indefinite_bytestring(); + assert_true(cbor_bytestring_add_chunk( + item, cbor_move(cbor_build_bytestring((cbor_data) "abc", 3)))); + + WITH_FAILING_MALLOC({ assert_null(cbor_copy(item)); }); + assert_int_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_bytestring_chunk_alloc_failure(void **_CBOR_UNUSED(_state)) { + item = cbor_new_indefinite_bytestring(); + assert_true(cbor_bytestring_add_chunk( + item, cbor_move(cbor_build_bytestring((cbor_data) "abc", 3)))); + + WITH_MOCK_MALLOC({ assert_null(cbor_copy(item)); }, 2, MALLOC, MALLOC_FAIL); + assert_int_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_bytestring_chunk_append_failure(void **_CBOR_UNUSED(_state)) { + item = cbor_new_indefinite_bytestring(); + assert_true(cbor_bytestring_add_chunk( + item, cbor_move(cbor_build_bytestring((cbor_data) "abc", 3)))); + + WITH_MOCK_MALLOC({ assert_null(cbor_copy(item)); }, 5, + // New indef string, cbor_indefinite_string_data, chunk item, + // chunk data, extend cbor_indefinite_string_data.chunks + MALLOC, MALLOC, MALLOC, MALLOC, REALLOC_FAIL); + assert_int_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_bytestring_second_chunk_alloc_failure( + void **_CBOR_UNUSED(_state)) { + item = cbor_new_indefinite_bytestring(); + assert_true(cbor_bytestring_add_chunk( + item, cbor_move(cbor_build_bytestring((cbor_data) "abc", 3)))); + assert_true(cbor_bytestring_add_chunk( + item, cbor_move(cbor_build_bytestring((cbor_data) "def", 3)))); + + WITH_MOCK_MALLOC({ assert_null(cbor_copy(item)); }, 6, + // New indef string, cbor_indefinite_string_data, chunk item, + // chunk data, extend cbor_indefinite_string_data.chunks, + // second chunk item + MALLOC, MALLOC, MALLOC, MALLOC, REALLOC, MALLOC_FAIL); + assert_int_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_string_alloc_failure(void **_CBOR_UNUSED(_state)) { + item = cbor_new_indefinite_string(); + assert_true(cbor_string_add_chunk(item, cbor_move(cbor_build_string("abc")))); + + WITH_FAILING_MALLOC({ assert_null(cbor_copy(item)); }); + assert_int_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_string_chunk_alloc_failure(void **_CBOR_UNUSED(_state)) { + item = cbor_new_indefinite_string(); + assert_true(cbor_string_add_chunk(item, cbor_move(cbor_build_string("abc")))); + + WITH_MOCK_MALLOC({ assert_null(cbor_copy(item)); }, 2, MALLOC, MALLOC_FAIL); + assert_int_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_string_chunk_append_failure(void **_CBOR_UNUSED(_state)) { + item = cbor_new_indefinite_string(); + assert_true(cbor_string_add_chunk(item, cbor_move(cbor_build_string("abc")))); + + WITH_MOCK_MALLOC({ assert_null(cbor_copy(item)); }, 5, + // New indef string, cbor_indefinite_string_data, chunk item, + // chunk data, extend cbor_indefinite_string_data.chunks + MALLOC, MALLOC, MALLOC, MALLOC, REALLOC_FAIL); + assert_int_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_string_second_chunk_alloc_failure( + void **_CBOR_UNUSED(_state)) { + item = cbor_new_indefinite_string(); + assert_true(cbor_string_add_chunk(item, cbor_move(cbor_build_string("abc")))); + assert_true(cbor_string_add_chunk(item, cbor_move(cbor_build_string("def")))); + + WITH_MOCK_MALLOC({ assert_null(cbor_copy(item)); }, 6, + // New indef string, cbor_indefinite_string_data, chunk item, + // chunk data, extend cbor_indefinite_string_data.chunks, + // second chunk item + MALLOC, MALLOC, MALLOC, MALLOC, REALLOC, MALLOC_FAIL); + assert_int_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_array_alloc_failure(void **_CBOR_UNUSED(_state)) { + item = cbor_new_indefinite_array(); + assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(42)))); + + WITH_FAILING_MALLOC({ assert_null(cbor_copy(item)); }); + assert_int_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_array_item_alloc_failure(void **_CBOR_UNUSED(_state)) { + item = cbor_new_indefinite_array(); + assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(42)))); + + WITH_MOCK_MALLOC({ assert_null(cbor_copy(item)); }, 2, + // New array, item copy + MALLOC, MALLOC_FAIL); + + assert_int_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_array_push_failure(void **_CBOR_UNUSED(_state)) { + item = cbor_new_indefinite_array(); + assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(42)))); + + WITH_MOCK_MALLOC({ assert_null(cbor_copy(item)); }, 3, + // New array, item copy, array reallocation + MALLOC, MALLOC, REALLOC_FAIL); + + assert_int_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_array_second_item_alloc_failure(void **_CBOR_UNUSED(_state)) { + item = cbor_new_indefinite_array(); + assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(42)))); + assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(43)))); + + WITH_MOCK_MALLOC({ assert_null(cbor_copy(item)); }, 4, + // New array, item copy, array reallocation, second item copy + MALLOC, MALLOC, REALLOC, MALLOC_FAIL); + + assert_int_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_map_alloc_failure(void **_CBOR_UNUSED(_state)) { + item = cbor_new_indefinite_map(); + assert_true( + cbor_map_add(item, (struct cbor_pair){cbor_move(cbor_build_uint8(42)), + cbor_move(cbor_build_bool(true))})); + + WITH_FAILING_MALLOC({ assert_null(cbor_copy(item)); }); + assert_int_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_map_key_alloc_failure(void **_CBOR_UNUSED(_state)) { + item = cbor_new_indefinite_map(); + assert_true( + cbor_map_add(item, (struct cbor_pair){cbor_move(cbor_build_uint8(42)), + cbor_move(cbor_build_bool(true))})); + + WITH_MOCK_MALLOC({ assert_null(cbor_copy(item)); }, 2, + // New map, key copy + MALLOC, MALLOC_FAIL); + assert_int_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_map_value_alloc_failure(void **_CBOR_UNUSED(_state)) { + item = cbor_new_indefinite_map(); + assert_true( + cbor_map_add(item, (struct cbor_pair){cbor_move(cbor_build_uint8(42)), + cbor_move(cbor_build_bool(true))})); + + WITH_MOCK_MALLOC({ assert_null(cbor_copy(item)); }, 3, + // New map, key copy, value copy + MALLOC, MALLOC, MALLOC_FAIL); + assert_int_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_map_add_failure(void **_CBOR_UNUSED(_state)) { + item = cbor_new_indefinite_map(); + assert_true( + cbor_map_add(item, (struct cbor_pair){cbor_move(cbor_build_uint8(42)), + cbor_move(cbor_build_bool(true))})); + + WITH_MOCK_MALLOC({ assert_null(cbor_copy(item)); }, 4, + // New map, key copy, value copy, add + MALLOC, MALLOC, MALLOC, REALLOC_FAIL); + assert_int_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_map_second_key_failure(void **_CBOR_UNUSED(_state)) { + item = cbor_new_indefinite_map(); + assert_true( + cbor_map_add(item, (struct cbor_pair){cbor_move(cbor_build_uint8(42)), + cbor_move(cbor_build_bool(true))})); + assert_true(cbor_map_add( + item, (struct cbor_pair){cbor_move(cbor_build_uint8(43)), + cbor_move(cbor_build_bool(false))})); + + WITH_MOCK_MALLOC({ assert_null(cbor_copy(item)); }, 5, + // New map, key copy, value copy, add, second key copy + MALLOC, MALLOC, MALLOC, REALLOC, MALLOC_FAIL); + assert_int_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_tag_item_alloc_failure(void **_CBOR_UNUSED(_state)) { + item = cbor_build_tag(1, cbor_move(cbor_build_uint8(42))); + + WITH_FAILING_MALLOC({ assert_null(cbor_copy(item)); }); + assert_int_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_tag_alloc_failure(void **_CBOR_UNUSED(_state)) { + item = cbor_build_tag(1, cbor_move(cbor_build_uint8(42))); + + WITH_MOCK_MALLOC({ assert_null(cbor_copy(item)); }, 2, + // Item copy, tag + MALLOC, MALLOC_FAIL); + assert_int_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + int main(void) { - const struct CMUnitTest tests[] = {cmocka_unit_test(test_uints), - cmocka_unit_test(test_negints), - cmocka_unit_test(test_def_bytestring), - cmocka_unit_test(test_indef_bytestring), - cmocka_unit_test(test_def_string), - cmocka_unit_test(test_indef_string), - cmocka_unit_test(test_def_array), - cmocka_unit_test(test_indef_array), - cmocka_unit_test(test_def_map), - cmocka_unit_test(test_indef_map), - cmocka_unit_test(test_tag), - cmocka_unit_test(test_ctrls), - cmocka_unit_test(test_floats)}; + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_uints), + cmocka_unit_test(test_negints), + cmocka_unit_test(test_def_bytestring), + cmocka_unit_test(test_indef_bytestring), + cmocka_unit_test(test_def_string), + cmocka_unit_test(test_indef_string), + cmocka_unit_test(test_def_array), + cmocka_unit_test(test_indef_array), + cmocka_unit_test(test_def_map), + cmocka_unit_test(test_indef_map), + cmocka_unit_test(test_tag), + cmocka_unit_test(test_ctrls), + cmocka_unit_test(test_floats), + cmocka_unit_test(test_alloc_failure_simple), + cmocka_unit_test(test_bytestring_alloc_failure), + cmocka_unit_test(test_bytestring_chunk_alloc_failure), + cmocka_unit_test(test_bytestring_chunk_append_failure), + cmocka_unit_test(test_bytestring_second_chunk_alloc_failure), + cmocka_unit_test(test_string_alloc_failure), + cmocka_unit_test(test_string_chunk_alloc_failure), + cmocka_unit_test(test_string_chunk_append_failure), + cmocka_unit_test(test_string_second_chunk_alloc_failure), + cmocka_unit_test(test_array_alloc_failure), + cmocka_unit_test(test_array_item_alloc_failure), + cmocka_unit_test(test_array_push_failure), + cmocka_unit_test(test_array_second_item_alloc_failure), + cmocka_unit_test(test_map_alloc_failure), + cmocka_unit_test(test_map_key_alloc_failure), + cmocka_unit_test(test_map_value_alloc_failure), + cmocka_unit_test(test_map_add_failure), + cmocka_unit_test(test_map_second_key_failure), + cmocka_unit_test(test_tag_item_alloc_failure), + cmocka_unit_test(test_tag_alloc_failure), + }; return cmocka_run_group_tests(tests, NULL, NULL); } diff --git a/test/tag_test.c b/test/tag_test.c index aa2f469f..3c825190 100644 --- a/test/tag_test.c +++ b/test/tag_test.c @@ -13,6 +13,7 @@ #include "assertions.h" #include "cbor.h" +#include "test_allocator.h" cbor_item_t *tag; struct cbor_load_result res; @@ -107,11 +108,36 @@ static void test_nested_tag(void **_CBOR_UNUSED(_state)) { assert_null(nested_tag); } +static void test_build_tag(void **_CBOR_UNUSED(_state)) { + tag = cbor_build_tag(1, cbor_move(cbor_build_uint8(42))); + + assert_true(cbor_typeof(tag) == CBOR_TYPE_TAG); + assert_int_equal(cbor_tag_value(tag), 1); + assert_uint8(cbor_move(cbor_tag_item(tag)), 42); + + cbor_decref(&tag); +} + +static void test_build_tag_failure(void **_CBOR_UNUSED(_state)) { + cbor_item_t *tagged_item = cbor_build_uint8(42); + + WITH_FAILING_MALLOC({ assert_null(cbor_build_tag(1, tagged_item)); }); + assert_int_equal(cbor_refcount(tagged_item), 1); + + cbor_decref(&tagged_item); +} + int main(void) { const struct CMUnitTest tests[] = { - cmocka_unit_test(test_refcounting), cmocka_unit_test(test_embedded_tag), - cmocka_unit_test(test_int8_tag), cmocka_unit_test(test_int16_tag), - cmocka_unit_test(test_int32_tag), cmocka_unit_test(test_int64_tag), - cmocka_unit_test(test_nested_tag)}; + cmocka_unit_test(test_refcounting), + cmocka_unit_test(test_embedded_tag), + cmocka_unit_test(test_int8_tag), + cmocka_unit_test(test_int16_tag), + cmocka_unit_test(test_int32_tag), + cmocka_unit_test(test_int64_tag), + cmocka_unit_test(test_nested_tag), + cmocka_unit_test(test_build_tag), + cmocka_unit_test(test_build_tag_failure), + }; return cmocka_run_group_tests(tests, NULL, NULL); } diff --git a/test/test_allocator.c b/test/test_allocator.c index 0b7e4f0f..45d727dd 100644 --- a/test/test_allocator.c +++ b/test/test_allocator.c @@ -1,5 +1,7 @@ #include "test_allocator.h" +#include + // How many alloc calls we expect int alloc_calls_expected; // How many alloc calls we got @@ -25,6 +27,17 @@ void finalize_mock_malloc(void) { free(expectations); } +void print_backtrace() { + void *buffer[128]; + int frames = backtrace(buffer, 128); + char **symbols = backtrace_symbols(buffer, frames); + // Skip this function and the caller + for (int i = 2; i < frames; ++i) { + printf("%s\n", symbols[i]); + } + free(symbols); +} + void *instrumented_malloc(size_t size) { if (alloc_calls >= alloc_calls_expected) { goto error; @@ -43,6 +56,7 @@ void *instrumented_malloc(size_t size) { "Unexpected call to malloc(%zu) at position %d of %d; expected %d\n", size, alloc_calls, alloc_calls_expected, alloc_calls < alloc_calls_expected ? expectations[alloc_calls] : -1); + print_backtrace(); fail(); return NULL; } @@ -65,6 +79,7 @@ void *instrumented_realloc(void *ptr, size_t size) { "Unexpected call to realloc(%zu) at position %d of %d; expected %d\n", size, alloc_calls, alloc_calls_expected, alloc_calls < alloc_calls_expected ? expectations[alloc_calls] : -1); + print_backtrace(); fail(); return NULL; }