summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordaan <daan@effp.org>2022-10-30 14:31:21 -0700
committerdaan <daan@effp.org>2022-10-30 14:31:21 -0700
commit66525ccae3e9225ff80accfa6d124042509839f2 (patch)
tree7ccf5747a252df2d1c22a6aa4cc64db0941de2dc
parent3d6017de7c1338bebbb9a4c0e7b8329af202b2e6 (diff)
parent74d002b61ceaa9d26d5fb731ba5b4bbb764b98a7 (diff)
merge from dev-track
-rw-r--r--CMakeLists.txt29
-rw-r--r--include/mimalloc-internal.h13
-rw-r--r--include/mimalloc-track.h43
-rw-r--r--include/mimalloc-types.h9
-rw-r--r--src/alloc-aligned.c6
-rw-r--r--src/alloc-override.c12
-rw-r--r--src/alloc.c71
-rw-r--r--src/os.c2
-rw-r--r--src/page.c4
-rw-r--r--src/region.c8
-rw-r--r--src/segment.c1
-rw-r--r--test/CMakeLists.txt5
-rw-r--r--test/test-api-fill.c10
-rw-r--r--test/test-api.c4
-rw-r--r--test/test-wrong.c49
15 files changed, 221 insertions, 45 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8127e09..76544a0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -6,10 +6,11 @@ set(CMAKE_CXX_STANDARD 17)
option(MI_SECURE "Use full security mitigations (like guard pages, allocation randomization, double-free mitigation, and free-list corruption detection)" OFF)
option(MI_DEBUG_FULL "Use full internal heap invariant checking in DEBUG mode (expensive)" OFF)
-option(MI_PADDING "Enable padding to detect heap block overflow (used only in DEBUG mode)" ON)
+option(MI_PADDING "Enable padding to detect heap block overflow (used only in DEBUG mode or with Valgrind)" ON)
option(MI_OVERRIDE "Override the standard malloc interface (e.g. define entry points for malloc() etc)" ON)
option(MI_XMALLOC "Enable abort() call on memory allocation failure by default" OFF)
option(MI_SHOW_ERRORS "Show error and warning messages by default (only enabled by default in DEBUG mode)" OFF)
+option(MI_VALGRIND "Compile with Valgrind support (adds a small overhead)" OFF)
option(MI_USE_CXX "Use the C++ compiler to compile the library (instead of the C compiler)" OFF)
option(MI_SEE_ASM "Generate assembly files" OFF)
option(MI_OSX_INTERPOSE "Use interpose to override standard malloc on macOS" ON)
@@ -28,6 +29,7 @@ option(MI_CHECK_FULL "Use full internal invariant checking in DEBUG mode
option(MI_INSTALL_TOPLEVEL "Install directly into $CMAKE_INSTALL_PREFIX instead of PREFIX/lib/mimalloc-version (deprecated)" OFF)
option(MI_USE_LIBATOMIC "Explicitly link with -latomic (on older systems) (deprecated and detected automatically)" OFF)
+include(CheckIncludeFiles)
include(GNUInstallDirs)
include("cmake/mimalloc-config-version.cmake")
@@ -106,6 +108,21 @@ endif()
if(MI_SECURE)
message(STATUS "Set full secure build (MI_SECURE=ON)")
list(APPEND mi_defines MI_SECURE=4)
+ #if (MI_VALGRIND)
+ # message(WARNING "Secure mode is a bit weakened when compiling with Valgrind support as buffer overflow detection is no longer byte-precise (if running without valgrind)")
+ #endif()
+endif()
+
+if(MI_VALGRIND)
+ CHECK_INCLUDE_FILES("valgrind/valgrind.h;valgrind/memcheck.h" MI_HAS_VALGRINDH)
+ if (NOT MI_HAS_VALGRINDH)
+ set(MI_VALGRIND OFF)
+ message(WARNING "Cannot find the 'valgrind/valgrind.h' and 'valgrind/memcheck.h' -- install valgrind first")
+ message(STATUS "Compile **without** Valgrind support (MI_VALGRIND=OFF)")
+ else()
+ message(STATUS "Compile with Valgrind support (MI_VALGRIND=ON)")
+ list(APPEND mi_defines MI_VALGRIND=1)
+ endif()
endif()
if(MI_SEE_ASM)
@@ -251,16 +268,18 @@ else()
set(mi_install_cmakedir "${CMAKE_INSTALL_LIBDIR}/cmake/mimalloc-${mi_version}") # for cmake package info
endif()
+set(mi_basename "mimalloc")
if(MI_SECURE)
- set(mi_basename "mimalloc-secure")
-else()
- set(mi_basename "mimalloc")
+ set(mi_basename "${mi_basename}-secure")
+endif()
+if(MI_VALGRIND)
+ set(mi_basename "${mi_basename}-valgrind")
endif()
-
string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LC)
if(NOT(CMAKE_BUILD_TYPE_LC MATCHES "^(release|relwithdebinfo|minsizerel|none)$"))
set(mi_basename "${mi_basename}-${CMAKE_BUILD_TYPE_LC}") #append build type (e.g. -debug) if not a release version
endif()
+
if(MI_BUILD_SHARED)
list(APPEND mi_build_targets "shared")
endif()
diff --git a/include/mimalloc-internal.h b/include/mimalloc-internal.h
index 2e1fee4..d545398 100644
--- a/include/mimalloc-internal.h
+++ b/include/mimalloc-internal.h
@@ -9,6 +9,7 @@ terms of the MIT license. A copy of the license can be found in the file
#define MIMALLOC_INTERNAL_H
#include "mimalloc-types.h"
+#include "mimalloc-track.h"
#if (MI_DEBUG>0)
#define mi_trace_message(...) _mi_trace_message(__VA_ARGS__)
@@ -668,21 +669,27 @@ static inline mi_encoded_t mi_ptr_encode(const void* null, const void* p, const
}
static inline mi_block_t* mi_block_nextx( const void* null, const mi_block_t* block, const uintptr_t* keys ) {
+ mi_track_mem_defined(block,sizeof(mi_block_t));
+ mi_block_t* next;
#ifdef MI_ENCODE_FREELIST
- return (mi_block_t*)mi_ptr_decode(null, block->next, keys);
+ next = (mi_block_t*)mi_ptr_decode(null, block->next, keys);
#else
MI_UNUSED(keys); MI_UNUSED(null);
- return (mi_block_t*)block->next;
+ next = (mi_block_t*)block->next;
#endif
+ mi_track_mem_noaccess(block,sizeof(mi_block_t));
+ return next;
}
static inline void mi_block_set_nextx(const void* null, mi_block_t* block, const mi_block_t* next, const uintptr_t* keys) {
+ mi_track_mem_undefined(block,sizeof(mi_block_t));
#ifdef MI_ENCODE_FREELIST
block->next = mi_ptr_encode(null, next, keys);
#else
MI_UNUSED(keys); MI_UNUSED(null);
block->next = (mi_encoded_t)next;
#endif
+ mi_track_mem_noaccess(block,sizeof(mi_block_t));
}
static inline mi_block_t* mi_block_next(const mi_page_t* page, const mi_block_t* block) {
@@ -1013,7 +1020,7 @@ static inline size_t mi_bsr(uintptr_t x) {
// (AMD Zen3+ (~2020) or Intel Ice Lake+ (~2017). See also issue #201 and pr #253.
// ---------------------------------------------------------------------------------
-#if defined(_WIN32) && (defined(_M_IX86) || defined(_M_X64))
+#if !MI_TRACK_ENABLED && defined(_WIN32) && (defined(_M_IX86) || defined(_M_X64))
#include <intrin.h>
#include <string.h>
extern bool _mi_cpu_has_fsrm;
diff --git a/include/mimalloc-track.h b/include/mimalloc-track.h
new file mode 100644
index 0000000..bb9df4f
--- /dev/null
+++ b/include/mimalloc-track.h
@@ -0,0 +1,43 @@
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018-2021, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+#pragma once
+#ifndef MIMALLOC_TRACK_H
+#define MIMALLOC_TRACK_H
+
+// ------------------------------------------------------
+// Track memory ranges with macros for tools like Valgrind
+// or other memory checkers.
+// ------------------------------------------------------
+
+#if MI_VALGRIND
+
+#define MI_TRACK_ENABLED 1
+
+#include <valgrind/valgrind.h>
+#include <valgrind/memcheck.h>
+
+#define mi_track_malloc(p,size,zero) VALGRIND_MALLOCLIKE_BLOCK(p,size,MI_PADDING_SIZE /*red zone*/,zero)
+#define mi_track_resize(p,oldsize,newsize) VALGRIND_RESIZEINPLACE_BLOCK(p,oldsize,newsize,MI_PADDING_SIZE /*red zone*/)
+#define mi_track_free(p) VALGRIND_FREELIKE_BLOCK(p,MI_PADDING_SIZE /*red zone*/)
+#define mi_track_mem_defined(p,size) VALGRIND_MAKE_MEM_DEFINED(p,size)
+#define mi_track_mem_undefined(p,size) VALGRIND_MAKE_MEM_UNDEFINED(p,size)
+#define mi_track_mem_noaccess(p,size) VALGRIND_MAKE_MEM_NOACCESS(p,size)
+
+#else
+
+#define MI_TRACK_ENABLED 0
+
+#define mi_track_malloc(p,size,zero)
+#define mi_track_resize(p,oldsize,newsize)
+#define mi_track_free(p)
+#define mi_track_mem_defined(p,size)
+#define mi_track_mem_undefined(p,size)
+#define mi_track_mem_noaccess(p,size)
+
+#endif
+
+#endif
diff --git a/include/mimalloc-types.h b/include/mimalloc-types.h
index fb75ea4..b55d57f 100644
--- a/include/mimalloc-types.h
+++ b/include/mimalloc-types.h
@@ -29,6 +29,9 @@ terms of the MIT license. A copy of the license can be found in the file
// Define NDEBUG in the release version to disable assertions.
// #define NDEBUG
+// Define MI_VALGRIND to enable valgrind support
+// #define MI_VALGRIND 1
+
// Define MI_STAT as 1 to maintain statistics; set it to 2 to have detailed statistics (but costs some performance).
// #define MI_STAT 1
@@ -56,15 +59,15 @@ terms of the MIT license. A copy of the license can be found in the file
// Reserve extra padding at the end of each block to be more resilient against heap block overflows.
// The padding can detect byte-precise buffer overflow on free.
-#if !defined(MI_PADDING) && (MI_DEBUG>=1)
+#if !defined(MI_PADDING) && (MI_DEBUG>=1 || MI_VALGRIND)
#define MI_PADDING 1
#endif
// Encoded free lists allow detection of corrupted free lists
// and can detect buffer overflows, modify after free, and double `free`s.
-#if (MI_SECURE>=3 || MI_DEBUG>=1 || MI_PADDING > 0)
-#define MI_ENCODE_FREELIST 1
+#if (MI_SECURE>=3 || MI_DEBUG>=1)
+#define MI_ENCODE_FREELIST 1
#endif
diff --git a/src/alloc-aligned.c b/src/alloc-aligned.c
index 2c8d8f3..bc26e29 100644
--- a/src/alloc-aligned.c
+++ b/src/alloc-aligned.c
@@ -41,6 +41,11 @@ static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_fallback(mi_heap_t*
if (aligned_p != p) mi_page_set_has_aligned(_mi_ptr_page(p), true);
mi_assert_internal(((uintptr_t)aligned_p + offset) % alignment == 0);
mi_assert_internal(p == _mi_page_ptr_unalign(_mi_ptr_segment(aligned_p), _mi_ptr_page(aligned_p), aligned_p));
+
+ if (p != aligned_p) {
+ mi_track_free(p);
+ mi_track_malloc(aligned_p,size,zero);
+ }
return aligned_p;
}
@@ -82,6 +87,7 @@ static void* mi_heap_malloc_zero_aligned_at(mi_heap_t* const heap, const size_t
void* p = _mi_page_malloc(heap, page, padsize, zero); // TODO: inline _mi_page_malloc
mi_assert_internal(p != NULL);
mi_assert_internal(((uintptr_t)p + offset) % alignment == 0);
+ mi_track_malloc(p,size,zero);
return p;
}
}
diff --git a/src/alloc-override.c b/src/alloc-override.c
index e29cb4b..9534e9d 100644
--- a/src/alloc-override.c
+++ b/src/alloc-override.c
@@ -29,7 +29,7 @@ typedef struct mi_nothrow_s { int _tag; } mi_nothrow_t;
// Override system malloc
// ------------------------------------------------------
-#if (defined(__GNUC__) || defined(__clang__)) && !defined(__APPLE__) && !defined(MI_VALGRIND)
+#if (defined(__GNUC__) || defined(__clang__)) && !defined(__APPLE__) && !MI_TRACK_ENABLED
// gcc, clang: use aliasing to alias the exported function to one of our `mi_` functions
#if (defined(__GNUC__) && __GNUC__ >= 9)
#pragma GCC diagnostic ignored "-Wattributes" // or we get warnings that nodiscard is ignored on a forward
@@ -44,7 +44,7 @@ typedef struct mi_nothrow_s { int _tag; } mi_nothrow_t;
#define MI_FORWARD02(fun,x,y) MI_FORWARD(fun)
#else
// otherwise use forwarding by calling our `mi_` function
- #define MI_FORWARD1(fun,x) { return fun(x); }
+ #define MI_FORWARD1(fun,x) { return fun(x); }
#define MI_FORWARD2(fun,x,y) { return fun(x,y); }
#define MI_FORWARD3(fun,x,y,z) { return fun(x,y,z); }
#define MI_FORWARD0(fun,x) { fun(x); }
@@ -123,10 +123,10 @@ typedef struct mi_nothrow_s { int _tag; } mi_nothrow_t;
// we just override new/delete which does work in a static library.
#else
// On all other systems forward to our API
- void* malloc(size_t size) MI_FORWARD1(mi_malloc, size)
- void* calloc(size_t size, size_t n) MI_FORWARD2(mi_calloc, size, n)
- void* realloc(void* p, size_t newsize) MI_FORWARD2(mi_realloc, p, newsize)
- void free(void* p) MI_FORWARD0(mi_free, p)
+ mi_decl_export void* malloc(size_t size) MI_FORWARD1(mi_malloc, size)
+ mi_decl_export void* calloc(size_t size, size_t n) MI_FORWARD2(mi_calloc, size, n)
+ mi_decl_export void* realloc(void* p, size_t newsize) MI_FORWARD2(mi_realloc, p, newsize)
+ mi_decl_export void free(void* p) MI_FORWARD0(mi_free, p)
#endif
#if (defined(__GNUC__) || defined(__clang__)) && !defined(__APPLE__)
diff --git a/src/alloc.c b/src/alloc.c
index 3892e8d..2ee41d7 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -12,6 +12,7 @@ terms of the MIT license. A copy of the license can be found in the file
#include "mimalloc-internal.h"
#include "mimalloc-atomic.h"
+
#include <string.h> // memset, strlen
#include <stdlib.h> // malloc, exit
@@ -37,15 +38,20 @@ extern inline void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t siz
page->free = mi_block_next(page, block);
mi_assert_internal(page->free == NULL || _mi_ptr_page(page->free) == page);
+ // allow use of the block internally
+ // note: when tracking we need to avoid ever touching the MI_PADDING since
+ // that is tracked by valgrind etc. as non-accessible (through the red-zone, see `mimalloc-track.h`)
+ mi_track_mem_undefined(block, mi_page_usable_block_size(page));
+
// zero the block? note: we need to zero the full block size (issue #63)
if mi_unlikely(zero) {
mi_assert_internal(page->xblock_size != 0); // do not call with zero'ing for huge blocks (see _mi_malloc_generic)
- const size_t zsize = (page->is_zero ? sizeof(block->next) : page->xblock_size);
- _mi_memzero_aligned(block, zsize);
+ const size_t zsize = (page->is_zero ? sizeof(block->next) + MI_PADDING_SIZE : page->xblock_size);
+ _mi_memzero_aligned(block, zsize - MI_PADDING_SIZE);
}
-#if (MI_DEBUG>0)
- if (!page->is_zero && !zero) { memset(block, MI_DEBUG_UNINIT, size); }
+#if (MI_DEBUG>0) && !MI_TRACK_ENABLED
+ if (!page->is_zero && !zero) { memset(block, MI_DEBUG_UNINIT, mi_page_usable_block_size(page)); }
#elif (MI_SECURE!=0)
if (!zero) { block->next = 0; } // don't leak internal data
#endif
@@ -62,10 +68,13 @@ extern inline void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t siz
}
#endif
-#if (MI_PADDING > 0) && defined(MI_ENCODE_FREELIST)
+#if (MI_PADDING > 0) && defined(MI_ENCODE_FREELIST) && !MI_TRACK_ENABLED
mi_padding_t* const padding = (mi_padding_t*)((uint8_t*)block + mi_page_usable_block_size(page));
ptrdiff_t delta = ((uint8_t*)padding - (uint8_t*)block - (size - MI_PADDING_SIZE));
+ #if (MI_DEBUG>1)
mi_assert_internal(delta >= 0 && mi_page_usable_block_size(page) >= (size - MI_PADDING_SIZE + delta));
+ mi_track_mem_defined(padding,sizeof(mi_padding_t)); // note: re-enable since mi_page_usable_block_size may set noaccess
+ #endif
padding->canary = (uint32_t)(mi_ptr_encode(page,block,page->keys));
padding->delta = (uint32_t)(delta);
uint8_t* fill = (uint8_t*)padding - delta;
@@ -94,6 +103,7 @@ static inline mi_decl_restrict void* mi_heap_malloc_small_zero(mi_heap_t* heap,
mi_heap_stat_increase(heap, malloc, mi_usable_size(p));
}
#endif
+ mi_track_malloc(p,size,zero);
return p;
}
@@ -122,6 +132,7 @@ extern inline void* _mi_heap_malloc_zero(mi_heap_t* heap, size_t size, bool zero
mi_heap_stat_increase(heap, malloc, mi_usable_size(p));
}
#endif
+ mi_track_malloc(p,size,zero);
return p;
}
}
@@ -176,16 +187,19 @@ static mi_decl_noinline bool mi_check_is_double_freex(const mi_page_t* page, con
return false;
}
+#define mi_track_page(page,access) { size_t psize; void* pstart = _mi_page_start(_mi_page_segment(page),page,&psize); mi_track_mem_##access( pstart, psize); }
+
static inline bool mi_check_is_double_free(const mi_page_t* page, const mi_block_t* block) {
+ bool is_double_free = false;
mi_block_t* n = mi_block_nextx(page, block, page->keys); // pretend it is freed, and get the decoded first field
if (((uintptr_t)n & (MI_INTPTR_SIZE-1))==0 && // quick check: aligned pointer?
(n==NULL || mi_is_in_same_page(block, n))) // quick check: in same page or NULL?
{
// Suspicous: decoded value a in block is in the same page (or NULL) -- maybe a double free?
// (continue in separate function to improve code generation)
- return mi_check_is_double_freex(page, block);
+ is_double_free = mi_check_is_double_freex(page, block);
}
- return false;
+ return is_double_free;
}
#else
static inline bool mi_check_is_double_free(const mi_page_t* page, const mi_block_t* block) {
@@ -199,12 +213,19 @@ static inline bool mi_check_is_double_free(const mi_page_t* page, const mi_block
// Check for heap block overflow by setting up padding at the end of the block
// ---------------------------------------------------------------------------
-#if (MI_PADDING>0) && defined(MI_ENCODE_FREELIST)
+#if (MI_PADDING>0) && defined(MI_ENCODE_FREELIST) && !MI_TRACK_ENABLED
static bool mi_page_decode_padding(const mi_page_t* page, const mi_block_t* block, size_t* delta, size_t* bsize) {
*bsize = mi_page_usable_block_size(page);
const mi_padding_t* const padding = (mi_padding_t*)((uint8_t*)block + *bsize);
+ mi_track_mem_defined(padding,sizeof(mi_padding_t));
*delta = padding->delta;
- return ((uint32_t)mi_ptr_encode(page,block,page->keys) == padding->canary && *delta <= *bsize);
+ uint32_t canary = padding->canary;
+ uintptr_t keys[2];
+ keys[0] = page->keys[0];
+ keys[1] = page->keys[1];
+ bool ok = ((uint32_t)mi_ptr_encode(page,block,keys) == canary && *delta <= *bsize);
+ mi_track_mem_noaccess(padding,sizeof(mi_padding_t));
+ return ok;
}
// Return the exact usable size of a block.
@@ -212,7 +233,7 @@ static size_t mi_page_usable_size_of(const mi_page_t* page, const mi_block_t* bl
size_t bsize;
size_t delta;
bool ok = mi_page_decode_padding(page, block, &delta, &bsize);
- mi_assert_internal(ok); mi_assert_internal(delta <= bsize);
+ mi_assert_internal(ok); mi_assert_internal(delta <= bsize);
return (ok ? bsize - delta : 0);
}
@@ -226,13 +247,16 @@ static bool mi_verify_padding(const mi_page_t* page, const mi_block_t* block, si
*size = bsize - delta;
uint8_t* fill = (uint8_t*)block + bsize - delta;
const size_t maxpad = (delta > MI_MAX_ALIGN_SIZE ? MI_MAX_ALIGN_SIZE : delta); // check at most the first N padding bytes
+ mi_track_mem_defined(fill,maxpad);
for (size_t i = 0; i < maxpad; i++) {
if (fill[i] != MI_DEBUG_PADDING) {
*wrong = bsize - delta + i;
- return false;
+ ok = false;
+ break;
}
}
- return true;
+ mi_track_mem_noaccess(fill,maxpad);
+ return ok;
}
static void mi_check_padding(const mi_page_t* page, const mi_block_t* block) {
@@ -331,14 +355,14 @@ static void mi_stat_huge_free(const mi_page_t* page) {
// Free
// ------------------------------------------------------
-// multi-threaded free
+// multi-threaded free (or free in huge block)
static mi_decl_noinline void _mi_free_block_mt(mi_page_t* page, mi_block_t* block)
{
// The padding check may access the non-thread-owned page for the key values.
// that is safe as these are constant and the page won't be freed (as the block is not freed yet).
mi_check_padding(page, block);
- mi_padding_shrink(page, block, sizeof(mi_block_t)); // for small size, ensure we can fit the delayed thread pointers without triggering overflow detection
- #if (MI_DEBUG!=0)
+ mi_padding_shrink(page, block, sizeof(mi_block_t)); // for small size, ensure we can fit the delayed thread pointers without triggering overflow detection
+ #if (MI_DEBUG!=0) && !MI_TRACK_ENABLED // note: when tracking, cannot use mi_usable_size with multi-threading
memset(block, MI_DEBUG_FREED, mi_usable_size(block));
#endif
@@ -393,11 +417,12 @@ static mi_decl_noinline void _mi_free_block_mt(mi_page_t* page, mi_block_t* bloc
static inline void _mi_free_block(mi_page_t* page, bool local, mi_block_t* block)
{
// and push it on the free list
+ //const size_t bsize = mi_page_block_size(page);
if mi_likely(local) {
// owning thread can free a block directly
if mi_unlikely(mi_check_is_double_free(page, block)) return;
mi_check_padding(page, block);
- #if (MI_DEBUG!=0)
+ #if (MI_DEBUG!=0) && !MI_TRACK_ENABLED
memset(block, MI_DEBUG_FREED, mi_page_block_size(page));
#endif
mi_block_set_next(page, block, page->local_free);
@@ -428,8 +453,9 @@ mi_block_t* _mi_page_ptr_unalign(const mi_segment_t* segment, const mi_page_t* p
static void mi_decl_noinline mi_free_generic(const mi_segment_t* segment, bool local, void* p) mi_attr_noexcept {
mi_page_t* const page = _mi_segment_page_of(segment, p);
mi_block_t* const block = (mi_page_has_aligned(page) ? _mi_page_ptr_unalign(segment, page, p) : (mi_block_t*)p);
- mi_stat_free(page, block);
- _mi_free_block(page, local, block);
+ mi_stat_free(page, block); // stat_free may access the padding
+ mi_track_free(p);
+ _mi_free_block(page, local, block);
}
// Get the segment data belonging to a pointer
@@ -481,20 +507,21 @@ void mi_free(void* p) mi_attr_noexcept
if mi_unlikely(mi_check_is_double_free(page,block)) return;
mi_check_padding(page, block);
mi_stat_free(page, block);
- #if (MI_DEBUG!=0)
+ #if (MI_DEBUG!=0) && !MI_TRACK_ENABLED
memset(block, MI_DEBUG_FREED, mi_page_block_size(page));
#endif
+ mi_track_free(p);
mi_block_set_next(page, block, page->local_free);
page->local_free = block;
if mi_unlikely(--page->used == 0) { // using this expression generates better code than: page->used--; if (mi_page_all_free(page))
_mi_page_retire(page);
- }
+ }
}
else {
// non-local, aligned blocks, or a full page; use the more generic path
// note: recalc page in generic to improve code generation
mi_free_generic(segment, tid == segment->thread_id, p);
- }
+ }
}
bool _mi_free_delayed_block(mi_block_t* block) {
@@ -627,10 +654,12 @@ void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero)
// else if size == 0 then reallocate to a zero-sized block (and don't return NULL, just as mi_malloc(0)).
// (this means that returning NULL always indicates an error, and `p` will not have been freed in that case.)
const size_t size = _mi_usable_size(p,"mi_realloc"); // also works if p == NULL (with size 0)
+ #if !MI_TRACK_ENABLED
if mi_unlikely(newsize <= size && newsize >= (size / 2) && newsize > 0) { // note: newsize must be > 0 or otherwise we return NULL for realloc(NULL,0)
// todo: adjust potential padding to reflect the new size?
return p; // reallocation still fits and not more than 50% waste
}
+ #endif
void* newp = mi_heap_malloc(heap,newsize);
if mi_likely(newp != NULL) {
if (zero && newsize > size) {
diff --git a/src/os.c b/src/os.c
index 1a806aa..c9b00e0 100644
--- a/src/os.c
+++ b/src/os.c
@@ -986,7 +986,7 @@ static bool mi_os_resetx(void* addr, size_t size, bool reset, mi_stats_t* stats)
else _mi_stat_decrease(&stats->reset, csize);
if (!reset) return true; // nothing to do on unreset!
- #if (MI_DEBUG>1)
+ #if (MI_DEBUG>1) && !MI_TRACK_ENABLED
if (MI_SECURE==0) {
memset(start, 0, csize); // pretend it is eagerly reset
}
diff --git a/src/page.c b/src/page.c
index 73a9725..192ec57 100644
--- a/src/page.c
+++ b/src/page.c
@@ -619,7 +619,9 @@ static void mi_page_init(mi_heap_t* heap, mi_page_t* page, size_t block_size, mi
mi_page_set_heap(page, heap);
page->xblock_size = (block_size < MI_HUGE_BLOCK_SIZE ? (uint32_t)block_size : MI_HUGE_BLOCK_SIZE); // initialize before _mi_segment_page_start
size_t page_size;
- _mi_segment_page_start(segment, page, &page_size);
+ const void* page_start = _mi_segment_page_start(segment, page, &page_size);
+ MI_UNUSED(page_start);
+ mi_track_mem_noaccess(page_start,page_size);
mi_assert_internal(mi_page_block_size(page) <= page_size);
mi_assert_internal(page_size <= page->slice_count*MI_SEGMENT_SLICE_SIZE);
mi_assert_internal(page_size / block_size < (1L<<16));
diff --git a/src/region.c b/src/region.c
index 72ce849..54b7be0 100644
--- a/src/region.c
+++ b/src/region.c
@@ -330,7 +330,7 @@ static void* mi_region_try_alloc(size_t blocks, bool* commit, bool* large, bool*
}
mi_assert_internal(!_mi_bitmap_is_any_claimed(&region->reset, 1, blocks, bit_idx));
- #if (MI_DEBUG>=2)
+ #if (MI_DEBUG>=2) && !MI_TRACK_ENABLED
if (*commit) { ((uint8_t*)p)[0] = 0; }
#endif
@@ -376,9 +376,9 @@ void* _mi_mem_alloc_aligned(size_t size, size_t alignment, bool* commit, bool* l
if (p != NULL) {
mi_assert_internal((uintptr_t)p % alignment == 0);
-#if (MI_DEBUG>=2)
+ #if (MI_DEBUG>=2) && !MI_TRACK_ENABLED
if (*commit) { ((uint8_t*)p)[0] = 0; } // ensure the memory is committed
-#endif
+ #endif
}
return p;
}
@@ -395,7 +395,7 @@ void _mi_mem_free(void* p, size_t size, size_t id, bool full_commit, bool any_re
if (p==NULL) return;
if (size==0) return;
size = _mi_align_up(size, _mi_os_page_size());
-
+
size_t arena_memid = 0;
mi_bitmap_index_t bit_idx;
mem_region_t* region;
diff --git a/src/segment.c b/src/segment.c
index 63dd5ac..07b9c4f 100644
--- a/src/segment.c
+++ b/src/segment.c
@@ -817,6 +817,7 @@ static mi_segment_t* mi_segment_init(mi_segment_t* segment, size_t required, mi_
if (!ok) return NULL; // failed to commit
mi_commit_mask_set(&commit_mask, &commit_needed_mask);
}
+ mi_track_mem_undefined(segment,commit_needed);
segment->memid = memid;
segment->mem_is_pinned = is_pinned;
segment->mem_is_large = mem_large;
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index bb8dc97..e76ffa6 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -47,3 +47,8 @@ target_link_libraries(static-override PUBLIC mimalloc-static)
add_executable(static-override-cxx main-override.cpp)
target_link_libraries(static-override-cxx PUBLIC mimalloc-static)
+
+
+## test memory errors
+add_executable(test-wrong test-wrong.c)
+target_link_libraries(test-wrong PUBLIC mimalloc)
diff --git a/test/test-api-fill.c b/test/test-api-fill.c
index ef50acc..c205637 100644
--- a/test/test-api-fill.c
+++ b/test/test-api-fill.c
@@ -309,6 +309,10 @@ bool check_zero_init(uint8_t* p, size_t size) {
#if MI_DEBUG >= 2
bool check_debug_fill_uninit(uint8_t* p, size_t size) {
+#if MI_VALGRIND
+ (void)p; (void)size;
+ return true; // when compiled with valgrind we don't init on purpose
+#else
if(!p)
return false;
@@ -317,9 +321,14 @@ bool check_debug_fill_uninit(uint8_t* p, size_t size) {
result &= p[i] == MI_DEBUG_UNINIT;
}
return result;
+#endif
}
bool check_debug_fill_freed(uint8_t* p, size_t size) {
+#if MI_VALGRIND
+ (void)p; (void)size;
+ return true; // when compiled with valgrind we don't fill on purpose
+#else
if(!p)
return false;
@@ -328,5 +337,6 @@ bool check_debug_fill_freed(uint8_t* p, size_t size) {
result &= p[i] == MI_DEBUG_FREED;
}
return result;
+#endif
}
#endif
diff --git a/test/test-api.c b/test/test-api.c
index c56fb88..1d427c0 100644
--- a/test/test-api.c
+++ b/test/test-api.c
@@ -72,7 +72,9 @@ int main(void) {
result = (mi_calloc((size_t)&mi_calloc,SIZE_MAX/1000) == NULL);
};
CHECK_BODY("calloc0") {
- result = (mi_usable_size(mi_calloc(0,1000)) <= 16);
+ void* p = mi_calloc(0,1000);
+ result = (mi_usable_size(p) <= 16);
+ mi_free(p);
};
CHECK_BODY("malloc-large") { // see PR #544.
void* p = mi_malloc(67108872);
diff --git a/test/test-wrong.c b/test/test-wrong.c
new file mode 100644
index 0000000..bb55600
--- /dev/null
+++ b/test/test-wrong.c
@@ -0,0 +1,49 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "mimalloc.h"
+
+#ifdef USE_STD_MALLOC
+# define mi(x) x
+#else
+# define mi(x) mi_##x
+#endif
+
+int main(int argc, char** argv) {
+ int* p = mi(malloc)(3*sizeof(int));
+
+ int* r = mi_malloc_aligned(8,16);
+ mi_free(r);
+
+ // illegal byte wise read
+ char* c = (char*)mi(malloc)(3);
+ printf("invalid byte: over: %d, under: %d\n", c[4], c[-1]);
+ mi(free)(c);
+
+ // undefined access
+ int* q = mi(malloc)(sizeof(int));
+ printf("undefined: %d\n", *q);
+
+ // illegal int read
+ printf("invalid: over: %d, under: %d\n", q[1], q[-1]);
+
+ *q = 42;
+
+ // buffer overflow
+ q[1] = 43;
+
+ // buffer underflow
+ q[-1] = 44;
+
+ mi(free)(q);
+
+
+ // double free
+ mi(free)(q);
+
+ // use after free
+ printf("use-after-free: %d\n", *q);
+
+ // leak p
+ // mi_free(p)
+ return 0;
+} \ No newline at end of file