test_multi_heap.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. #include "catch.hpp"
  2. #include "multi_heap.h"
  3. #include "../multi_heap_config.h"
  4. #include "../tlsf/tlsf.h"
  5. #include "../tlsf/tlsf_common.h"
  6. #include "../tlsf/tlsf_block_functions.h"
  7. #include <string.h>
  8. #include <assert.h>
  9. /* The functions __malloc__ and __free__ are used to call the libc
  10. * malloc and free and allocate memory from the host heap. Since the test
  11. * `TEST_CASE("multi_heap many random allocations", "[multi_heap]")`
  12. * calls multi_heap_allocation_impl() with sizes that can go up to 8MB,
  13. * an allocatation on the heap will be prefered rather than the stack which
  14. * might not have the necessary memory.
  15. */
  16. static void *__malloc__(size_t bytes)
  17. {
  18. return malloc(bytes);
  19. }
  20. static void __free__(void *ptr)
  21. {
  22. free(ptr);
  23. }
  24. /* Insurance against accidentally using libc heap functions in tests */
  25. #undef free
  26. #define free #error
  27. #undef malloc
  28. #define malloc #error
  29. #undef calloc
  30. #define calloc #error
  31. #undef realloc
  32. #define realloc #error
  33. TEST_CASE("multi_heap simple allocations", "[multi_heap]")
  34. {
  35. uint8_t small_heap[4 * 1024];
  36. multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap));
  37. size_t test_alloc_size = (multi_heap_free_size(heap) + 4) / 2;
  38. printf("New heap:\n");
  39. multi_heap_dump(heap);
  40. printf("*********************\n");
  41. uint8_t *buf = (uint8_t *)multi_heap_malloc(heap, test_alloc_size);
  42. printf("small_heap %p buf %p\n", small_heap, buf);
  43. REQUIRE( buf != NULL );
  44. REQUIRE((intptr_t)buf >= (intptr_t)small_heap);
  45. REQUIRE( (intptr_t)buf < (intptr_t)(small_heap + sizeof(small_heap)));
  46. REQUIRE( multi_heap_get_allocated_size(heap, buf) >= test_alloc_size );
  47. REQUIRE( multi_heap_get_allocated_size(heap, buf) < test_alloc_size + 16);
  48. memset(buf, 0xEE, test_alloc_size);
  49. REQUIRE( multi_heap_malloc(heap, test_alloc_size) == NULL );
  50. multi_heap_free(heap, buf);
  51. printf("Empty?\n");
  52. multi_heap_dump(heap);
  53. printf("*********************\n");
  54. /* Now there should be space for another allocation */
  55. buf = (uint8_t *)multi_heap_malloc(heap, test_alloc_size);
  56. REQUIRE( buf != NULL );
  57. multi_heap_free(heap, buf);
  58. REQUIRE( multi_heap_free_size(heap) > multi_heap_minimum_free_size(heap) );
  59. }
  60. TEST_CASE("multi_heap fragmentation", "[multi_heap]")
  61. {
  62. const size_t HEAP_SIZE = 4 * 1024;
  63. uint8_t small_heap[HEAP_SIZE];
  64. multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap));
  65. const size_t alloc_size = 500;
  66. void *p[4];
  67. for (int i = 0; i < 4; i++) {
  68. multi_heap_dump(heap);
  69. REQUIRE( multi_heap_check(heap, true) );
  70. p[i] = multi_heap_malloc(heap, alloc_size);
  71. printf("%d = %p ****->\n", i, p[i]);
  72. multi_heap_dump(heap);
  73. REQUIRE( p[i] != NULL );
  74. }
  75. printf("allocated %p %p %p %p\n", p[0], p[1], p[2], p[3]);
  76. REQUIRE( multi_heap_malloc(heap, alloc_size * 5) == NULL ); /* no room to allocate 5*alloc_size now */
  77. printf("4 allocations:\n");
  78. multi_heap_dump(heap);
  79. printf("****************\n");
  80. multi_heap_free(heap, p[0]);
  81. multi_heap_free(heap, p[1]);
  82. multi_heap_free(heap, p[3]);
  83. printf("1 allocations:\n");
  84. multi_heap_dump(heap);
  85. printf("****************\n");
  86. void *big = multi_heap_malloc(heap, alloc_size * 3);
  87. //Blocks in TLSF are organized in different form, so this makes no sense
  88. multi_heap_free(heap, big);
  89. multi_heap_free(heap, p[2]);
  90. printf("0 allocations:\n");
  91. multi_heap_dump(heap);
  92. printf("****************\n");
  93. big = multi_heap_malloc(heap, alloc_size * 2);
  94. //Blocks in TLSF are organized in different form, so this makes no sense
  95. multi_heap_free(heap, big);
  96. }
  97. /* Test that malloc/free does not leave free space fragmented */
  98. TEST_CASE("multi_heap defrag", "[multi_heap]")
  99. {
  100. void *p[4];
  101. uint8_t small_heap[4 * 1024];
  102. multi_heap_info_t info, info2;
  103. multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap));
  104. printf("0 ---\n");
  105. multi_heap_dump(heap);
  106. REQUIRE( multi_heap_check(heap, true) );
  107. multi_heap_get_info(heap, &info);
  108. REQUIRE( 0 == info.allocated_blocks );
  109. REQUIRE( 1 == info.free_blocks );
  110. printf("1 ---\n");
  111. p[0] = multi_heap_malloc(heap, 128);
  112. p[1] = multi_heap_malloc(heap, 32);
  113. multi_heap_dump(heap);
  114. REQUIRE( multi_heap_check(heap, true) );
  115. printf("2 ---\n");
  116. multi_heap_free(heap, p[0]);
  117. p[2] = multi_heap_malloc(heap, 64);
  118. multi_heap_dump(heap);
  119. REQUIRE( p[2] == p[0] );
  120. REQUIRE( multi_heap_check(heap, true) );
  121. printf("3 ---\n");
  122. multi_heap_free(heap, p[2]);
  123. p[3] = multi_heap_malloc(heap, 32);
  124. multi_heap_dump(heap);
  125. REQUIRE( p[3] == p[0] );
  126. REQUIRE( multi_heap_check(heap, true) );
  127. multi_heap_get_info(heap, &info2);
  128. REQUIRE( 2 == info2.allocated_blocks );
  129. REQUIRE( 2 == info2.free_blocks );
  130. multi_heap_free(heap, p[0]);
  131. multi_heap_free(heap, p[1]);
  132. multi_heap_get_info(heap, &info2);
  133. REQUIRE( 0 == info2.allocated_blocks );
  134. REQUIRE( 1 == info2.free_blocks );
  135. REQUIRE( info.total_free_bytes == info2.total_free_bytes );
  136. }
  137. /* Test that malloc/free does not leave free space fragmented
  138. Note: With fancy poisoning, realloc is implemented as malloc-copy-free and this test does not apply.
  139. */
  140. #ifndef MULTI_HEAP_POISONING_SLOW
  141. TEST_CASE("multi_heap defrag realloc", "[multi_heap]")
  142. {
  143. void *p[4];
  144. uint8_t small_heap[4 * 1024];
  145. multi_heap_info_t info, info2;
  146. multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap));
  147. printf("0 ---\n");
  148. multi_heap_dump(heap);
  149. REQUIRE( multi_heap_check(heap, true) );
  150. multi_heap_get_info(heap, &info);
  151. REQUIRE( 0 == info.allocated_blocks );
  152. REQUIRE( 1 == info.free_blocks );
  153. printf("1 ---\n");
  154. p[0] = multi_heap_malloc(heap, 128);
  155. p[1] = multi_heap_malloc(heap, 32);
  156. multi_heap_dump(heap);
  157. REQUIRE( multi_heap_check(heap, true) );
  158. printf("2 ---\n");
  159. p[2] = multi_heap_realloc(heap, p[0], 64);
  160. multi_heap_dump(heap);
  161. REQUIRE( p[2] == p[0] );
  162. REQUIRE( multi_heap_check(heap, true) );
  163. printf("3 ---\n");
  164. p[3] = multi_heap_realloc(heap, p[2], 32);
  165. multi_heap_dump(heap);
  166. REQUIRE( p[3] == p[0] );
  167. REQUIRE( multi_heap_check(heap, true) );
  168. multi_heap_get_info(heap, &info2);
  169. REQUIRE( 2 == info2.allocated_blocks );
  170. REQUIRE( 2 == info2.free_blocks );
  171. multi_heap_free(heap, p[0]);
  172. multi_heap_free(heap, p[1]);
  173. multi_heap_get_info(heap, &info2);
  174. REQUIRE( 0 == info2.allocated_blocks );
  175. REQUIRE( 1 == info2.free_blocks );
  176. REQUIRE( info.total_free_bytes == info2.total_free_bytes );
  177. }
  178. #endif
  179. void multi_heap_allocation_impl(int heap_size)
  180. {
  181. uint8_t *big_heap = (uint8_t *) __malloc__(heap_size);
  182. const int NUM_POINTERS = 64;
  183. printf("Running multi-allocation test with heap_size %d...\n", heap_size);
  184. REQUIRE( big_heap );
  185. multi_heap_handle_t heap = multi_heap_register(big_heap, heap_size);
  186. void *p[NUM_POINTERS] = { 0 };
  187. size_t s[NUM_POINTERS] = { 0 };
  188. const size_t initial_free = multi_heap_free_size(heap);
  189. const int ITERATIONS = 5000;
  190. for (int i = 0; i < ITERATIONS; i++) {
  191. /* check all pointers allocated so far are valid inside big_heap */
  192. for (int j = 0; j < NUM_POINTERS; j++) {
  193. if (p[j] != NULL) {
  194. }
  195. }
  196. uint8_t n = rand() % NUM_POINTERS;
  197. if (i % 4 == 0) {
  198. /* 1 in 4 iterations, try to realloc the buffer instead
  199. of using malloc/free
  200. */
  201. size_t new_size = (rand() % 1023) + 1;
  202. void *new_p = multi_heap_realloc(heap, p[n], new_size);
  203. printf("realloc %p -> %p (%zu -> %zu)\n", p[n], new_p, s[n], new_size);
  204. multi_heap_check(heap, true);
  205. if (new_size == 0 || new_p != NULL) {
  206. p[n] = new_p;
  207. s[n] = new_size;
  208. if (new_size > 0) {
  209. REQUIRE( p[n] >= big_heap );
  210. REQUIRE( p[n] < big_heap + heap_size );
  211. memset(p[n], n, new_size);
  212. }
  213. }
  214. continue;
  215. }
  216. if (p[n] != NULL) {
  217. if (s[n] > 0) {
  218. /* Verify pre-existing contents of p[n] */
  219. uint8_t compare[s[n]];
  220. memset(compare, n, s[n]);
  221. /*REQUIRE*/assert( memcmp(compare, p[n], s[n]) == 0 );
  222. }
  223. REQUIRE( multi_heap_check(heap, true) );
  224. multi_heap_free(heap, p[n]);
  225. printf("freed %p (%zu)\n", p[n], s[n]);
  226. if (!multi_heap_check(heap, true)) {
  227. printf("FAILED iteration %d after freeing %p\n", i, p[n]);
  228. multi_heap_dump(heap);
  229. REQUIRE(0);
  230. }
  231. }
  232. s[n] = rand() % 1024;
  233. REQUIRE( multi_heap_check(heap, true) );
  234. p[n] = multi_heap_malloc(heap, s[n]);
  235. printf("malloc %p (%zu)\n", p[n], s[n]);
  236. if (p[n] != NULL) {
  237. REQUIRE( p[n] >= big_heap );
  238. REQUIRE( p[n] < big_heap + heap_size );
  239. }
  240. if (!multi_heap_check(heap, true)) {
  241. printf("FAILED iteration %d after mallocing %p (%zu bytes)\n", i, p[n], s[n]);
  242. multi_heap_dump(heap);
  243. REQUIRE(0);
  244. }
  245. if (p[n] != NULL) {
  246. memset(p[n], n, s[n]);
  247. }
  248. }
  249. for (int i = 0; i < NUM_POINTERS; i++) {
  250. multi_heap_free(heap, p[i]);
  251. if (!multi_heap_check(heap, true)) {
  252. printf("FAILED during cleanup after freeing %p\n", p[i]);
  253. multi_heap_dump(heap);
  254. REQUIRE(0);
  255. }
  256. }
  257. REQUIRE( initial_free == multi_heap_free_size(heap) );
  258. __free__(big_heap);
  259. }
  260. TEST_CASE("multi_heap many random allocations", "[multi_heap]")
  261. {
  262. size_t poolsize[] = { 15, 255, 4095, 8191 };
  263. for (size_t i = 0; i < sizeof(poolsize)/sizeof(size_t); i++) {
  264. multi_heap_allocation_impl(poolsize[i] * 1024);
  265. }
  266. }
  267. TEST_CASE("multi_heap_get_info() function", "[multi_heap]")
  268. {
  269. uint8_t heapdata[4 * 1024];
  270. multi_heap_handle_t heap = multi_heap_register(heapdata, sizeof(heapdata));
  271. multi_heap_info_t before, after, freed;
  272. multi_heap_get_info(heap, &before);
  273. printf("before: total_free_bytes %zu\ntotal_allocated_bytes %zu\nlargest_free_block %zu\nminimum_free_bytes %zu\nallocated_blocks %zu\nfree_blocks %zu\ntotal_blocks %zu\n",
  274. before.total_free_bytes,
  275. before.total_allocated_bytes,
  276. before.largest_free_block,
  277. before.minimum_free_bytes,
  278. before.allocated_blocks,
  279. before.free_blocks,
  280. before.total_blocks);
  281. REQUIRE( 0 == before.allocated_blocks );
  282. REQUIRE( 0 == before.total_allocated_bytes );
  283. REQUIRE( before.total_free_bytes == before.minimum_free_bytes );
  284. void *x = multi_heap_malloc(heap, 32);
  285. multi_heap_get_info(heap, &after);
  286. printf("after: total_free_bytes %zu\ntotal_allocated_bytes %zu\nlargest_free_block %zu\nminimum_free_bytes %zu\nallocated_blocks %zu\nfree_blocks %zu\ntotal_blocks %zu\n",
  287. after.total_free_bytes,
  288. after.total_allocated_bytes,
  289. after.largest_free_block,
  290. after.minimum_free_bytes,
  291. after.allocated_blocks,
  292. after.free_blocks,
  293. after.total_blocks);
  294. REQUIRE( 1 == after.allocated_blocks );
  295. REQUIRE( 32 == after.total_allocated_bytes );
  296. REQUIRE( after.minimum_free_bytes < before.minimum_free_bytes);
  297. REQUIRE( after.minimum_free_bytes > 0 );
  298. multi_heap_free(heap, x);
  299. multi_heap_get_info(heap, &freed);
  300. printf("freed: total_free_bytes %zu\ntotal_allocated_bytes %zu\nlargest_free_block %zu\nminimum_free_bytes %zu\nallocated_blocks %zu\nfree_blocks %zu\ntotal_blocks %zu\n",
  301. freed.total_free_bytes,
  302. freed.total_allocated_bytes,
  303. freed.largest_free_block,
  304. freed.minimum_free_bytes,
  305. freed.allocated_blocks,
  306. freed.free_blocks,
  307. freed.total_blocks);
  308. REQUIRE( 0 == freed.allocated_blocks );
  309. REQUIRE( 0 == freed.total_allocated_bytes );
  310. REQUIRE( before.total_free_bytes == freed.total_free_bytes );
  311. REQUIRE( after.minimum_free_bytes == freed.minimum_free_bytes );
  312. }
  313. TEST_CASE("multi_heap minimum-size allocations", "[multi_heap]")
  314. {
  315. uint8_t heapdata[4096];
  316. void *p[sizeof(heapdata) / sizeof(void *)] = {NULL};
  317. const size_t NUM_P = sizeof(p) / sizeof(void *);
  318. size_t allocated_size = 0;
  319. multi_heap_handle_t heap = multi_heap_register(heapdata, sizeof(heapdata));
  320. size_t before_free = multi_heap_free_size(heap);
  321. size_t i;
  322. for (i = 0; i < NUM_P; i++) {
  323. //TLSF minimum block size is 4 bytes
  324. p[i] = multi_heap_malloc(heap, 1);
  325. if (p[i] == NULL) {
  326. break;
  327. }
  328. }
  329. REQUIRE( i < NUM_P); // Should have run out of heap before we ran out of pointers
  330. printf("Allocated %zu minimum size chunks\n", i);
  331. REQUIRE(multi_heap_free_size(heap) < before_free);
  332. multi_heap_check(heap, true);
  333. /* Free in random order */
  334. bool has_allocations = true;
  335. while (has_allocations) {
  336. i = rand() % NUM_P;
  337. multi_heap_free(heap, p[i]);
  338. p[i] = NULL;
  339. multi_heap_check(heap, true);
  340. has_allocations = false;
  341. for (i = 0; i < NUM_P && !has_allocations; i++) {
  342. has_allocations = (p[i] != NULL);
  343. }
  344. }
  345. /* all freed! */
  346. REQUIRE( before_free == multi_heap_free_size(heap) );
  347. }
  348. TEST_CASE("multi_heap_realloc()", "[multi_heap]")
  349. {
  350. const size_t HEAP_SIZE = 4 * 1024;
  351. const uint32_t PATTERN = 0xABABDADA;
  352. uint8_t small_heap[HEAP_SIZE];
  353. multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap));
  354. uint32_t *a = (uint32_t *)multi_heap_malloc(heap, 64);
  355. uint32_t *b = (uint32_t *)multi_heap_malloc(heap, 32);
  356. REQUIRE( a != NULL );
  357. REQUIRE( b != NULL );
  358. REQUIRE( b > a); /* 'b' takes the block after 'a' */
  359. *a = PATTERN;
  360. uint32_t *c = (uint32_t *)multi_heap_realloc(heap, a, 72);
  361. REQUIRE( multi_heap_check(heap, true));
  362. REQUIRE( c != NULL );
  363. REQUIRE( c > b ); /* 'a' moves, 'c' takes the block after 'b' */
  364. REQUIRE( *c == PATTERN );
  365. #ifndef MULTI_HEAP_POISONING_SLOW
  366. // "Slow" poisoning implementation doesn't reallocate in place, so these
  367. // test will fail...
  368. uint32_t *d = (uint32_t *)multi_heap_realloc(heap, c, 36);
  369. REQUIRE( multi_heap_check(heap, true) );
  370. REQUIRE( c == d ); /* 'c' block should be shrunk in-place */
  371. REQUIRE( *d == PATTERN);
  372. // biggest allocation possible to completely fill the block left free after it was reallocated
  373. uint32_t *e = (uint32_t *)multi_heap_malloc(heap, 60);
  374. REQUIRE( multi_heap_check(heap, true));
  375. REQUIRE( a == e ); /* 'e' takes the block formerly occupied by 'a' */
  376. multi_heap_free(heap, d);
  377. uint32_t *f = (uint32_t *)multi_heap_realloc(heap, b, 64);
  378. REQUIRE( multi_heap_check(heap, true) );
  379. REQUIRE( f == b ); /* 'b' should be extended in-place, over space formerly occupied by 'd' */
  380. #define TOO_MUCH HEAP_SIZE + 1
  381. /* not enough contiguous space left in the heap */
  382. uint32_t *g = (uint32_t *)multi_heap_realloc(heap, e, TOO_MUCH);
  383. REQUIRE( g == NULL );
  384. multi_heap_free(heap, f);
  385. /* try again */
  386. g = (uint32_t *)multi_heap_realloc(heap, e, 128);
  387. REQUIRE( multi_heap_check(heap, true) );
  388. REQUIRE( e == g ); /* 'g' extends 'e' in place, into the space formerly held by 'f' */
  389. #endif // MULTI_HEAP_POISONING_SLOW
  390. }
  391. // TLSF only accepts heaps aligned to 4-byte boundary so
  392. // only aligned allocation tests make sense.
  393. TEST_CASE("multi_heap aligned allocations", "[multi_heap]")
  394. {
  395. uint8_t test_heap[4 * 1024];
  396. multi_heap_handle_t heap = multi_heap_register(test_heap, sizeof(test_heap));
  397. uint32_t aligments = 0; // starts from alignment by 4-byte boundary
  398. size_t old_size = multi_heap_free_size(heap);
  399. size_t leakage = 1024;
  400. printf("[ALIGNED_ALLOC] heap_size before: %d \n", old_size);
  401. printf("New heap:\n");
  402. multi_heap_dump(heap);
  403. printf("*********************\n");
  404. for(;aligments <= 256; aligments++) {
  405. //Use some stupid size value to test correct alignment even in strange
  406. //memory layout objects:
  407. uint8_t *buf = (uint8_t *)multi_heap_aligned_alloc(heap, (aligments + 137), aligments );
  408. if(((aligments & (aligments - 1)) != 0) || (!aligments)) {
  409. REQUIRE( buf == NULL );
  410. } else {
  411. REQUIRE( buf != NULL );
  412. REQUIRE((intptr_t)buf >= (intptr_t)test_heap);
  413. REQUIRE((intptr_t)buf < (intptr_t)(test_heap + sizeof(test_heap)));
  414. printf("[ALIGNED_ALLOC] alignment required: %u \n", aligments);
  415. printf("[ALIGNED_ALLOC] address of allocated memory: %p \n\n", (void *)buf);
  416. //Address of obtained block must be aligned with selected value
  417. REQUIRE(((intptr_t)buf & (aligments - 1)) == 0);
  418. //Write some data, if it corrupts memory probably the heap
  419. //canary verification will fail:
  420. memset(buf, 0xA5, (aligments + 137));
  421. multi_heap_free(heap, buf);
  422. }
  423. }
  424. /* Check that TLSF doesn't allocate a memory space smaller than required.
  425. * In any case, TLSF will write data in the previous block than the one
  426. * allocated. Thus, we should try to get/allocate this previous block. If
  427. * the poisoned filled pattern has beeen overwritten by TLSF, then this
  428. * previous block will trigger an exception.
  429. * More info on this bug in !16296. */
  430. const size_t size = 50; /* TLSF will round the size up */
  431. uint8_t *buf1 = (uint8_t *)multi_heap_aligned_alloc(heap, size, 4);
  432. uint8_t *buf2 = (uint8_t *)multi_heap_aligned_alloc(heap, size, 4);
  433. multi_heap_free(heap, buf1);
  434. /* By specifying a size equal of the gap between buf1 and buf2. We are
  435. * trying to force TLSF to allocate two consecutive blocks. */
  436. buf1 = (uint8_t *)multi_heap_aligned_alloc(heap, buf2 - buf1, 4);
  437. multi_heap_free(heap, buf2);
  438. printf("[ALIGNED_ALLOC] heap_size after: %d \n", multi_heap_free_size(heap));
  439. REQUIRE((old_size - multi_heap_free_size(heap)) <= leakage);
  440. }
  441. // TLSF has some overhead when allocating blocks, check that overhead
  442. TEST_CASE("multi_heap allocation overhead", "[multi_heap]")
  443. {
  444. uint8_t heapdata[4 * 1024];
  445. size_t alloc_size = 256;
  446. multi_heap_handle_t heap = multi_heap_register(heapdata, sizeof(heapdata));
  447. size_t free_bytes_1 = multi_heap_free_size(heap);
  448. /* Allocate any amount of data, in any case there will be an overhead */
  449. void *x = multi_heap_malloc(heap, alloc_size);
  450. /* free_bytes_2 should be free_bytes_1 - alloc_size - overhead.
  451. * We don't know the value of overhead, let's check that it is non-zero */
  452. size_t free_bytes_2 = multi_heap_free_size(heap);
  453. REQUIRE( free_bytes_1 > free_bytes_2 );
  454. REQUIRE( free_bytes_1 - free_bytes_2 > alloc_size );
  455. multi_heap_free(heap, x);
  456. }
  457. /* This test will corrupt the memory of a free block in the heap and check
  458. * that in the case of comprehensive poisoning the heap corruption is detected
  459. * by multi_heap_check(). For light poisoning and no poisoning, the test will
  460. * check that multi_heap_check() does not report the corruption.
  461. */
  462. TEST_CASE("multi_heap poisoning detection", "[multi_heap]")
  463. {
  464. const size_t HEAP_SIZE = 4 * 1024;
  465. /* define heap related data */
  466. uint8_t heap_mem[HEAP_SIZE];
  467. memset(heap_mem, 0x00, HEAP_SIZE);
  468. /* register the heap memory. One free block only will be available */
  469. multi_heap_handle_t heap = multi_heap_register(heap_mem, HEAP_SIZE);
  470. control_t *tlsf_ptr = (control_t*)(heap_mem + 20);
  471. const size_t control_t_size = tlsf_ptr->size;
  472. const size_t heap_t_size = 20;
  473. /* offset in memory at which to find the first free memory byte */
  474. const size_t free_memory_offset = heap_t_size + control_t_size + sizeof(block_header_t) - block_header_overhead;
  475. /* block header of the free block under test in the heap () */
  476. const block_header_t* block = (block_header_t*)(heap_mem + free_memory_offset - sizeof(block_header_t));
  477. /* actual number of bytes potentially filled with the free pattern in the free block under test */
  478. const size_t effective_free_size = block_size(block) - block_header_overhead - offsetof(block_header_t, next_free);
  479. /* variable used in the test */
  480. size_t affected_byte = 0x00;
  481. uint8_t original_value = 0x00;
  482. uint8_t corrupted_value = 0x00;
  483. /* repeat the corruption a few times to cover more of the free memory */
  484. for (size_t i = 0; i < effective_free_size; i++)
  485. {
  486. /* corrupt random bytes in the heap (it needs to be bytes from free memory in
  487. * order to check that the comprehensive poisoning is doing its job) */
  488. affected_byte = free_memory_offset + i;
  489. corrupted_value = (rand() % UINT8_MAX) | 1;
  490. /* keep the good value in store in order to check that when we set the byte back
  491. * to its original value, multi_heap_check() no longer returns the heap corruption. */
  492. original_value = heap_mem[affected_byte];
  493. /* make sure we are not replacing the original value with the same value */
  494. heap_mem[affected_byte] ^= corrupted_value;
  495. bool is_heap_ok = multi_heap_check(heap, true);
  496. #ifdef CONFIG_HEAP_POISONING_COMPREHENSIVE
  497. /* check that multi_heap_check() detects the corruption */
  498. REQUIRE(is_heap_ok == false);
  499. #else
  500. /* the comprehensive corruption is not checked in the multi_heap_check() */
  501. REQUIRE(is_heap_ok == true);
  502. #endif
  503. /* fix the corruption */
  504. heap_mem[affected_byte] = original_value;
  505. /* check that multi_heap_check() stops reporting the corruption */
  506. is_heap_ok = multi_heap_check(heap, true);
  507. REQUIRE(is_heap_ok == true);
  508. }
  509. }