summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason Evans <jasone@canonware.com>2013-12-17 23:14:36 (GMT)
committerJason Evans <jasone@canonware.com>2013-12-17 23:19:12 (GMT)
commit0d6c5d8bd0d866a0ce4ce321259cec65d6459821 (patch)
tree083566d27a62252feb2c329c9dee1ee4745a140e
parenteca367b77909ebd649fbd0430e1e9b80dded14e0 (diff)
downloadjemalloc-0d6c5d8bd0d866a0ce4ce321259cec65d6459821.zip
jemalloc-0d6c5d8bd0d866a0ce4ce321259cec65d6459821.tar.gz
jemalloc-0d6c5d8bd0d866a0ce4ce321259cec65d6459821.tar.bz2
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.
-rw-r--r--Makefile.in3
-rw-r--r--include/jemalloc/internal/arena.h6
-rw-r--r--include/jemalloc/internal/private_symbols.txt2
-rw-r--r--src/arena.c66
-rw-r--r--src/quarantine.c13
-rw-r--r--test/include/test/test.h10
-rw-r--r--test/unit/quarantine.c108
7 files changed, 193 insertions, 15 deletions
diff --git a/Makefile.in b/Makefile.in
index 08dad5f..afa7bde 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -110,7 +110,8 @@ C_UTIL_INTEGRATION_SRCS := $(srcroot)src/util.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/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 \
$(srcroot)test/integration/allocated.c \
$(srcroot)test/integration/mallocx.c \
diff --git a/include/jemalloc/internal/arena.h b/include/jemalloc/internal/arena.h
index e43aed1..4151751 100644
--- a/include/jemalloc/internal/arena.h
+++ b/include/jemalloc/internal/arena.h
@@ -405,7 +405,13 @@ void arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin,
size_t binind, uint64_t prof_accumbytes);
void arena_alloc_junk_small(void *ptr, arena_bin_info_t *bin_info,
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_quarantine_junk_small(void *ptr, size_t usize);
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_palloc(arena_t *arena, size_t size, size_t alignment, bool zero);
diff --git a/include/jemalloc/internal/private_symbols.txt b/include/jemalloc/internal/private_symbols.txt
index 541e1b2..fae648c 100644
--- a/include/jemalloc/internal/private_symbols.txt
+++ b/include/jemalloc/internal/private_symbols.txt
@@ -50,8 +50,10 @@ arena_prof_ctx_set
arena_prof_promoted
arena_ptr_small_binind_get
arena_purge_all
+arena_quarantine_junk_small
arena_ralloc
arena_ralloc_no_move
+arena_redzone_corruption
arena_run_regind
arena_salloc
arena_stats_merge
diff --git a/src/arena.c b/src/arena.c
index 406cf5d..71057aa 100644
--- a/src/arena.c
+++ b/src/arena.c
@@ -1432,8 +1432,28 @@ arena_alloc_junk_small(void *ptr, arena_bin_info_t *bin_info, bool zero)
}
}
-void
-arena_dalloc_junk_small(void *ptr, arena_bin_info_t *bin_info)
+#ifdef JEMALLOC_JET
+#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 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;
for (i = 1; i <= redzone_size; i++) {
- unsigned byte;
- if ((byte = *(uint8_t *)((uintptr_t)ptr - i)) != 0xa5) {
+ uint8_t *byte = (uint8_t *)((uintptr_t)ptr - i);
+ if (*byte != 0xa5) {
error = true;
- malloc_printf("<jemalloc>: Corrupt redzone "
- "%zu byte%s before %p (size %zu), byte=%#x\n", i,
- (i == 1) ? "" : "s", ptr, size, byte);
+ arena_redzone_corruption(ptr, size, false, i, *byte);
+ if (reset)
+ *byte = 0xa5;
}
}
for (i = 0; i < redzone_size; i++) {
- unsigned byte;
- if ((byte = *(uint8_t *)((uintptr_t)ptr + size + i)) != 0xa5) {
+ uint8_t *byte = (uint8_t *)((uintptr_t)ptr + size + i);
+ if (*byte != 0xa5) {
error = true;
- malloc_printf("<jemalloc>: Corrupt redzone "
- "%zu byte%s after end of %p (size %zu), byte=%#x\n",
- i, (i == 1) ? "" : "s", ptr, size, byte);
+ arena_redzone_corruption(ptr, size, true, i, *byte);
+ if (reset)
+ *byte = 0xa5;
}
}
if (opt_abort && error)
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,
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 *
arena_malloc_small(arena_t *arena, size_t size, bool zero)
{
diff --git a/src/quarantine.c b/src/quarantine.c
index f96a948..5431511 100644
--- a/src/quarantine.c
+++ b/src/quarantine.c
@@ -141,8 +141,17 @@ quarantine(void *ptr)
obj->usize = usize;
quarantine->curbytes += usize;
quarantine->curobjs++;
- if (opt_junk)
- memset(ptr, 0x5a, usize);
+ if (config_fill && opt_junk) {
+ /*
+ * 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 {
assert(quarantine->curbytes == 0);
idalloc(ptr);
diff --git a/test/include/test/test.h b/test/include/test/test.h
index d7601f8..e0bbfac 100644
--- a/test/include/test/test.h
+++ b/test/include/test/test.h
@@ -187,12 +187,22 @@ f(void) \
p_test_init(#f);
#define TEST_END \
+ goto label_test_end; \
+label_test_end: \
p_test_fini(); \
}
#define test(tests...) \
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_fail(const char *format, ...) JEMALLOC_ATTR(format(printf, 1, 2));
diff --git a/test/unit/quarantine.c b/test/unit/quarantine.c
new file mode 100644
index 0000000..d8a65e2
--- /dev/null
+++ b/test/unit/quarantine.c
@@ -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));
+}