Add quarantine unit tests.
Verify that freed regions are quarantined, and that redzone corruption is detected. Introduce a testing idiom for intercepting/replacing internal functions. In this case the replaced function is ordinarily a static function, but the idiom should work similarly for library-private functions.
This commit is contained in:
parent
eca367b779
commit
0d6c5d8bd0
@ -110,7 +110,8 @@ C_UTIL_INTEGRATION_SRCS := $(srcroot)src/util.c
|
|||||||
TESTS_UNIT := $(srcroot)test/unit/bitmap.c $(srcroot)test/unit/ckh.c \
|
TESTS_UNIT := $(srcroot)test/unit/bitmap.c $(srcroot)test/unit/ckh.c \
|
||||||
$(srcroot)test/unit/hash.c $(srcroot)test/unit/math.c \
|
$(srcroot)test/unit/hash.c $(srcroot)test/unit/math.c \
|
||||||
$(srcroot)test/unit/mq.c $(srcroot)test/unit/mtx.c \
|
$(srcroot)test/unit/mq.c $(srcroot)test/unit/mtx.c \
|
||||||
$(srcroot)test/unit/SFMT.c $(srcroot)test/unit/tsd.c
|
$(srcroot)test/unit/quarantine.c $(srcroot)test/unit/SFMT.c \
|
||||||
|
$(srcroot)test/unit/tsd.c
|
||||||
TESTS_INTEGRATION := $(srcroot)test/integration/aligned_alloc.c \
|
TESTS_INTEGRATION := $(srcroot)test/integration/aligned_alloc.c \
|
||||||
$(srcroot)test/integration/allocated.c \
|
$(srcroot)test/integration/allocated.c \
|
||||||
$(srcroot)test/integration/mallocx.c \
|
$(srcroot)test/integration/mallocx.c \
|
||||||
|
@ -405,7 +405,13 @@ void arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin,
|
|||||||
size_t binind, uint64_t prof_accumbytes);
|
size_t binind, uint64_t prof_accumbytes);
|
||||||
void arena_alloc_junk_small(void *ptr, arena_bin_info_t *bin_info,
|
void arena_alloc_junk_small(void *ptr, arena_bin_info_t *bin_info,
|
||||||
bool zero);
|
bool zero);
|
||||||
|
#ifdef JEMALLOC_JET
|
||||||
|
typedef void (arena_redzone_corruption_t)(void *, size_t, bool, size_t,
|
||||||
|
uint8_t);
|
||||||
|
extern arena_redzone_corruption_t *arena_redzone_corruption_fptr;
|
||||||
|
#endif
|
||||||
void arena_dalloc_junk_small(void *ptr, arena_bin_info_t *bin_info);
|
void arena_dalloc_junk_small(void *ptr, arena_bin_info_t *bin_info);
|
||||||
|
void arena_quarantine_junk_small(void *ptr, size_t usize);
|
||||||
void *arena_malloc_small(arena_t *arena, size_t size, bool zero);
|
void *arena_malloc_small(arena_t *arena, size_t size, bool zero);
|
||||||
void *arena_malloc_large(arena_t *arena, size_t size, bool zero);
|
void *arena_malloc_large(arena_t *arena, size_t size, bool zero);
|
||||||
void *arena_palloc(arena_t *arena, size_t size, size_t alignment, bool zero);
|
void *arena_palloc(arena_t *arena, size_t size, size_t alignment, bool zero);
|
||||||
|
@ -50,8 +50,10 @@ arena_prof_ctx_set
|
|||||||
arena_prof_promoted
|
arena_prof_promoted
|
||||||
arena_ptr_small_binind_get
|
arena_ptr_small_binind_get
|
||||||
arena_purge_all
|
arena_purge_all
|
||||||
|
arena_quarantine_junk_small
|
||||||
arena_ralloc
|
arena_ralloc
|
||||||
arena_ralloc_no_move
|
arena_ralloc_no_move
|
||||||
|
arena_redzone_corruption
|
||||||
arena_run_regind
|
arena_run_regind
|
||||||
arena_salloc
|
arena_salloc
|
||||||
arena_stats_merge
|
arena_stats_merge
|
||||||
|
66
src/arena.c
66
src/arena.c
@ -1432,8 +1432,28 @@ arena_alloc_junk_small(void *ptr, arena_bin_info_t *bin_info, bool zero)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
#ifdef JEMALLOC_JET
|
||||||
arena_dalloc_junk_small(void *ptr, arena_bin_info_t *bin_info)
|
#undef arena_redzone_corruption
|
||||||
|
#define arena_redzone_corruption JEMALLOC_N(arena_redzone_corruption_impl)
|
||||||
|
#endif
|
||||||
|
static void
|
||||||
|
arena_redzone_corruption(void *ptr, size_t usize, bool after,
|
||||||
|
size_t offset, uint8_t byte)
|
||||||
|
{
|
||||||
|
|
||||||
|
malloc_printf("<jemalloc>: Corrupt redzone %zu byte%s %s %p "
|
||||||
|
"(size %zu), byte=%#x\n", offset, (offset == 1) ? "" : "s",
|
||||||
|
after ? "after" : "before", ptr, usize, byte);
|
||||||
|
}
|
||||||
|
#ifdef JEMALLOC_JET
|
||||||
|
arena_redzone_corruption_t *arena_redzone_corruption_fptr =
|
||||||
|
arena_redzone_corruption;
|
||||||
|
#undef arena_redzone_corruption
|
||||||
|
#define arena_redzone_corruption arena_redzone_corruption_fptr
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void
|
||||||
|
arena_redzones_validate(void *ptr, arena_bin_info_t *bin_info, bool reset)
|
||||||
{
|
{
|
||||||
size_t size = bin_info->reg_size;
|
size_t size = bin_info->reg_size;
|
||||||
size_t redzone_size = bin_info->redzone_size;
|
size_t redzone_size = bin_info->redzone_size;
|
||||||
@ -1441,30 +1461,52 @@ arena_dalloc_junk_small(void *ptr, arena_bin_info_t *bin_info)
|
|||||||
bool error = false;
|
bool error = false;
|
||||||
|
|
||||||
for (i = 1; i <= redzone_size; i++) {
|
for (i = 1; i <= redzone_size; i++) {
|
||||||
unsigned byte;
|
uint8_t *byte = (uint8_t *)((uintptr_t)ptr - i);
|
||||||
if ((byte = *(uint8_t *)((uintptr_t)ptr - i)) != 0xa5) {
|
if (*byte != 0xa5) {
|
||||||
error = true;
|
error = true;
|
||||||
malloc_printf("<jemalloc>: Corrupt redzone "
|
arena_redzone_corruption(ptr, size, false, i, *byte);
|
||||||
"%zu byte%s before %p (size %zu), byte=%#x\n", i,
|
if (reset)
|
||||||
(i == 1) ? "" : "s", ptr, size, byte);
|
*byte = 0xa5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (i = 0; i < redzone_size; i++) {
|
for (i = 0; i < redzone_size; i++) {
|
||||||
unsigned byte;
|
uint8_t *byte = (uint8_t *)((uintptr_t)ptr + size + i);
|
||||||
if ((byte = *(uint8_t *)((uintptr_t)ptr + size + i)) != 0xa5) {
|
if (*byte != 0xa5) {
|
||||||
error = true;
|
error = true;
|
||||||
malloc_printf("<jemalloc>: Corrupt redzone "
|
arena_redzone_corruption(ptr, size, true, i, *byte);
|
||||||
"%zu byte%s after end of %p (size %zu), byte=%#x\n",
|
if (reset)
|
||||||
i, (i == 1) ? "" : "s", ptr, size, byte);
|
*byte = 0xa5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (opt_abort && error)
|
if (opt_abort && error)
|
||||||
abort();
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
arena_dalloc_junk_small(void *ptr, arena_bin_info_t *bin_info)
|
||||||
|
{
|
||||||
|
size_t redzone_size = bin_info->redzone_size;
|
||||||
|
|
||||||
|
arena_redzones_validate(ptr, bin_info, false);
|
||||||
memset((void *)((uintptr_t)ptr - redzone_size), 0x5a,
|
memset((void *)((uintptr_t)ptr - redzone_size), 0x5a,
|
||||||
bin_info->reg_interval);
|
bin_info->reg_interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
arena_quarantine_junk_small(void *ptr, size_t usize)
|
||||||
|
{
|
||||||
|
size_t binind;
|
||||||
|
arena_bin_info_t *bin_info;
|
||||||
|
cassert(config_fill);
|
||||||
|
assert(opt_junk);
|
||||||
|
assert(opt_quarantine);
|
||||||
|
assert(usize <= SMALL_MAXCLASS);
|
||||||
|
|
||||||
|
binind = SMALL_SIZE2BIN(usize);
|
||||||
|
bin_info = &arena_bin_info[binind];
|
||||||
|
arena_redzones_validate(ptr, bin_info, true);
|
||||||
|
}
|
||||||
|
|
||||||
void *
|
void *
|
||||||
arena_malloc_small(arena_t *arena, size_t size, bool zero)
|
arena_malloc_small(arena_t *arena, size_t size, bool zero)
|
||||||
{
|
{
|
||||||
|
@ -141,8 +141,17 @@ quarantine(void *ptr)
|
|||||||
obj->usize = usize;
|
obj->usize = usize;
|
||||||
quarantine->curbytes += usize;
|
quarantine->curbytes += usize;
|
||||||
quarantine->curobjs++;
|
quarantine->curobjs++;
|
||||||
if (opt_junk)
|
if (config_fill && opt_junk) {
|
||||||
memset(ptr, 0x5a, usize);
|
/*
|
||||||
|
* Only do redzone validation if Valgrind isn't in
|
||||||
|
* operation.
|
||||||
|
*/
|
||||||
|
if ((config_valgrind == false || opt_valgrind == false)
|
||||||
|
&& usize <= SMALL_MAXCLASS)
|
||||||
|
arena_quarantine_junk_small(ptr, usize);
|
||||||
|
else
|
||||||
|
memset(ptr, 0x5a, usize);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
assert(quarantine->curbytes == 0);
|
assert(quarantine->curbytes == 0);
|
||||||
idalloc(ptr);
|
idalloc(ptr);
|
||||||
|
@ -187,12 +187,22 @@ f(void) \
|
|||||||
p_test_init(#f);
|
p_test_init(#f);
|
||||||
|
|
||||||
#define TEST_END \
|
#define TEST_END \
|
||||||
|
goto label_test_end; \
|
||||||
|
label_test_end: \
|
||||||
p_test_fini(); \
|
p_test_fini(); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define test(tests...) \
|
#define test(tests...) \
|
||||||
p_test(tests, NULL)
|
p_test(tests, NULL)
|
||||||
|
|
||||||
|
#define test_skip_if(e) do { \
|
||||||
|
if (e) { \
|
||||||
|
test_skip("%s:%s:%d: Test skipped: (%s)", \
|
||||||
|
__func__, __FILE__, __LINE__, #e); \
|
||||||
|
goto label_test_end; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
void test_skip(const char *format, ...) JEMALLOC_ATTR(format(printf, 1, 2));
|
void test_skip(const char *format, ...) JEMALLOC_ATTR(format(printf, 1, 2));
|
||||||
void test_fail(const char *format, ...) JEMALLOC_ATTR(format(printf, 1, 2));
|
void test_fail(const char *format, ...) JEMALLOC_ATTR(format(printf, 1, 2));
|
||||||
|
|
||||||
|
108
test/unit/quarantine.c
Normal file
108
test/unit/quarantine.c
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
#include "test/jemalloc_test.h"
|
||||||
|
|
||||||
|
#define QUARANTINE_SIZE 8192
|
||||||
|
#define STRINGIFY_HELPER(x) #x
|
||||||
|
#define STRINGIFY(x) STRINGIFY_HELPER(x)
|
||||||
|
|
||||||
|
#ifdef JEMALLOC_FILL
|
||||||
|
const char *malloc_conf = "abort:false,junk:true,redzone:true,quarantine:"
|
||||||
|
STRINGIFY(QUARANTINE_SIZE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void
|
||||||
|
quarantine_clear(void)
|
||||||
|
{
|
||||||
|
void *p;
|
||||||
|
|
||||||
|
p = mallocx(QUARANTINE_SIZE*2, 0);
|
||||||
|
assert_ptr_not_null(p, "Unexpected mallocx() failure");
|
||||||
|
dallocx(p, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_BEGIN(test_quarantine)
|
||||||
|
{
|
||||||
|
#define SZ 256
|
||||||
|
#define NQUARANTINED (QUARANTINE_SIZE/SZ)
|
||||||
|
void *quarantined[NQUARANTINED+1];
|
||||||
|
size_t i, j;
|
||||||
|
|
||||||
|
test_skip_if(!config_fill);
|
||||||
|
|
||||||
|
assert_zu_eq(nallocx(SZ, 0), SZ,
|
||||||
|
"SZ=%zu does not precisely equal a size class", SZ);
|
||||||
|
|
||||||
|
quarantine_clear();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate enough regions to completely fill the quarantine, plus one
|
||||||
|
* more. The last iteration occurs with a completely full quarantine,
|
||||||
|
* but no regions should be drained from the quarantine until the last
|
||||||
|
* deallocation occurs. Therefore no region recycling should occur
|
||||||
|
* until after this loop completes.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < NQUARANTINED+1; i++) {
|
||||||
|
void *p = mallocx(SZ, 0);
|
||||||
|
assert_ptr_not_null(p, "Unexpected mallocx() failure");
|
||||||
|
quarantined[i] = p;
|
||||||
|
dallocx(p, 0);
|
||||||
|
for (j = 0; j < i; j++) {
|
||||||
|
assert_ptr_ne(p, quarantined[j],
|
||||||
|
"Quarantined region recycled too early; "
|
||||||
|
"i=%zu, j=%zu", i, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#undef NQUARANTINED
|
||||||
|
#undef SZ
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
static bool detected_redzone_corruption;
|
||||||
|
|
||||||
|
static void
|
||||||
|
arena_redzone_corruption_replacement(void *ptr, size_t usize, bool after,
|
||||||
|
size_t offset, uint8_t byte)
|
||||||
|
{
|
||||||
|
|
||||||
|
detected_redzone_corruption = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_BEGIN(test_quarantine_redzone)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
arena_redzone_corruption_t *arena_redzone_corruption_orig;
|
||||||
|
|
||||||
|
test_skip_if(!config_fill);
|
||||||
|
|
||||||
|
arena_redzone_corruption_orig = arena_redzone_corruption_fptr;
|
||||||
|
arena_redzone_corruption_fptr = arena_redzone_corruption_replacement;
|
||||||
|
|
||||||
|
/* Test underflow. */
|
||||||
|
detected_redzone_corruption = false;
|
||||||
|
s = (char *)mallocx(1, 0);
|
||||||
|
assert_ptr_not_null((void *)s, "Unexpected mallocx() failure");
|
||||||
|
s[-1] = 0xbb;
|
||||||
|
dallocx(s, 0);
|
||||||
|
assert_true(detected_redzone_corruption,
|
||||||
|
"Did not detect redzone corruption");
|
||||||
|
|
||||||
|
/* Test overflow. */
|
||||||
|
detected_redzone_corruption = false;
|
||||||
|
s = (char *)mallocx(1, 0);
|
||||||
|
assert_ptr_not_null((void *)s, "Unexpected mallocx() failure");
|
||||||
|
s[sallocx(s, 0)] = 0xbb;
|
||||||
|
dallocx(s, 0);
|
||||||
|
assert_true(detected_redzone_corruption,
|
||||||
|
"Did not detect redzone corruption");
|
||||||
|
|
||||||
|
arena_redzone_corruption_fptr = arena_redzone_corruption_orig;
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
return (test(
|
||||||
|
test_quarantine,
|
||||||
|
test_quarantine_redzone));
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user