Skip to content

Commit b1188ea

Browse files
committed
fix potential race on subproc field in the segment
1 parent 76b0873 commit b1188ea

File tree

5 files changed

+19
-21
lines changed

5 files changed

+19
-21
lines changed

include/mimalloc/types.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -397,9 +397,10 @@ typedef struct mi_segment_s {
397397
bool allow_decommit;
398398
bool allow_purge;
399399
size_t segment_size; // for huge pages this may be different from `MI_SEGMENT_SIZE`
400+
mi_subproc_t* subproc; // segment belongs to sub process
400401

401402
// segment fields
402-
struct mi_segment_s* next; // must be the first segment field after abandoned_next -- see `segment.c:segment_init`
403+
struct mi_segment_s* next; // must be the first (non-constant) segment field -- see `segment.c:segment_init`
403404
struct mi_segment_s* prev;
404405
bool was_reclaimed; // true if it was reclaimed (used to limit on-free reclamation)
405406

@@ -410,7 +411,6 @@ typedef struct mi_segment_s {
410411
size_t capacity; // count of available pages (`#free + used`)
411412
size_t segment_info_size;// space we are using from the first page for segment meta-data and possible guard pages.
412413
uintptr_t cookie; // verify addresses in secure mode: `_mi_ptr_cookie(segment) == segment->cookie`
413-
mi_subproc_t* subproc; // segment belongs to sub process
414414

415415
struct mi_segment_s* abandoned_os_next; // only used for abandoned segments outside arena's, and only if `mi_option_visit_abandoned` is enabled
416416
struct mi_segment_s* abandoned_os_prev;

src/arena-abandoned.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,9 @@ void _mi_arena_segment_mark_abandoned(mi_segment_t* segment)
162162
mi_arena_t* arena = mi_arena_from_index(arena_idx);
163163
mi_assert_internal(arena != NULL);
164164
// set abandonment atomically
165+
mi_subproc_t* const subproc = segment->subproc; // don't access the segment after setting it abandoned
165166
const bool was_unmarked = _mi_bitmap_claim(arena->blocks_abandoned, arena->field_count, 1, bitmap_idx, NULL);
166-
if (was_unmarked) { mi_atomic_increment_relaxed(&segment->subproc->abandoned_count); }
167+
if (was_unmarked) { mi_atomic_increment_relaxed(&subproc->abandoned_count); }
167168
mi_assert_internal(was_unmarked);
168169
mi_assert_internal(_mi_bitmap_is_claimed(arena->blocks_inuse, arena->field_count, 1, bitmap_idx));
169170
}

src/init.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const mi_page_t _mi_page_empty = {
3434
MI_ATOMIC_VAR_INIT(0), // xthread_free
3535
MI_ATOMIC_VAR_INIT(0), // xheap
3636
NULL, NULL
37-
#if MI_INTPTR_SIZE==4
37+
#if MI_INTPTR_SIZE==4
3838
, { NULL }
3939
#endif
4040
};
@@ -129,7 +129,7 @@ static mi_decl_cache_align mi_subproc_t mi_subproc_default;
129129

130130
static mi_decl_cache_align mi_tld_t tld_main = {
131131
0, false,
132-
&_mi_heap_main, &_mi_heap_main,
132+
&_mi_heap_main, &_mi_heap_main,
133133
{ { NULL, NULL }, {NULL ,NULL}, {NULL ,NULL, 0},
134134
0, 0, 0, 0, 0, &mi_subproc_default,
135135
&tld_main.stats, &tld_main.os
@@ -171,7 +171,7 @@ static void mi_heap_main_init(void) {
171171
#endif
172172
_mi_heap_main.cookie = _mi_heap_random_next(&_mi_heap_main);
173173
_mi_heap_main.keys[0] = _mi_heap_random_next(&_mi_heap_main);
174-
_mi_heap_main.keys[1] = _mi_heap_random_next(&_mi_heap_main);
174+
_mi_heap_main.keys[1] = _mi_heap_random_next(&_mi_heap_main);
175175
mi_lock_init(&mi_subproc_default.abandoned_os_lock);
176176
mi_lock_init(&mi_subproc_default.abandoned_os_visit_lock);
177177
}
@@ -341,7 +341,7 @@ static bool _mi_thread_heap_init(void) {
341341
mi_heap_t* heap = &td->heap;
342342
_mi_tld_init(tld, heap); // must be before `_mi_heap_init`
343343
_mi_heap_init(heap, tld, _mi_arena_id_none(), false /* can reclaim */, 0 /* default tag */);
344-
_mi_heap_set_default_direct(heap);
344+
_mi_heap_set_default_direct(heap);
345345
}
346346
return false;
347347
}

src/segment.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,7 @@ static void mi_segment_os_free(mi_segment_t* segment, size_t segment_size, mi_se
512512
_mi_arena_free(segment, segment_size, committed_size, segment->memid, tld->stats);
513513
}
514514

515-
// called from `heap_collect`.
515+
// called from `heap_collect`.
516516
void _mi_segments_collect(bool force, mi_segments_tld_t* tld) {
517517
mi_pages_try_purge(force,tld);
518518
#if MI_DEBUG>=2
@@ -563,6 +563,7 @@ static mi_segment_t* mi_segment_os_alloc(bool eager_delayed, size_t page_alignme
563563
segment->allow_decommit = !memid.is_pinned;
564564
segment->allow_purge = segment->allow_decommit && (mi_option_get(mi_option_purge_delay) >= 0);
565565
segment->segment_size = segment_size;
566+
segment->subproc = tld->subproc;
566567
mi_segments_track_size((long)(segment_size), tld);
567568
_mi_segment_map_allocated_at(segment);
568569
return segment;
@@ -628,7 +629,6 @@ static mi_segment_t* mi_segment_alloc(size_t required, mi_page_kind_t page_kind,
628629
segment->segment_info_size = pre_size;
629630
segment->thread_id = _mi_thread_id();
630631
segment->cookie = _mi_ptr_cookie(segment);
631-
segment->subproc = tld->subproc;
632632

633633
// set protection
634634
mi_segment_protect(segment, true, tld->os);
@@ -896,7 +896,7 @@ static mi_segment_t* mi_segment_reclaim(mi_segment_t* segment, mi_heap_t* heap,
896896
segment->abandoned--;
897897
mi_assert(page->next == NULL);
898898
_mi_stat_decrease(&tld->stats->pages_abandoned, 1);
899-
// get the target heap for this thread which has a matching heap tag (so we reclaim into a matching heap)
899+
// get the target heap for this thread which has a matching heap tag (so we reclaim into a matching heap)
900900
mi_heap_t* target_heap = _mi_heap_by_tag(heap, page->heap_tag); // allow custom heaps to separate objects
901901
if (target_heap == NULL) {
902902
target_heap = heap;
@@ -961,7 +961,7 @@ bool _mi_segment_attempt_reclaim(mi_heap_t* heap, mi_segment_t* segment) {
961961

962962
void _mi_abandoned_reclaim_all(mi_heap_t* heap, mi_segments_tld_t* tld) {
963963
mi_segment_t* segment;
964-
mi_arena_field_cursor_t current;
964+
mi_arena_field_cursor_t current;
965965
_mi_arena_field_cursor_init(heap, tld->subproc, true /* visit all, blocking */, &current);
966966
while ((segment = _mi_arena_segment_clear_abandoned_next(&current)) != NULL) {
967967
mi_segment_reclaim(segment, heap, 0, NULL, tld);
@@ -989,7 +989,7 @@ static mi_segment_t* mi_segment_try_reclaim(mi_heap_t* heap, size_t block_size,
989989

990990
mi_segment_t* result = NULL;
991991
mi_segment_t* segment = NULL;
992-
mi_arena_field_cursor_t current;
992+
mi_arena_field_cursor_t current;
993993
_mi_arena_field_cursor_init(heap, tld->subproc, false /* non-blocking */, &current);
994994
while ((max_tries-- > 0) && ((segment = _mi_arena_segment_clear_abandoned_next(&current)) != NULL))
995995
{
@@ -1264,7 +1264,7 @@ static bool mi_segment_visit_page(mi_page_t* page, bool visit_blocks, mi_block_v
12641264
}
12651265
}
12661266

1267-
bool _mi_segment_visit_blocks(mi_segment_t* segment, int heap_tag, bool visit_blocks, mi_block_visit_fun* visitor, void* arg) {
1267+
bool _mi_segment_visit_blocks(mi_segment_t* segment, int heap_tag, bool visit_blocks, mi_block_visit_fun* visitor, void* arg) {
12681268
for (size_t i = 0; i < segment->capacity; i++) {
12691269
mi_page_t* const page = &segment->pages[i];
12701270
if (page->segment_in_use) {

test/test-stress.c

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,14 @@ terms of the MIT license.
2525
// > mimalloc-test-stress [THREADS] [SCALE] [ITER]
2626
//
2727
// argument defaults
28+
#if !defined(MI_TSAN)
2829
static int THREADS = 32; // more repeatable if THREADS <= #processors
29-
static int SCALE = 25; // scaling factor
30-
31-
#if defined(MI_TSAN)
32-
static int ITER = 10; // N full iterations destructing and re-creating all threads (on tsan reduce for azure pipeline limits)
33-
#else
34-
static int ITER = 50; // N full iterations destructing and re-creating all threads
30+
#else // with thread-sanitizer reduce the defaults for azure pipeline limits
31+
static int THREADS = 8;
3532
#endif
3633

37-
// static int THREADS = 8; // more repeatable if THREADS <= #processors
38-
// static int SCALE = 100; // scaling factor
34+
static int SCALE = 25; // scaling factor
35+
static int ITER = 50; // N full iterations destructing and re-creating all threads
3936

4037
#define STRESS // undefine for leak test
4138

0 commit comments

Comments
 (0)