Safety checks: Add a redzoning feature.

This commit is contained in:
David Goldblatt 2019-03-22 12:53:11 -07:00 committed by David Goldblatt
parent b92c9a1a81
commit 33e1dad680
12 changed files with 233 additions and 22 deletions

View File

@ -210,6 +210,7 @@ TESTS_UNIT := \
$(srcroot)test/unit/rb.c \ $(srcroot)test/unit/rb.c \
$(srcroot)test/unit/retained.c \ $(srcroot)test/unit/retained.c \
$(srcroot)test/unit/rtree.c \ $(srcroot)test/unit/rtree.c \
$(srcroot)test/unit/safety_check.c \
$(srcroot)test/unit/seq.c \ $(srcroot)test/unit/seq.c \
$(srcroot)test/unit/SFMT.c \ $(srcroot)test/unit/SFMT.c \
$(srcroot)test/unit/sc.c \ $(srcroot)test/unit/sc.c \

View File

@ -60,7 +60,7 @@ void *arena_malloc_hard(tsdn_t *tsdn, arena_t *arena, size_t size,
szind_t ind, bool zero); szind_t ind, bool zero);
void *arena_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, void *arena_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize,
size_t alignment, bool zero, tcache_t *tcache); size_t alignment, bool zero, tcache_t *tcache);
void arena_prof_promote(tsdn_t *tsdn, const void *ptr, size_t usize); void arena_prof_promote(tsdn_t *tsdn, void *ptr, size_t usize);
void arena_dalloc_promoted(tsdn_t *tsdn, void *ptr, tcache_t *tcache, void arena_dalloc_promoted(tsdn_t *tsdn, void *ptr, tcache_t *tcache,
bool slow_path); bool slow_path);
void arena_dalloc_bin_junked_locked(tsdn_t *tsdn, arena_t *arena, bin_t *bin, void arena_dalloc_bin_junked_locked(tsdn_t *tsdn, arena_t *arena, bin_t *bin,

View File

@ -166,7 +166,7 @@ static const bool config_log =
* deallocations, double-frees, etc. * deallocations, double-frees, etc.
*/ */
static const bool config_opt_safety_checks = static const bool config_opt_safety_checks =
#if defined(JEMALLOC_EXTRA_SAFETY_CHECKS) #ifdef JEMALLOC_OPT_SAFETY_CHECKS
true true
#elif defined(JEMALLOC_DEBUG) #elif defined(JEMALLOC_DEBUG)
/* /*

View File

@ -1,6 +1,7 @@
#ifndef JEMALLOC_INTERNAL_PROF_INLINES_B_H #ifndef JEMALLOC_INTERNAL_PROF_INLINES_B_H
#define JEMALLOC_INTERNAL_PROF_INLINES_B_H #define JEMALLOC_INTERNAL_PROF_INLINES_B_H
#include "jemalloc/internal/safety_check.h"
#include "jemalloc/internal/sz.h" #include "jemalloc/internal/sz.h"
JEMALLOC_ALWAYS_INLINE bool JEMALLOC_ALWAYS_INLINE bool

View File

@ -2,5 +2,25 @@
#define JEMALLOC_INTERNAL_SAFETY_CHECK_H #define JEMALLOC_INTERNAL_SAFETY_CHECK_H
void safety_check_fail(const char *format, ...); void safety_check_fail(const char *format, ...);
/* Can set to NULL for a default. */
void safety_check_set_abort(void (*abort_fn)());
JEMALLOC_ALWAYS_INLINE void
safety_check_set_redzone(void *ptr, size_t usize, size_t bumped_usize) {
assert(usize < bumped_usize);
for (size_t i = usize; i < bumped_usize && i < usize + 32; ++i) {
*((unsigned char *)ptr + usize) = 0xBC;
}
}
JEMALLOC_ALWAYS_INLINE void
safety_check_verify_redzone(const void *ptr, size_t usize, size_t bumped_usize)
{
for (size_t i = usize; i < bumped_usize && i < usize + 32; ++i) {
if (unlikely(*((unsigned char *)ptr + usize) != 0xBC)) {
safety_check_fail("Use after free error\n");
}
}
}
#endif /*JEMALLOC_INTERNAL_SAFETY_CHECK_H */ #endif /*JEMALLOC_INTERNAL_SAFETY_CHECK_H */

View File

@ -8,6 +8,7 @@
#include "jemalloc/internal/extent_mmap.h" #include "jemalloc/internal/extent_mmap.h"
#include "jemalloc/internal/mutex.h" #include "jemalloc/internal/mutex.h"
#include "jemalloc/internal/rtree.h" #include "jemalloc/internal/rtree.h"
#include "jemalloc/internal/safety_check.h"
#include "jemalloc/internal/util.h" #include "jemalloc/internal/util.h"
JEMALLOC_DIAGNOSTIC_DISABLE_SPURIOUS JEMALLOC_DIAGNOSTIC_DISABLE_SPURIOUS
@ -1531,12 +1532,16 @@ arena_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment,
} }
void void
arena_prof_promote(tsdn_t *tsdn, const void *ptr, size_t usize) { arena_prof_promote(tsdn_t *tsdn, void *ptr, size_t usize) {
cassert(config_prof); cassert(config_prof);
assert(ptr != NULL); assert(ptr != NULL);
assert(isalloc(tsdn, ptr) == SC_LARGE_MINCLASS); assert(isalloc(tsdn, ptr) == SC_LARGE_MINCLASS);
assert(usize <= SC_SMALL_MAXCLASS); assert(usize <= SC_SMALL_MAXCLASS);
if (config_opt_safety_checks) {
safety_check_set_redzone(ptr, usize, SC_LARGE_MINCLASS);
}
rtree_ctx_t rtree_ctx_fallback; rtree_ctx_t rtree_ctx_fallback;
rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback); rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
@ -1577,10 +1582,19 @@ arena_dalloc_promoted(tsdn_t *tsdn, void *ptr, tcache_t *tcache,
assert(opt_prof); assert(opt_prof);
extent_t *extent = iealloc(tsdn, ptr); extent_t *extent = iealloc(tsdn, ptr);
size_t usize = arena_prof_demote(tsdn, extent, ptr); size_t usize = extent_usize_get(extent);
if (usize <= tcache_maxclass) { size_t bumped_usize = arena_prof_demote(tsdn, extent, ptr);
if (config_opt_safety_checks && usize < SC_LARGE_MINCLASS) {
/*
* Currently, we only do redzoning for small sampled
* allocations.
*/
assert(bumped_usize == SC_LARGE_MINCLASS);
safety_check_verify_redzone(ptr, usize, bumped_usize);
}
if (bumped_usize <= tcache_maxclass) {
tcache_dalloc_large(tsdn_tsd(tsdn), tcache, ptr, tcache_dalloc_large(tsdn_tsd(tsdn), tcache, ptr,
sz_size2index(usize), slow_path); sz_size2index(bumped_usize), slow_path);
} else { } else {
large_dalloc(tsdn, extent); large_dalloc(tsdn, extent);
} }

View File

@ -13,6 +13,7 @@
#include "jemalloc/internal/malloc_io.h" #include "jemalloc/internal/malloc_io.h"
#include "jemalloc/internal/mutex.h" #include "jemalloc/internal/mutex.h"
#include "jemalloc/internal/rtree.h" #include "jemalloc/internal/rtree.h"
#include "jemalloc/internal/safety_check.h"
#include "jemalloc/internal/sc.h" #include "jemalloc/internal/sc.h"
#include "jemalloc/internal/spin.h" #include "jemalloc/internal/spin.h"
#include "jemalloc/internal/sz.h" #include "jemalloc/internal/sz.h"

View File

@ -1,11 +1,24 @@
#include "jemalloc/internal/jemalloc_preamble.h" #include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/jemalloc_internal_includes.h" #include "jemalloc/internal/jemalloc_internal_includes.h"
void safety_check_fail(const char *format, ...) { static void (*safety_check_abort)(const char *message);
va_list ap;
va_start(ap, format); void safety_check_set_abort(void (*abort_fn)(const char *)) {
malloc_vcprintf(NULL, NULL, format, ap); safety_check_abort = abort_fn;
va_end(ap); }
abort();
void safety_check_fail(const char *format, ...) {
char buf[MALLOC_PRINTF_BUFSIZE];
va_list ap;
va_start(ap, format);
malloc_vsnprintf(buf, MALLOC_PRINTF_BUFSIZE, format, ap);
va_end(ap);
if (safety_check_abort == NULL) {
malloc_write(buf);
abort();
} else {
safety_check_abort(buf);
}
} }

156
test/unit/safety_check.c Normal file
View File

@ -0,0 +1,156 @@
#include "test/jemalloc_test.h"
#include "jemalloc/internal/safety_check.h"
/*
* Note that we get called through safety_check.sh, which turns on sampling for
* everything.
*/
bool fake_abort_called;
void fake_abort(const char *message) {
(void)message;
fake_abort_called = true;
}
TEST_BEGIN(test_malloc_free_overflow) {
test_skip_if(!config_prof);
test_skip_if(!config_opt_safety_checks);
safety_check_set_abort(&fake_abort);
/* Buffer overflow! */
char* ptr = malloc(128);
ptr[128] = 0;
free(ptr);
safety_check_set_abort(NULL);
assert_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
fake_abort_called = false;
}
TEST_END
TEST_BEGIN(test_mallocx_dallocx_overflow) {
test_skip_if(!config_prof);
test_skip_if(!config_opt_safety_checks);
safety_check_set_abort(&fake_abort);
/* Buffer overflow! */
char* ptr = mallocx(128, 0);
ptr[128] = 0;
dallocx(ptr, 0);
safety_check_set_abort(NULL);
assert_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
fake_abort_called = false;
}
TEST_END
TEST_BEGIN(test_malloc_sdallocx_overflow) {
test_skip_if(!config_prof);
test_skip_if(!config_opt_safety_checks);
safety_check_set_abort(&fake_abort);
/* Buffer overflow! */
char* ptr = malloc(128);
ptr[128] = 0;
sdallocx(ptr, 128, 0);
safety_check_set_abort(NULL);
assert_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
fake_abort_called = false;
}
TEST_END
TEST_BEGIN(test_realloc_overflow) {
test_skip_if(!config_prof);
test_skip_if(!config_opt_safety_checks);
safety_check_set_abort(&fake_abort);
/* Buffer overflow! */
char* ptr = malloc(128);
ptr[128] = 0;
ptr = realloc(ptr, 129);
safety_check_set_abort(NULL);
free(ptr);
assert_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
fake_abort_called = false;
}
TEST_END
TEST_BEGIN(test_rallocx_overflow) {
test_skip_if(!config_prof);
test_skip_if(!config_opt_safety_checks);
safety_check_set_abort(&fake_abort);
/* Buffer overflow! */
char* ptr = malloc(128);
ptr[128] = 0;
ptr = rallocx(ptr, 129, 0);
safety_check_set_abort(NULL);
free(ptr);
assert_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
fake_abort_called = false;
}
TEST_END
TEST_BEGIN(test_xallocx_overflow) {
test_skip_if(!config_prof);
test_skip_if(!config_opt_safety_checks);
safety_check_set_abort(&fake_abort);
/* Buffer overflow! */
char* ptr = malloc(128);
ptr[128] = 0;
size_t result = xallocx(ptr, 129, 0, 0);
assert_zu_eq(result, 128, "");
free(ptr);
assert_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
fake_abort_called = false;
safety_check_set_abort(NULL);
}
TEST_END
TEST_BEGIN(test_realloc_no_overflow) {
char* ptr = malloc(128);
ptr = realloc(ptr, 256);
ptr[128] = 0;
ptr[255] = 0;
free(ptr);
ptr = malloc(128);
ptr = realloc(ptr, 64);
ptr[63] = 0;
ptr[0] = 0;
free(ptr);
}
TEST_END
TEST_BEGIN(test_rallocx_no_overflow) {
char* ptr = malloc(128);
ptr = rallocx(ptr, 256, 0);
ptr[128] = 0;
ptr[255] = 0;
free(ptr);
ptr = malloc(128);
ptr = rallocx(ptr, 64, 0);
ptr[63] = 0;
ptr[0] = 0;
free(ptr);
}
TEST_END
int
main(void) {
return test(
test_malloc_free_overflow,
test_mallocx_dallocx_overflow,
test_malloc_sdallocx_overflow,
test_realloc_overflow,
test_rallocx_overflow,
test_xallocx_overflow,
test_realloc_no_overflow,
test_rallocx_no_overflow);
}

View File

@ -0,0 +1,5 @@
#!/bin/sh
if [ "x${enable_prof}" = "x1" ] ; then
export MALLOC_CONF="prof:true,lg_prof_sample:0"
fi