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
set_mock_malloc(int calls,...)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
finalize_mock_malloc()49 void finalize_mock_malloc() {
50 assert_int_equal(alloc_calls, alloc_calls_expected);
51 free(expectations);
52 }
53
instrumented_malloc(size_t size)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
instrumented_realloc(void * ptr,size_t size)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
test_int_creation(void ** state)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
test_bytestring_creation(void ** state)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
test_string_creation(void ** state)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
test_array_creation(void ** state)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
test_map_creation(void ** state)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
test_tag_creation(void ** state)164 static void test_tag_creation(void **state) {
165 WITH_FAILING_MALLOC({ assert_null(cbor_new_tag(42)); });
166 }
167
test_float_ctrl_creation(void ** state)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
test_bytestring_add_chunk(void ** state)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
test_string_add_chunk(void ** state)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
test_array_push(void ** state)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
test_map_add(void ** state)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
main(void)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