| #include "test/jemalloc_test.h" |
| #include "test/san.h" |
| |
| const char *malloc_conf = TEST_SAN_UAF_ALIGN_DISABLE; |
| |
| enum { |
| alloc_option_start = 0, |
| use_malloc = 0, |
| use_mallocx, |
| alloc_option_end |
| }; |
| |
| enum { |
| dalloc_option_start = 0, |
| use_free = 0, |
| use_dallocx, |
| use_sdallocx, |
| dalloc_option_end |
| }; |
| |
| static unsigned alloc_option, dalloc_option; |
| static size_t tcache_max; |
| |
| static void * |
| alloc_func(size_t sz) { |
| void *ret; |
| |
| switch (alloc_option) { |
| case use_malloc: |
| ret = malloc(sz); |
| break; |
| case use_mallocx: |
| ret = mallocx(sz, 0); |
| break; |
| default: |
| unreachable(); |
| } |
| expect_ptr_not_null(ret, "Unexpected malloc / mallocx failure"); |
| |
| return ret; |
| } |
| |
| static void |
| dalloc_func(void *ptr, size_t sz) { |
| switch (dalloc_option) { |
| case use_free: |
| free(ptr); |
| break; |
| case use_dallocx: |
| dallocx(ptr, 0); |
| break; |
| case use_sdallocx: |
| sdallocx(ptr, sz, 0); |
| break; |
| default: |
| unreachable(); |
| } |
| } |
| |
| static size_t |
| tcache_bytes_read(void) { |
| uint64_t epoch; |
| assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)), |
| 0, "Unexpected mallctl() failure"); |
| |
| size_t tcache_bytes; |
| size_t sz = sizeof(tcache_bytes); |
| assert_d_eq(mallctl( |
| "stats.arenas." STRINGIFY(MALLCTL_ARENAS_ALL) ".tcache_bytes", |
| &tcache_bytes, &sz, NULL, 0), 0, "Unexpected mallctl failure"); |
| |
| return tcache_bytes; |
| } |
| |
| static void |
| tcache_bytes_check_update(size_t *prev, ssize_t diff) { |
| size_t tcache_bytes = tcache_bytes_read(); |
| expect_zu_eq(tcache_bytes, *prev + diff, "tcache bytes not expected"); |
| |
| *prev += diff; |
| } |
| |
| static void |
| test_tcache_bytes_alloc(size_t alloc_size) { |
| expect_d_eq(mallctl("thread.tcache.flush", NULL, NULL, NULL, 0), 0, |
| "Unexpected tcache flush failure"); |
| |
| size_t usize = sz_s2u(alloc_size); |
| /* No change is expected if usize is outside of tcache_max range. */ |
| bool cached = (usize <= tcache_max); |
| ssize_t diff = cached ? usize : 0; |
| |
| void *ptr1 = alloc_func(alloc_size); |
| void *ptr2 = alloc_func(alloc_size); |
| |
| size_t bytes = tcache_bytes_read(); |
| dalloc_func(ptr2, alloc_size); |
| /* Expect tcache_bytes increase after dalloc */ |
| tcache_bytes_check_update(&bytes, diff); |
| |
| dalloc_func(ptr1, alloc_size); |
| /* Expect tcache_bytes increase again */ |
| tcache_bytes_check_update(&bytes, diff); |
| |
| void *ptr3 = alloc_func(alloc_size); |
| if (cached) { |
| expect_ptr_eq(ptr1, ptr3, "Unexpected cached ptr"); |
| } |
| /* Expect tcache_bytes decrease after alloc */ |
| tcache_bytes_check_update(&bytes, -diff); |
| |
| void *ptr4 = alloc_func(alloc_size); |
| if (cached) { |
| expect_ptr_eq(ptr2, ptr4, "Unexpected cached ptr"); |
| } |
| /* Expect tcache_bytes decrease again */ |
| tcache_bytes_check_update(&bytes, -diff); |
| |
| dalloc_func(ptr3, alloc_size); |
| tcache_bytes_check_update(&bytes, diff); |
| dalloc_func(ptr4, alloc_size); |
| tcache_bytes_check_update(&bytes, diff); |
| } |
| |
| static void |
| test_tcache_max_impl(void) { |
| size_t sz; |
| sz = sizeof(tcache_max); |
| assert_d_eq(mallctl("arenas.tcache_max", (void *)&tcache_max, |
| &sz, NULL, 0), 0, "Unexpected mallctl() failure"); |
| |
| /* opt.tcache_max set to 1024 in tcache_max.sh */ |
| expect_zu_eq(tcache_max, 1024, "tcache_max not expected"); |
| |
| test_tcache_bytes_alloc(1); |
| test_tcache_bytes_alloc(tcache_max - 1); |
| test_tcache_bytes_alloc(tcache_max); |
| test_tcache_bytes_alloc(tcache_max + 1); |
| |
| test_tcache_bytes_alloc(PAGE - 1); |
| test_tcache_bytes_alloc(PAGE); |
| test_tcache_bytes_alloc(PAGE + 1); |
| |
| size_t large; |
| sz = sizeof(large); |
| assert_d_eq(mallctl("arenas.lextent.0.size", (void *)&large, &sz, NULL, |
| 0), 0, "Unexpected mallctl() failure"); |
| |
| test_tcache_bytes_alloc(large - 1); |
| test_tcache_bytes_alloc(large); |
| test_tcache_bytes_alloc(large + 1); |
| } |
| |
| TEST_BEGIN(test_tcache_max) { |
| test_skip_if(!config_stats); |
| test_skip_if(!opt_tcache); |
| test_skip_if(opt_prof); |
| test_skip_if(san_uaf_detection_enabled()); |
| |
| for (alloc_option = alloc_option_start; |
| alloc_option < alloc_option_end; |
| alloc_option++) { |
| for (dalloc_option = dalloc_option_start; |
| dalloc_option < dalloc_option_end; |
| dalloc_option++) { |
| test_tcache_max_impl(); |
| } |
| } |
| } |
| TEST_END |
| |
| int |
| main(void) { |
| return test(test_tcache_max); |
| } |