potential fix for use-after-free of the tld on unsafe arenas destroy

This commit is contained in:
Daan 2025-06-17 19:49:09 -07:00
parent 99976d6c2b
commit aaf8da9aba
3 changed files with 9 additions and 10 deletions

View file

@ -223,7 +223,7 @@ void* _mi_arenas_alloc_aligned(mi_subproc_t* subproc, size_t size, size_
void _mi_arenas_free(void* p, size_t size, mi_memid_t memid);
bool _mi_arenas_contain(const void* p);
void _mi_arenas_collect(bool force_purge, bool visit_all, mi_tld_t* tld);
void _mi_arenas_unsafe_destroy_all(mi_tld_t* tld);
void _mi_arenas_unsafe_destroy_all(mi_subproc_t* subproc);
mi_page_t* _mi_arenas_page_alloc(mi_heap_t* heap, size_t block_size, size_t page_alignment);
void _mi_arenas_page_free(mi_page_t* page, mi_tld_t* tld);

View file

@ -993,7 +993,7 @@ void _mi_arenas_page_unabandon(mi_page_t* page) {
Arena free
----------------------------------------------------------- */
static void mi_arena_schedule_purge(mi_arena_t* arena, size_t slice_index, size_t slices);
static void mi_arenas_try_purge(bool force, bool visit_all, mi_tld_t* tld);
static void mi_arenas_try_purge(bool force, bool visit_all, mi_subproc_t* subproc, size_t tseq);
void _mi_arenas_free(void* p, size_t size, mi_memid_t memid) {
if (p==NULL) return;
@ -1054,7 +1054,7 @@ void _mi_arenas_free(void* p, size_t size, mi_memid_t memid) {
// Purge the arenas; if `force_purge` is true, amenable parts are purged even if not yet expired
void _mi_arenas_collect(bool force_purge, bool visit_all, mi_tld_t* tld) {
mi_arenas_try_purge(force_purge, visit_all, tld);
mi_arenas_try_purge(force_purge, visit_all, tld->subproc, tld->thread_seq);
}
@ -1109,9 +1109,9 @@ static void mi_arenas_unsafe_destroy(mi_subproc_t* subproc) {
// destroy owned arenas; this is unsafe and should only be done using `mi_option_destroy_on_exit`
// for dynamic libraries that are unloaded and need to release all their allocated memory.
void _mi_arenas_unsafe_destroy_all(mi_tld_t* tld) {
mi_arenas_unsafe_destroy(tld->subproc);
_mi_arenas_collect(true /* force purge */, true /* visit all*/, tld); // purge non-owned arenas
void _mi_arenas_unsafe_destroy_all(mi_subproc_t* subproc) {
mi_arenas_unsafe_destroy(subproc);
mi_arenas_try_purge(true /* force purge */, true /* visit all*/, subproc, 0 /* thread seq */); // purge non-owned arenas
}
@ -1775,14 +1775,13 @@ static bool mi_arena_try_purge(mi_arena_t* arena, mi_msecs_t now, bool force)
}
static void mi_arenas_try_purge(bool force, bool visit_all, mi_tld_t* tld)
static void mi_arenas_try_purge(bool force, bool visit_all, mi_subproc_t* subproc, size_t tseq)
{
// try purge can be called often so try to only run when needed
const long delay = mi_arena_purge_delay();
if (_mi_preloading() || delay <= 0) return; // nothing will be scheduled
// check if any arena needs purging?
mi_subproc_t* subproc = tld->subproc;
const mi_msecs_t now = _mi_clock_now();
const mi_msecs_t arenas_expire = mi_atomic_loadi64_acquire(&subproc->purge_expire);
if (!visit_all && !force && (arenas_expire == 0 || arenas_expire > now)) return;
@ -1796,7 +1795,7 @@ static void mi_arenas_try_purge(bool force, bool visit_all, mi_tld_t* tld)
{
// increase global expire: at most one purge per delay cycle
if (arenas_expire > now) { mi_atomic_storei64_release(&subproc->purge_expire, now + (delay/10)); }
const size_t arena_start = tld->thread_seq % max_arena;
const size_t arena_start = tseq % max_arena;
size_t max_purge_count = (visit_all ? max_arena : (max_arena/4)+1);
bool all_visited = true;
bool any_purged = false;

View file

@ -778,7 +778,7 @@ void mi_cdecl mi_process_done(void) mi_attr_noexcept {
if (mi_option_is_enabled(mi_option_destroy_on_exit)) {
mi_heap_collect(heap, true /* force */);
_mi_heap_unsafe_destroy_all(heap); // forcefully release all memory held by all heaps (of this thread only!)
_mi_arenas_unsafe_destroy_all(heap->tld);
_mi_arenas_unsafe_destroy_all(_mi_subproc_main());
_mi_page_map_unsafe_destroy(_mi_subproc_main());
}
//_mi_page_map_unsafe_destroy(_mi_subproc_main());