merge from dev-track

This commit is contained in:
daan 2022-10-30 14:31:21 -07:00
commit 66525ccae3
15 changed files with 221 additions and 45 deletions

View File

@ -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_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_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_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_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_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_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_SEE_ASM "Generate assembly files" OFF)
option(MI_OSX_INTERPOSE "Use interpose to override standard malloc on macOS" ON) 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_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) option(MI_USE_LIBATOMIC "Explicitly link with -latomic (on older systems) (deprecated and detected automatically)" OFF)
include(CheckIncludeFiles)
include(GNUInstallDirs) include(GNUInstallDirs)
include("cmake/mimalloc-config-version.cmake") include("cmake/mimalloc-config-version.cmake")
@ -106,6 +108,21 @@ endif()
if(MI_SECURE) if(MI_SECURE)
message(STATUS "Set full secure build (MI_SECURE=ON)") message(STATUS "Set full secure build (MI_SECURE=ON)")
list(APPEND mi_defines MI_SECURE=4) 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() endif()
if(MI_SEE_ASM) if(MI_SEE_ASM)
@ -251,16 +268,18 @@ else()
set(mi_install_cmakedir "${CMAKE_INSTALL_LIBDIR}/cmake/mimalloc-${mi_version}") # for cmake package info set(mi_install_cmakedir "${CMAKE_INSTALL_LIBDIR}/cmake/mimalloc-${mi_version}") # for cmake package info
endif() endif()
if(MI_SECURE)
set(mi_basename "mimalloc-secure")
else()
set(mi_basename "mimalloc") set(mi_basename "mimalloc")
if(MI_SECURE)
set(mi_basename "${mi_basename}-secure")
endif()
if(MI_VALGRIND)
set(mi_basename "${mi_basename}-valgrind")
endif() endif()
string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LC) string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LC)
if(NOT(CMAKE_BUILD_TYPE_LC MATCHES "^(release|relwithdebinfo|minsizerel|none)$")) 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 set(mi_basename "${mi_basename}-${CMAKE_BUILD_TYPE_LC}") #append build type (e.g. -debug) if not a release version
endif() endif()
if(MI_BUILD_SHARED) if(MI_BUILD_SHARED)
list(APPEND mi_build_targets "shared") list(APPEND mi_build_targets "shared")
endif() endif()

View File

@ -9,6 +9,7 @@ terms of the MIT license. A copy of the license can be found in the file
#define MIMALLOC_INTERNAL_H #define MIMALLOC_INTERNAL_H
#include "mimalloc-types.h" #include "mimalloc-types.h"
#include "mimalloc-track.h"
#if (MI_DEBUG>0) #if (MI_DEBUG>0)
#define mi_trace_message(...) _mi_trace_message(__VA_ARGS__) #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 ) { 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 #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 #else
MI_UNUSED(keys); MI_UNUSED(null); MI_UNUSED(keys); MI_UNUSED(null);
return (mi_block_t*)block->next; next = (mi_block_t*)block->next;
#endif #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) { 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 #ifdef MI_ENCODE_FREELIST
block->next = mi_ptr_encode(null, next, keys); block->next = mi_ptr_encode(null, next, keys);
#else #else
MI_UNUSED(keys); MI_UNUSED(null); MI_UNUSED(keys); MI_UNUSED(null);
block->next = (mi_encoded_t)next; block->next = (mi_encoded_t)next;
#endif #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) { 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. // (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 <intrin.h>
#include <string.h> #include <string.h>
extern bool _mi_cpu_has_fsrm; extern bool _mi_cpu_has_fsrm;

43
include/mimalloc-track.h Normal file
View File

@ -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

View File

@ -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 in the release version to disable assertions.
// #define NDEBUG // #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 as 1 to maintain statistics; set it to 2 to have detailed statistics (but costs some performance).
// #define MI_STAT 1 // #define MI_STAT 1
@ -56,14 +59,14 @@ 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. // 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. // 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 #define MI_PADDING 1
#endif #endif
// Encoded free lists allow detection of corrupted free lists // Encoded free lists allow detection of corrupted free lists
// and can detect buffer overflows, modify after free, and double `free`s. // and can detect buffer overflows, modify after free, and double `free`s.
#if (MI_SECURE>=3 || MI_DEBUG>=1 || MI_PADDING > 0) #if (MI_SECURE>=3 || MI_DEBUG>=1)
#define MI_ENCODE_FREELIST 1 #define MI_ENCODE_FREELIST 1
#endif #endif

View File

@ -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); 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(((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)); 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; 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 void* p = _mi_page_malloc(heap, page, padsize, zero); // TODO: inline _mi_page_malloc
mi_assert_internal(p != NULL); mi_assert_internal(p != NULL);
mi_assert_internal(((uintptr_t)p + offset) % alignment == 0); mi_assert_internal(((uintptr_t)p + offset) % alignment == 0);
mi_track_malloc(p,size,zero);
return p; return p;
} }
} }

View File

