xref: /netbsd-src/external/mit/libcbor/dist/test/memory_allocation_test.c (revision 5dd36a3bc8bf2a9dec29ceb6349550414570c447)
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