#include "test/jemalloc_test.h" #define arraylen(arr) (sizeof(arr)/sizeof(arr[0])) static size_t ptr_ind; static void *volatile ptrs[100]; static void *last_junked_ptr; static size_t last_junked_usize; static void reset() { ptr_ind = 0; last_junked_ptr = NULL; last_junked_usize = 0; } static void test_junk(void *ptr, size_t usize) { last_junked_ptr = ptr; last_junked_usize = usize; } static void do_allocs(size_t size, bool zero, size_t lg_align) { #define JUNK_ALLOC(...) \ do { \ assert(ptr_ind + 1 < arraylen(ptrs)); \ void *ptr = __VA_ARGS__; \ assert_ptr_not_null(ptr, ""); \ ptrs[ptr_ind++] = ptr; \ if (opt_junk_alloc && !zero) { \ expect_ptr_eq(ptr, last_junked_ptr, ""); \ expect_zu_eq(last_junked_usize, \ TEST_MALLOC_SIZE(ptr), ""); \ } \ } while (0) if (!zero && lg_align == 0) { JUNK_ALLOC(malloc(size)); } if (!zero) { JUNK_ALLOC(aligned_alloc(1 << lg_align, size)); } #ifdef JEMALLOC_OVERRIDE_MEMALIGN if (!zero) { JUNK_ALLOC(je_memalign(1 << lg_align, size)); } #endif #ifdef JEMALLOC_OVERRIDE_VALLOC if (!zero && lg_align == LG_PAGE) { JUNK_ALLOC(je_valloc(size)); } #endif int zero_flag = zero ? MALLOCX_ZERO : 0; JUNK_ALLOC(mallocx(size, zero_flag | MALLOCX_LG_ALIGN(lg_align))); JUNK_ALLOC(mallocx(size, zero_flag | MALLOCX_LG_ALIGN(lg_align) | MALLOCX_TCACHE_NONE)); if (lg_align >= LG_SIZEOF_PTR) { void *memalign_result; int err = posix_memalign(&memalign_result, (1 << lg_align), size); assert_d_eq(err, 0, ""); JUNK_ALLOC(memalign_result); } } TEST_BEGIN(test_junk_alloc_free) { bool zerovals[] = {false, true}; size_t sizevals[] = { 1, 8, 100, 1000, 100*1000 /* * Memory allocation failure is a real possibility in 32-bit mode. * Rather than try to check in the face of resource exhaustion, we just * rely more on the 64-bit tests. This is a little bit white-box-y in * the sense that this is only a good test strategy if we know that the * junk pathways don't touch interact with the allocation selection * mechanisms; but this is in fact the case. */ #if LG_SIZEOF_PTR == 3 , 10 * 1000 * 1000 #endif }; size_t lg_alignvals[] = { 0, 4, 10, 15, 16, LG_PAGE #if LG_SIZEOF_PTR == 3 , 20, 24 #endif }; #define JUNK_FREE(...) \ do { \ do_allocs(size, zero, lg_align); \ for (size_t n = 0; n < ptr_ind; n++) { \ void *ptr = ptrs[n]; \ __VA_ARGS__; \ if (opt_junk_free) { \ assert_ptr_eq(ptr, last_junked_ptr, \ ""); \ assert_zu_eq(usize, last_junked_usize, \ ""); \ } \ reset(); \ } \ } while (0) for (size_t i = 0; i < arraylen(zerovals); i++) { for (size_t j = 0; j < arraylen(sizevals); j++) { for (size_t k = 0; k < arraylen(lg_alignvals); k++) { bool zero = zerovals[i]; size_t size = sizevals[j]; size_t lg_align = lg_alignvals[k]; size_t usize = nallocx(size, MALLOCX_LG_ALIGN(lg_align)); JUNK_FREE(free(ptr)); JUNK_FREE(dallocx(ptr, 0)); JUNK_FREE(dallocx(ptr, MALLOCX_TCACHE_NONE)); JUNK_FREE(dallocx(ptr, MALLOCX_LG_ALIGN( lg_align))); JUNK_FREE(sdallocx(ptr, usize, MALLOCX_LG_ALIGN( lg_align))); JUNK_FREE(sdallocx(ptr, usize, MALLOCX_TCACHE_NONE | MALLOCX_LG_ALIGN(lg_align))); if (opt_zero_realloc_action == zero_realloc_action_free) { JUNK_FREE(realloc(ptr, 0)); } } } } } TEST_END TEST_BEGIN(test_realloc_expand) { char *volatile ptr; char *volatile expanded; test_skip_if(!opt_junk_alloc); /* Realloc */ ptr = malloc(SC_SMALL_MAXCLASS); expanded = realloc(ptr, SC_LARGE_MINCLASS); expect_ptr_eq(last_junked_ptr, &expanded[SC_SMALL_MAXCLASS], ""); expect_zu_eq(last_junked_usize, SC_LARGE_MINCLASS - SC_SMALL_MAXCLASS, ""); free(expanded); /* rallocx(..., 0) */ ptr = malloc(SC_SMALL_MAXCLASS); expanded = rallocx(ptr, SC_LARGE_MINCLASS, 0); expect_ptr_eq(last_junked_ptr, &expanded[SC_SMALL_MAXCLASS], ""); expect_zu_eq(last_junked_usize, SC_LARGE_MINCLASS - SC_SMALL_MAXCLASS, ""); free(expanded); /* rallocx(..., nonzero) */ ptr = malloc(SC_SMALL_MAXCLASS); expanded = rallocx(ptr, SC_LARGE_MINCLASS, MALLOCX_TCACHE_NONE); expect_ptr_eq(last_junked_ptr, &expanded[SC_SMALL_MAXCLASS], ""); expect_zu_eq(last_junked_usize, SC_LARGE_MINCLASS - SC_SMALL_MAXCLASS, ""); free(expanded); /* rallocx(..., MALLOCX_ZERO) */ ptr = malloc(SC_SMALL_MAXCLASS); last_junked_ptr = (void *)-1; last_junked_usize = (size_t)-1; expanded = rallocx(ptr, SC_LARGE_MINCLASS, MALLOCX_ZERO); expect_ptr_eq(last_junked_ptr, (void *)-1, ""); expect_zu_eq(last_junked_usize, (size_t)-1, ""); free(expanded); /* * Unfortunately, testing xallocx reliably is difficult to do portably * (since allocations can be expanded / not expanded differently on * different platforms. We rely on manual inspection there -- the * xallocx pathway is easy to inspect, though. * * Likewise, we don't test the shrinking pathways. It's difficult to do * so consistently (because of the risk of split failure or memory * exhaustion, in which case no junking should happen). This is fine * -- junking is a best-effort debug mechanism in the first place. */ } TEST_END int main(void) { junk_alloc_callback = &test_junk; junk_free_callback = &test_junk; /* * We check the last pointer junked. If a reentrant call happens, that * might be an internal allocation. */ return test_no_reentrancy( test_junk_alloc_free, test_realloc_expand); }