@ -29,7 +29,7 @@ typedef struct mi_nothrow_s { int _tag; } mi_nothrow_t;
// Override system malloc // 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 // gcc, clang: use aliasing to alias the exported function to one of our `mi_` functions
#if (defined(__GNUC__) && __GNUC__ >= 9) #if (defined(__GNUC__) && __GNUC__ >= 9)
#pragma GCC diagnostic ignored "-Wattributes" // or we get warnings that nodiscard is ignored on a forward #pragma GCC diagnostic ignored "-Wattributes" // or we get warnings that nodiscard is ignored on a forward
@ -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. // we just override new/delete which does work in a static library.
#else #else
// On all other systems forward to our API // On all other systems forward to our API
void* malloc(size_t size) MI_FORWARD1(mi_malloc, size) mi_decl_export void* malloc(size_t size) MI_FORWARD1(mi_malloc, size)
void* calloc(size_t size, size_t n) MI_FORWARD2(mi_calloc, size, n) mi_decl_export 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) mi_decl_export 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 free(void* p) MI_FORWARD0(mi_free, p)
#endif #endif
#if (defined(__GNUC__) || defined(__clang__)) && !defined(__APPLE__) #if (defined(__GNUC__) || defined(__clang__)) && !defined(__APPLE__)

View File

@ -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-internal.h"
#include "mimalloc-atomic.h" #include "mimalloc-atomic.h"
#include <string.h> // memset, strlen #include <string.h> // memset, strlen
#include <stdlib.h> // malloc, exit #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); page->free = mi_block_next(page, block);
mi_assert_internal(page->free == NULL || _mi_ptr_page(page->free) == page); 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) // zero the block? note: we need to zero the full block size (issue #63)
if mi_unlikely(zero) { if mi_unlikely(zero) {
mi_assert_internal(page->xblock_size != 0); // do not call with zero'ing for huge blocks (see _mi_malloc_generic) 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); const size_t zsize = (page->is_zero ? sizeof(block->next) + MI_PADDING_SIZE : page->xblock_size);
_mi_memzero_aligned(block, zsize); _mi_memzero_aligned(block, zsize - MI_PADDING_SIZE);
} }
#if (MI_DEBUG>0) #if (MI_DEBUG>0) && !MI_TRACK_ENABLED
if (!page->is_zero && !zero) { memset(block, MI_DEBUG_UNINIT, size); } if (!page->is_zero && !zero) { memset(block, MI_DEBUG_UNINIT, mi_page_usable_block_size(page)); }
#elif (MI_SECURE!=0) #elif (MI_SECURE!=0)
if (!zero) { block->next = 0; } // don't leak internal data if (!zero) { block->next = 0; } // don't leak internal data
#endif #endif
@ -62,10 +68,13 @@ extern inline void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t siz
} }
#endif #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)); 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)); 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_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->canary = (uint32_t)(mi_ptr_encode(page,block,page->keys));
padding->delta = (uint32_t)(delta); padding->delta = (uint32_t)(delta);
uint8_t* fill = (uint8_t*)padding - 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)); mi_heap_stat_increase(heap, malloc, mi_usable_size(p));
} }
#endif #endif
mi_track_malloc(p,size,zero);
return p; 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)); mi_heap_stat_increase(heap, malloc, mi_usable_size(p));
} }
#endif #endif
mi_track_malloc(p,size,zero);
return p; return p;
} }
} }
@ -176,16 +187,19 @@ static mi_decl_noinline bool mi_check_is_double_freex(const mi_page_t* page, con
return false; 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) { 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 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? 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? (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? // 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) // (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 #else
static inline bool mi_check_is_double_free(const mi_page_t* page, const mi_block_t* block) { 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 // 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) { 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); *bsize = mi_page_usable_block_size(page);
const mi_padding_t* const padding = (mi_padding_t*)((uint8_t*)block + *bsize); 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; *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. // Return the exact usable size of a block.
@ -226,13 +247,16 @@ static bool mi_verify_padding(const mi_page_t* page, const mi_block_t* block, si
*size = bsize - delta; *size = bsize - delta;
uint8_t* fill = (uint8_t*)block + 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 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++) { for (size_t i = 0; i < maxpad; i++) {
if (fill[i] != MI_DEBUG_PADDING) { if (fill[i] != MI_DEBUG_PADDING) {
*wrong = bsize - delta + i; *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) { 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 // 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) 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. // 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). // 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_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 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) #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)); memset(block, MI_DEBUG_FREED, mi_usable_size(block));
#endif #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) static inline void _mi_free_block(mi_page_t* page, bool local, mi_block_t* block)
{ {
// and push it on the free list // and push it on the free list
//const size_t bsize = mi_page_block_size(page);
if mi_likely(local) { if mi_likely(local) {
// owning thread can free a block directly // owning thread can free a block directly
if mi_unlikely(mi_check_is_double_free(page, block)) return; if mi_unlikely(mi_check_is_double_free(page, block)) return;
mi_check_padding(page, block); 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)); memset(block, MI_DEBUG_FREED, mi_page_block_size(page));
#endif #endif
mi_block_set_next(page, block, page->local_free); mi_block_set_next(page, block, page->local_free);
@ -428,7 +453,8 @@ 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 { 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_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_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_stat_free(page, block); // stat_free may access the padding
mi_track_free(p);
_mi_free_block(page, local, block); _mi_free_block(page, local, block);
} }
@ -481,9 +507,10 @@ void mi_free(void* p) mi_attr_noexcept
if mi_unlikely(mi_check_is_double_free(page,block)) return; if mi_unlikely(mi_check_is_double_free(page,block)) return;
mi_check_padding(page, block); mi_check_padding(page, block);
mi_stat_free(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)); memset(block, MI_DEBUG_FREED, mi_page_block_size(page));
#endif #endif
mi_track_free(p);
mi_block_set_next(page, block, page->local_free); mi_block_set_next(page, block, page->local_free);
page->local_free = block; 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)) if mi_unlikely(--page->used == 0) { // using this expression generates better code than: page->used--; if (mi_page_all_free(page))
@ -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)). // 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.) // (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) 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) 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? // todo: adjust potential padding to reflect the new size?
return p; // reallocation still fits and not more than 50% waste return p; // reallocation still fits and not more than 50% waste
} }
#endif
void* newp = mi_heap_malloc(heap,newsize); void* newp = mi_heap_malloc(heap,newsize);
if mi_likely(newp != NULL) { if mi_likely(newp != NULL) {
if (zero && newsize > size) { if (zero && newsize > size) {

View File

@ -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); else _mi_stat_decrease(&stats->reset, csize);
if (!reset) return true; // nothing to do on unreset! if (!reset) return true; // nothing to do on unreset!
#if (MI_DEBUG>1) #if (MI_DEBUG>1) && !MI_TRACK_ENABLED
if (MI_SECURE==0) { if (MI_SECURE==0) {
memset(start, 0, csize); // pretend it is eagerly reset memset(start, 0, csize); // pretend it is eagerly reset
} }

View File

@ -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); 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 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; 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(mi_page_block_size(page) <= page_size);
mi_assert_internal(page_size <= page->slice_count*MI_SEGMENT_SLICE_SIZE); mi_assert_internal(page_size <= page->slice_count*MI_SEGMENT_SLICE_SIZE);
mi_assert_internal(page_size / block_size < (1L<<16)); mi_assert_internal(page_size / block_size < (1L<<16));

View File

@ -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)); 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; } if (*commit) { ((uint8_t*)p)[0] = 0; }
#endif #endif
@ -376,7 +376,7 @@ void* _mi_mem_alloc_aligned(size_t size, size_t alignment, bool* commit, bool* l
if (p != NULL) { if (p != NULL) {
mi_assert_internal((uintptr_t)p % alignment == 0); 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 if (*commit) { ((uint8_t*)p)[0] = 0; } // ensure the memory is committed
#endif #endif
} }

View File

@ -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 if (!ok) return NULL; // failed to commit
mi_commit_mask_set(&commit_mask, &commit_needed_mask); mi_commit_mask_set(&commit_mask, &commit_needed_mask);
} }
mi_track_mem_undefined(segment,commit_needed);
segment->memid = memid; segment->memid = memid;
segment->mem_is_pinned = is_pinned; segment->mem_is_pinned = is_pinned;
segment->mem_is_large = mem_large; segment->mem_is_large = mem_large;

