1 /* 2 * Copyright (c) 2014-2019 Pavel Kalvoda <me@pavelkalvoda.com> 3 * 4 * libcbor is free software; you can redistribute it and/or modify 5 * it under the terms of the MIT license. See LICENSE for details. 6 */ 7 8 #include <setjmp.h> 9 #include <stdarg.h> 10 #include <stddef.h> 11 12 #include <cmocka.h> 13 14 #include "cbor.h" 15 16 // This test simulates cases when malloc unexpectedly fails and leaves a 17 // possibly partially constructed object behind. It this is especially useful 18 // in conjunction with the memory correctness check. 19 // 20 // WARNING: The test only works with CBOR_CUSTOM_ALLOC 21 22 typedef enum call_expectation { 23 MALLOC, 24 MALLOC_FAIL, 25 REALLOC, 26 REALLOC_FAIL 27 } call_expectation; 28 29 // How many alloc calls we expect 30 int alloc_calls_expected; 31 // How many alloc calls we got 32 int alloc_calls; 33 // Array of booleans indicating whether to return a block or fail with NULL 34 call_expectation *expectations; 35 36 void set_mock_malloc(int calls, ...) { 37 va_list args; 38 va_start(args, calls); 39 alloc_calls_expected = calls; 40 alloc_calls = 0; 41 expectations = calloc(calls, sizeof(expectations)); 42 for (int i = 0; i < calls; i++) { 43 // Promotable types, baby 44 expectations[i] = va_arg(args, call_expectation); 45 } 46 va_end(args); 47 } 48 49 void finalize_mock_malloc() { 50 assert_int_equal(alloc_calls, alloc_calls_expected); 51 free(expectations); 52 } 53 54 void *instrumented_malloc(size_t size) { 55 if (alloc_calls >= alloc_calls_expected) { 56 goto error; 57 } 58 59 if (expectations[alloc_calls] == MALLOC) { 60 alloc_calls++; 61 return malloc(size); 62 } else if (expectations[alloc_calls] == MALLOC_FAIL) { 63 alloc_calls++; 64 return NULL; 65 } 66 67 error: 68 print_error("Unexpected call to malloc"); 69 fail(); 70 return NULL; 71 } 72 73 void *instrumented_realloc(void *ptr, size_t size) { 74 if (alloc_calls >= alloc_calls_expected) { 75 goto error; 76 } 77 78 if (expectations[alloc_calls] == REALLOC) { 79 alloc_calls++; 80 return realloc(ptr, size); 81 } else if (expectations[alloc_calls] == REALLOC_FAIL) { 82 alloc_calls++; 83 return NULL; 84 } 85 86 error: 87 print_error("Unexpected call to realloc"); 88 fail(); 89 return NULL; 90 } 91 92 #define WITH_MOCK_MALLOC(block, malloc_calls, ...) \ 93 do { \ 94 set_mock_malloc(malloc_calls, __VA_ARGS__); \ 95 block; \ 96 finalize_mock_malloc(); \ 97 } while (0) 98 99 #define WITH_FAILING_MALLOC(block) WITH_MOCK_MALLOC(block, 1, MALLOC_FAIL) 100 101 static void test_int_creation(void **state) { 102 WITH_FAILING_MALLOC({ assert_null(cbor_new_int8()); }); 103 WITH_FAILING_MALLOC({ assert_null(cbor_new_int16()); }); 104 WITH_FAILING_MALLOC({ assert_null(cbor_new_int32()); }); 105 WITH_FAILING_MALLOC({ assert_null(cbor_new_int64()); }); 106 107 WITH_FAILING_MALLOC({ assert_null(cbor_build_uint8(0xFF)); }); 108 WITH_FAILING_MALLOC({ assert_null(cbor_build_uint16(0xFF)); }); 109 WITH_FAILING_MALLOC({ assert_null(cbor_build_uint32(0xFF)); }); 110 WITH_FAILING_MALLOC({ assert_null(cbor_build_uint64(0xFF)); }); 111 112 WITH_FAILING_MALLOC({ assert_null(cbor_build_negint8(0xFF)); }); 113 WITH_FAILING_MALLOC({ assert_null(cbor_build_negint16(0xFF)); }); 114 WITH_FAILING_MALLOC({ assert_null(cbor_build_negint32(0xFF)); }); 115 WITH_FAILING_MALLOC({ assert_null(cbor_build_negint64(0xFF)); }); 116 } 117 118 static void test_bytestring_creation(void **state) { 119 WITH_FAILING_MALLOC({ assert_null(cbor_new_definite_bytestring()); }); 120 121 WITH_FAILING_MALLOC({ assert_null(cbor_new_indefinite_bytestring()); }); 122 WITH_MOCK_MALLOC({ assert_null(cbor_new_indefinite_bytestring()); }, 2, 123 MALLOC, MALLOC_FAIL); 124 125 unsigned char bytes[] = {0, 0, 0xFF, 0xAB}; 126 127 WITH_FAILING_MALLOC({ assert_null(cbor_build_bytestring(bytes, 4)); }); 128 WITH_MOCK_MALLOC({ assert_null(cbor_build_bytestring(bytes, 4)); }, 2, MALLOC, 129 MALLOC_FAIL); 130 } 131 132 static void test_string_creation(void **state) { 133 WITH_FAILING_MALLOC({ assert_null(cbor_new_definite_string()); }); 134 135 WITH_FAILING_MALLOC({ assert_null(cbor_new_indefinite_string()); }); 136 WITH_MOCK_MALLOC({ assert_null(cbor_new_indefinite_string()); }, 2, MALLOC, 137 MALLOC_FAIL); 138 139 WITH_FAILING_MALLOC({ assert_null(cbor_build_string("Test")); }); 140 WITH_MOCK_MALLOC({ assert_null(cbor_build_string("Test")); }, 2, MALLOC, 141 MALLOC_FAIL); 142 143 WITH_FAILING_MALLOC({ assert_null(cbor_build_stringn("Test", 4)); }); 144 WITH_MOCK_MALLOC({ assert_null(cbor_build_stringn("Test", 4)); }, 2, MALLOC, 145 MALLOC_FAIL); 146 } 147 148 static void test_array_creation(void **state) { 149 WITH_FAILING_MALLOC({ assert_null(cbor_new_definite_array(42)); }); 150 WITH_MOCK_MALLOC({ assert_null(cbor_new_definite_array(42)); }, 2, MALLOC, 151 MALLOC_FAIL); 152 153 WITH_FAILING_MALLOC({ assert_null(cbor_new_indefinite_array()); }); 154 } 155 156 static void test_map_creation(void **state) { 157 WITH_FAILING_MALLOC({ assert_null(cbor_new_definite_map(42)); }); 158 WITH_MOCK_MALLOC({ assert_null(cbor_new_definite_map(42)); }, 2, MALLOC, 159 MALLOC_FAIL); 160 161 WITH_FAILING_MALLOC({ assert_null(cbor_new_indefinite_map()); }); 162 } 163 164 static void test_tag_creation(void **state) { 165 WITH_FAILING_MALLOC({ assert_null(cbor_new_tag(42)); }); 166 } 167 168 static void test_float_ctrl_creation(void **state) { 169 WITH_FAILING_MALLOC({ assert_null(cbor_new_ctrl()); }); 170 WITH_FAILING_MALLOC({ assert_null(cbor_new_float2()); }); 171 WITH_FAILING_MALLOC({ assert_null(cbor_new_float4()); }); 172 WITH_FAILING_MALLOC({ assert_null(cbor_new_float8()); }); 173 WITH_FAILING_MALLOC({ assert_null(cbor_new_null()); }); 174 WITH_FAILING_MALLOC({ assert_null(cbor_new_undef()); }); 175 176 WITH_FAILING_MALLOC({ assert_null(cbor_build_bool(false)); }); 177 WITH_FAILING_MALLOC({ assert_null(cbor_build_float2(3.14)); }); 178 WITH_FAILING_MALLOC({ assert_null(cbor_build_float4(3.14)); }); 179 WITH_FAILING_MALLOC({ assert_null(cbor_build_float8(3.14)); }); 180 WITH_FAILING_MALLOC({ assert_null(cbor_build_ctrl(0xAF)); }); 181 } 182 183 static void test_bytestring_add_chunk(void **state) { 184 unsigned char bytes[] = {0, 0, 0xFF, 0xAB}; 185 WITH_MOCK_MALLOC( 186 { 187 cbor_item_t *bytestring = cbor_new_indefinite_bytestring(); 188 cbor_item_t *chunk = cbor_build_bytestring(bytes, 4); 189 190 assert_false(cbor_bytestring_add_chunk(bytestring, chunk)); 191 assert_int_equal(cbor_bytestring_chunk_count(bytestring), 0); 192 assert_int_equal( 193 ((struct cbor_indefinite_string_data *)bytestring->data) 194 ->chunk_capacity, 195 0); 196 197 cbor_decref(&chunk); 198 cbor_decref(&bytestring); 199 }, 200 5, MALLOC, MALLOC, MALLOC, MALLOC, REALLOC_FAIL); 201 } 202 203 static void test_string_add_chunk(void **state) { 204 WITH_MOCK_MALLOC( 205 { 206 cbor_item_t *string = cbor_new_indefinite_string(); 207 cbor_item_t *chunk = cbor_build_string("Hello!"); 208 209 assert_false(cbor_string_add_chunk(string, chunk)); 210 assert_int_equal(cbor_string_chunk_count(string), 0); 211 assert_int_equal(((struct cbor_indefinite_string_data *)string->data) 212 ->chunk_capacity, 213 0); 214 215 cbor_decref(&chunk); 216 cbor_decref(&string); 217 }, 218 5, MALLOC, MALLOC, MALLOC, MALLOC, REALLOC_FAIL); 219 } 220 221 static void test_array_push(void **state) { 222 WITH_MOCK_MALLOC( 223 { 224 cbor_item_t *array = cbor_new_indefinite_array(); 225 cbor_item_t *string = cbor_build_string("Hello!"); 226 227 assert_false(cbor_array_push(array, string)); 228 assert_int_equal(cbor_array_allocated(array), 0); 229 assert_null(array->data); 230 assert_int_equal(array->metadata.array_metadata.end_ptr, 0); 231 232 cbor_decref(&string); 233 cbor_decref(&array); 234 }, 235 4, MALLOC, MALLOC, MALLOC, REALLOC_FAIL); 236 } 237 238 static void test_map_add(void **state) { 239 WITH_MOCK_MALLOC( 240 { 241 cbor_item_t *map = cbor_new_indefinite_map(); 242 cbor_item_t *key = cbor_build_uint8(0); 243 cbor_item_t *value = cbor_build_bool(true); 244 245 assert_false( 246 cbor_map_add(map, (struct cbor_pair){.key = key, .value = value})); 247 assert_int_equal(cbor_map_allocated(map), 0); 248 assert_null(map->data); 249 250 cbor_decref(&map); 251 cbor_decref(&key); 252 cbor_decref(&value); 253 }, 254 4, MALLOC, MALLOC, MALLOC, REALLOC_FAIL); 255 } 256 257 int main(void) { 258 #if CBOR_CUSTOM_ALLOC 259 cbor_set_allocs(instrumented_malloc, instrumented_realloc, free); 260 261 // TODO: string chunks realloc test 262 const struct CMUnitTest tests[] = { 263 cmocka_unit_test(test_int_creation), 264 cmocka_unit_test(test_bytestring_creation), 265 cmocka_unit_test(test_string_creation), 266 cmocka_unit_test(test_array_creation), 267 cmocka_unit_test(test_map_creation), 268 cmocka_unit_test(test_tag_creation), 269 cmocka_unit_test(test_float_ctrl_creation), 270 271 cmocka_unit_test(test_bytestring_add_chunk), 272 cmocka_unit_test(test_string_add_chunk), 273 cmocka_unit_test(test_array_push), 274 cmocka_unit_test(test_map_add), 275 }; 276 #else 277 // Can't do anything without a custom allocator 278 const struct CMUnitTest tests[] = {}; 279 #endif 280 281 return cmocka_run_group_tests(tests, NULL, NULL); 282 } 283