#include "mimalloc.h"
#include "mimalloc/internal.h"
#include "mimalloc/prim-tls.h"
mi_decl_export void mi_theap_guarded_set_sample_rate(mi_theap_t* theap, size_t sample_rate, size_t seed) {
theap->guarded_sample_rate = (sample_rate > MI_SAMPLE_RATE_MAX ? MI_SAMPLE_RATE_MAX : sample_rate);
if (theap->guarded_sample_rate > 1) {
if (seed == 0) {
seed = _mi_theap_random_next(theap);
}
theap->guarded_sample_countdown = (seed % theap->guarded_sample_rate) + 1; }
else if (theap->guarded_sample_countdown > theap->guarded_sample_rate) {
theap->guarded_sample_countdown = theap->guarded_sample_rate;
}
_mi_theap_update_sample_rate(theap);
}
mi_decl_export void mi_theap_guarded_set_size_bound(mi_theap_t* theap, size_t min, size_t max) {
theap->guarded_size_min = min;
theap->guarded_size_max = (min > max ? min : max);
}
void _mi_theap_guarded_init(mi_theap_t* theap) {
#if MI_GUARDED
mi_theap_guarded_set_sample_rate(theap,
(size_t)mi_option_get_clamp(mi_option_guarded_sample_rate, 0, LONG_MAX),
(size_t)mi_option_get(mi_option_guarded_sample_seed));
mi_theap_guarded_set_size_bound(theap,
(size_t)mi_option_get_clamp(mi_option_guarded_min, 0, LONG_MAX),
(size_t)mi_option_get_clamp(mi_option_guarded_max, 0, LONG_MAX) );
#else
mi_theap_guarded_set_sample_rate(theap,0,0);
#endif
}
static void* mi_block_ptr_set_guarded(mi_block_t* block, size_t obj_size, size_t* usable_size) {
mi_page_t* const page = _mi_ptr_page(block);
mi_page_set_has_interior_pointers(page, true);
block->next = MI_BLOCK_TAG_GUARDED;
const size_t block_size = mi_page_block_size(page); const size_t os_page_size = _mi_os_page_size();
mi_assert_internal(block_size >= obj_size + os_page_size + sizeof(mi_block_t));
if (block_size < obj_size + os_page_size + sizeof(mi_block_t)) {
mi_free(block);
return NULL;
}
uint8_t* guard_page = (uint8_t*)block + block_size - os_page_size;
mi_assert_internal(_mi_is_aligned(block, os_page_size));
mi_assert_internal(_mi_is_aligned(guard_page, os_page_size));
if (!page->memid.is_pinned && _mi_is_aligned(guard_page, os_page_size)) {
const bool ok = _mi_os_protect(guard_page, os_page_size);
if mi_unlikely(!ok) {
_mi_warning_message("failed to set a guard page behind an object (object %p of size %zu)\n", block, block_size);
}
}
else {
_mi_warning_message("unable to set a guard page behind an object due to pinned memory (large OS pages?) (object %p of size %zu)\n", block, block_size);
}
size_t offset = block_size - os_page_size - obj_size;
mi_assert_internal(offset > sizeof(mi_block_t));
if (offset > MI_PAGE_MAX_OVERALLOC_ALIGN) {
offset = MI_PAGE_MAX_OVERALLOC_ALIGN;
}
uint8_t* const p = (uint8_t*)block + offset;
mi_assert_internal(p == guard_page - obj_size || offset >= MI_PAGE_MAX_OVERALLOC_ALIGN);
if (usable_size != NULL) { *usable_size = (guard_page - p); mi_assert_internal(mi_usable_size(p)==*usable_size); }
mi_track_align(block, p, offset, obj_size);
mi_track_mem_defined(block, sizeof(mi_block_t));
return p;
}
mi_decl_restrict void* _mi_theap_malloc_guarded(mi_theap_t* theap, size_t size, bool zero, mi_page_t** ppage) mi_attr_noexcept
{
if mi_unlikely(size >= MI_MAX_ALLOC_SIZE - MI_PADDING_SIZE) { _mi_error_message(EOVERFLOW, "(guarded) allocation request is too large (%zu bytes)\n", size);
return NULL;
}
const size_t os_page_size = _mi_os_page_size();
const size_t obj_size = (mi_option_is_enabled(mi_option_guarded_precise) ? size : _mi_align_up(size, MI_MAX_ALIGN_SIZE));
const size_t bsize = _mi_align_up(_mi_align_up(obj_size, MI_MAX_ALIGN_SIZE) + sizeof(mi_block_t), MI_MAX_ALIGN_SIZE);
const size_t req_size = _mi_align_up(bsize + os_page_size, os_page_size);
mi_block_t* const block = (mi_block_t*)_mi_malloc_generic_no_sample(theap, req_size, false , ppage);
if (block==NULL) return NULL;
size_t usable_size = 0;
void* const p = mi_block_ptr_set_guarded(block, obj_size, &usable_size);
if (p == NULL) return NULL;
if (zero) {
_mi_memzero(p,obj_size); }
mi_track_malloc(p, usable_size, zero);
if (!mi_theap_is_initialized(theap)) { theap = _mi_theap_default(); }
mi_theap_stat_counter_increase(theap, malloc_guarded_count, 1);
#if MI_STATS
mi_theap_stat_counter_decrease(theap, malloc_requested, req_size);
mi_theap_stat_counter_increase(theap, malloc_requested, size);
#endif
#if MI_DEBUG>3
if (zero) {
mi_assert_expensive(mi_mem_is_zero(p, size));
}
#endif
#if MI_PAGE_META_SMALL_IS_ALIGNED && MI_DEBUG>=2
if (size <= MI_SMALL_SIZE_MAX) {
mi_page_t* const page = _mi_ptr_page(p);
mi_assert_internal(mi_page_block_size(page) <= MI_SMALL_MAX_OBJ_SIZE);
}
#endif
return p;
}
#if MI_GUARDED
void _mi_page_block_unguard(mi_page_t* page, mi_block_t* block, void* p) {
mi_assert_internal(mi_block_ptr_is_guarded(block, p));
mi_assert_internal(mi_page_has_interior_pointers(page));
mi_assert_internal((uint8_t*)p - (uint8_t*)block >= (ptrdiff_t)sizeof(mi_block_t));
mi_assert_internal(block->next == MI_BLOCK_TAG_GUARDED);
const size_t bsize = mi_page_block_size(page);
const size_t psize = _mi_os_page_size();
mi_assert_internal(bsize > psize);
mi_assert_internal(!page->memid.is_pinned);
void* gpage = (uint8_t*)block + bsize - psize;
mi_assert_internal(_mi_is_aligned(gpage, psize));
_mi_os_unprotect(gpage, psize);
}
void _mi_page_unguard_all(mi_page_t* page) {
if mi_likely(!mi_page_has_interior_pointers(page)) return;
uint8_t* const start = mi_page_start(page);
const size_t psize = mi_page_committed(page);
_mi_os_unprotect(start,psize); }
#else
void _mi_page_block_unguard(mi_page_t* page, mi_block_t* block, void* p) {
MI_UNUSED(page); MI_UNUSED(block); MI_UNUSED(p);
}
void _mi_page_unguard_all(mi_page_t* page) {
MI_UNUSED(page);
}
#endif