initial progress on valgrind integration

This commit is contained in:
daan 2022-10-28 19:54:56 -07:00
parent eb29d6b06f
commit 6eeb81ee05
4 changed files with 82 additions and 9 deletions

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__)
@ -625,21 +626,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) {

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

@ -0,0 +1,41 @@
/* ----------------------------------------------------------------------------
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.
// ------------------------------------------------------
#define MI_VALGRIND 1
#if MI_VALGRIND
#include <valgrind/valgrind.h>
#include <valgrind/memcheck.h>
#define MI_TRACK_ZALLOC(p,size,zero) VALGRIND_MALLOCLIKE_BLOCK(p,size,0 /*red zone*/,zero)
#define MI_TRACK_MALLOC(p,size) MI_TRACK_ZALLOC(p,size,false)
#define MI_TRACK_FREE(p) VALGRIND_FREELIKE_BLOCK(p,0 /*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_ZALLOC(p,size,zero)
#define MI_TRACK_MALLOC(p,size)
#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

@ -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,6 +38,9 @@ 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 internally
MI_TRACK_MEM_UNDEFINED(block,mi_page_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)
@ -73,6 +77,7 @@ extern inline void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t siz
for (size_t i = 0; i < maxpad; i++) { fill[i] = MI_DEBUG_PADDING; } for (size_t i = 0; i < maxpad; i++) { fill[i] = MI_DEBUG_PADDING; }
#endif #endif
MI_TRACK_MEM_NOACCESS(block,mi_page_block_size(page));
return block; return block;
} }
@ -94,6 +99,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_ZALLOC(p,size,zero);
return p; return p;
} }
@ -122,6 +128,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_ZALLOC(p,size,zero);
return p; return p;
} }
} }
@ -176,16 +183,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) {
@ -203,8 +213,11 @@ static inline bool mi_check_is_double_free(const mi_page_t* page, const mi_block
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(*padding));
*delta = padding->delta; *delta = padding->delta;
return ((uint32_t)mi_ptr_encode(page,block,page->keys) == padding->canary && *delta <= *bsize); bool ok = ((uint32_t)mi_ptr_encode(page,block,page->keys) == padding->canary && *delta <= *bsize);
MI_TRACK_MEM_NOACCESS(padding,sizeof(*padding));
return ok;
} }
// Return the exact usable size of a block. // Return the exact usable size of a block.
@ -212,7 +225,7 @@ static size_t mi_page_usable_size_of(const mi_page_t* page, const mi_block_t* bl
size_t bsize; size_t bsize;
size_t delta; size_t delta;
bool ok = mi_page_decode_padding(page, block, &delta, &bsize); 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); return (ok ? bsize - delta : 0);
} }
@ -226,13 +239,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) {
@ -465,6 +481,7 @@ void mi_free(void* p) mi_attr_noexcept
{ {
mi_segment_t* const segment = mi_checked_ptr_segment(p,"mi_free"); mi_segment_t* const segment = mi_checked_ptr_segment(p,"mi_free");
if mi_unlikely(segment == NULL) return; if mi_unlikely(segment == NULL) return;
MI_TRACK_FREE(p);
mi_threadid_t tid = _mi_thread_id(); mi_threadid_t tid = _mi_thread_id();
mi_page_t* const page = _mi_segment_page_of(segment, p); mi_page_t* const page = _mi_segment_page_of(segment, p);
@ -472,10 +489,11 @@ void mi_free(void* p) mi_attr_noexcept
if mi_likely(tid == mi_atomic_load_relaxed(&segment->thread_id) && page->flags.full_aligned == 0) { // the thread id matches and it is not a full page, nor has aligned blocks if mi_likely(tid == mi_atomic_load_relaxed(&segment->thread_id) && page->flags.full_aligned == 0) { // the thread id matches and it is not a full page, nor has aligned blocks
// local, and not full or aligned // local, and not full or aligned
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_MEM_UNDEFINED(block,mi_page_block_size(page));
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);
@ -487,8 +505,10 @@ void mi_free(void* p) mi_attr_noexcept
else { else {
// non-local, aligned blocks, or a full page; use the more generic path // non-local, aligned blocks, or a full page; use the more generic path
// note: recalc page in generic to improve code generation // note: recalc page in generic to improve code generation
MI_TRACK_MEM_UNDEFINED(block,mi_page_block_size(page));
mi_free_generic(segment, tid == segment->thread_id, p); mi_free_generic(segment, tid == segment->thread_id, p);
} }
MI_TRACK_MEM_NOACCESS(block,mi_page_block_size(page));
} }
bool _mi_free_delayed_block(mi_block_t* block) { bool _mi_free_delayed_block(mi_block_t* block) {

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)