View File

@ -47,3 +47,8 @@ target_link_libraries(static-override PUBLIC mimalloc-static)
add_executable(static-override-cxx main-override.cpp) add_executable(static-override-cxx main-override.cpp)
target_link_libraries(static-override-cxx PUBLIC mimalloc-static) 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)

View File

@ -309,6 +309,10 @@ bool check_zero_init(uint8_t* p, size_t size) {
#if MI_DEBUG >= 2 #if MI_DEBUG >= 2
bool check_debug_fill_uninit(uint8_t* p, size_t size) { 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) if(!p)
return false; return false;
@ -317,9 +321,14 @@ bool check_debug_fill_uninit(uint8_t* p, size_t size) {
result &= p[i] == MI_DEBUG_UNINIT; result &= p[i] == MI_DEBUG_UNINIT;
} }
return result; return result;
#endif
} }
bool check_debug_fill_freed(uint8_t* p, size_t size) { 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) if(!p)
return false; return false;
@ -328,5 +337,6 @@ bool check_debug_fill_freed(uint8_t* p, size_t size) {
result &= p[i] == MI_DEBUG_FREED; result &= p[i] == MI_DEBUG_FREED;
} }
return result; return result;
#endif
} }
#endif #endif

View File

@ -72,7 +72,9 @@ int main(void) {
result = (mi_calloc((size_t)&mi_calloc,SIZE_MAX/1000) == NULL); result = (mi_calloc((size_t)&mi_calloc,SIZE_MAX/1000) == NULL);
}; };
CHECK_BODY("calloc0") { 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. CHECK_BODY("malloc-large") { // see PR #544.
void* p = mi_malloc(67108872); void* p = mi_malloc(67108872);

49
test/test-wrong.c Normal file
View File

@ -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;
}