Coverage Report

Created: 2026-10-31 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython/Objects/mimalloc/segment-map.c
Line
Count
Source
1
/* ----------------------------------------------------------------------------
2
Copyright (c) 2019-2023, Microsoft Research, Daan Leijen
3
This is free software; you can redistribute it and/or modify it under the
4
terms of the MIT license. A copy of the license can be found in the file
5
"LICENSE" at the root of this distribution.
6
-----------------------------------------------------------------------------*/
7
8
/* -----------------------------------------------------------
9
  The following functions are to reliably find the segment or
10
  block that encompasses any pointer p (or NULL if it is not
11
  in any of our segments).
12
  We maintain a bitmap of all memory with 1 bit per MI_SEGMENT_SIZE (64MiB)
13
  set to 1 if it contains the segment meta data.
14
----------------------------------------------------------- */
15
#include "mimalloc.h"
16
#include "mimalloc/internal.h"
17
#include "mimalloc/atomic.h"
18
19
// Reduce total address space to reduce .bss  (due to the `mi_segment_map`)
20
#if (MI_INTPTR_SIZE > 4) && MI_TRACK_ASAN
21
#define MI_SEGMENT_MAP_MAX_ADDRESS    (128*1024ULL*MI_GiB)  // 128 TiB  (see issue #881)
22
#elif (MI_INTPTR_SIZE > 4)
23
0
#define MI_SEGMENT_MAP_MAX_ADDRESS    (48*1024ULL*MI_GiB)   // 48 TiB
24
#else
25
#define MI_SEGMENT_MAP_MAX_ADDRESS    (UINT32_MAX)
26
#endif
27
28
0
#define MI_SEGMENT_MAP_PART_SIZE      (MI_INTPTR_SIZE*MI_KiB - 128)      // 128 > sizeof(mi_memid_t) !
29
0
#define MI_SEGMENT_MAP_PART_BITS      (8*MI_SEGMENT_MAP_PART_SIZE)
30
#define MI_SEGMENT_MAP_PART_ENTRIES   (MI_SEGMENT_MAP_PART_SIZE / MI_INTPTR_SIZE)
31
0
#define MI_SEGMENT_MAP_PART_BIT_SPAN  (MI_SEGMENT_ALIGN)                 // memory area covered by 1 bit
32
33
#if (MI_SEGMENT_MAP_PART_BITS < (MI_SEGMENT_MAP_MAX_ADDRESS / MI_SEGMENT_MAP_PART_BIT_SPAN)) // prevent overflow on 32-bit (issue #1017)
34
0
#define MI_SEGMENT_MAP_PART_SPAN      (MI_SEGMENT_MAP_PART_BITS * MI_SEGMENT_MAP_PART_BIT_SPAN)
35
#else
36
#define MI_SEGMENT_MAP_PART_SPAN      MI_SEGMENT_MAP_MAX_ADDRESS
37
#endif
38
39
0
#define MI_SEGMENT_MAP_MAX_PARTS      ((MI_SEGMENT_MAP_MAX_ADDRESS / MI_SEGMENT_MAP_PART_SPAN) + 1)
40
41
// A part of the segment map.
42
typedef struct mi_segmap_part_s {
43
  mi_memid_t memid;
44
  _Atomic(uintptr_t) map[MI_SEGMENT_MAP_PART_ENTRIES];
45
} mi_segmap_part_t;
46
47
// Allocate parts on-demand to reduce .bss footprint
48
static _Atomic(mi_segmap_part_t*) mi_segment_map[MI_SEGMENT_MAP_MAX_PARTS]; // = { NULL, .. }
49
50
0
static mi_segmap_part_t* mi_segment_map_index_of(const mi_segment_t* segment, bool create_on_demand, size_t* idx, size_t* bitidx) {
51
  // note: segment can be invalid or NULL.
52
0
  mi_assert_internal(_mi_ptr_segment(segment + 1) == segment); // is it aligned on MI_SEGMENT_SIZE?
53
0
  *idx = 0;
54
0
  *bitidx = 0;
55
0
  if ((uintptr_t)segment >= MI_SEGMENT_MAP_MAX_ADDRESS) return NULL;
56
0
  const uintptr_t segindex = ((uintptr_t)segment) / MI_SEGMENT_MAP_PART_SPAN;
57
0
  if (segindex >= MI_SEGMENT_MAP_MAX_PARTS) return NULL;
58
0
  mi_segmap_part_t* part = mi_atomic_load_ptr_relaxed(mi_segmap_part_t, &mi_segment_map[segindex]);
59
60
  // allocate on demand to reduce .bss footprint
61
0
  if mi_unlikely(part == NULL) {
62
0
    if (!create_on_demand) return NULL;
63
0
    mi_memid_t memid;
64
0
    part = (mi_segmap_part_t*)_mi_os_zalloc(sizeof(mi_segmap_part_t), &memid);
65
0
    if (part == NULL) return NULL;
66
0
    part->memid = memid;
67
0
    mi_segmap_part_t* expected = NULL;
68
0
    if (!mi_atomic_cas_ptr_strong_release(mi_segmap_part_t, &mi_segment_map[segindex], &expected, part)) {
69
0
      _mi_os_free(part, sizeof(mi_segmap_part_t), memid);
70
0
      part = expected;
71
0
      if (part == NULL) return NULL;
72
0
    }
73
0
  }
74
0
  mi_assert(part != NULL);
75
0
  const uintptr_t offset = ((uintptr_t)segment) % MI_SEGMENT_MAP_PART_SPAN;
76
0
  const uintptr_t bitofs = offset / MI_SEGMENT_MAP_PART_BIT_SPAN;
77
0
  *idx = bitofs / MI_INTPTR_BITS;
78
0
  *bitidx = bitofs % MI_INTPTR_BITS;
79
0
  return part;
80
0
}
81
82
0
void _mi_segment_map_allocated_at(const mi_segment_t* segment) {
83
0
  if (segment->memid.memkind == MI_MEM_ARENA) return; // we lookup segments first in the arena's and don't need the segment map
84
0
  size_t index;
85
0
  size_t bitidx;
86
0
  mi_segmap_part_t* part = mi_segment_map_index_of(segment, true /* alloc map if needed */, &index, &bitidx);
87
0
  if (part == NULL) return; // outside our address range..
88
0
  uintptr_t mask = mi_atomic_load_relaxed(&part->map[index]);
89
0
  uintptr_t newmask;
90
0
  do {
91
0
    newmask = (mask | ((uintptr_t)1 << bitidx));
92
0
  } while (!mi_atomic_cas_weak_release(&part->map[index], &mask, newmask));
93
0
}
94
95
0
void _mi_segment_map_freed_at(const mi_segment_t* segment) {
96
0
  if (segment->memid.memkind == MI_MEM_ARENA) return;
97
0
  size_t index;
98
0
  size_t bitidx;
99
0
  mi_segmap_part_t* part = mi_segment_map_index_of(segment, false /* don't alloc if not present */, &index, &bitidx);
100
0
  if (part == NULL) return; // outside our address range..
101
0
  uintptr_t mask = mi_atomic_load_relaxed(&part->map[index]);
102
0
  uintptr_t newmask;
103
0
  do {
104
0
    newmask = (mask & ~((uintptr_t)1 << bitidx));
105
0
  } while (!mi_atomic_cas_weak_release(&part->map[index], &mask, newmask));
106
0
}
107
108
// Determine the segment belonging to a pointer or NULL if it is not in a valid segment.
109
0
static mi_segment_t* _mi_segment_of(const void* p) {
110
0
  if (p == NULL) return NULL;
111
0
  mi_segment_t* segment = _mi_ptr_segment(p);  // segment can be NULL
112
0
  size_t index;
113
0
  size_t bitidx;
114
0
  mi_segmap_part_t* part = mi_segment_map_index_of(segment, false /* dont alloc if not present */, &index, &bitidx);
115
0
  if (part == NULL) return NULL;
116
0
  const uintptr_t mask = mi_atomic_load_relaxed(&part->map[index]);
117
0
  if mi_likely((mask & ((uintptr_t)1 << bitidx)) != 0) {
118
    // yes, allocated by us
119
0
    const bool cookie_ok = (_mi_ptr_cookie(segment) == segment->cookie);
120
0
    if mi_likely(cookie_ok) {
121
0
      return segment; // yes, allocated by us and valid
122
0
    }
123
0
    else {
124
0
      _mi_error_message(EFAULT, "segment map found an invalid segment, possibly corrupted meta-data (address=%p, segment=%p)\n", p, segment);
125
0
    }
126
0
  }
127
0
  return NULL;
128
0
}
129
130
// Is this a valid pointer in our heap?
131
0
static bool mi_is_valid_pointer(const void* p) {
132
  // first check if it is in an arena, then check if it is OS allocated
133
0
  return (_mi_arena_contains(p) || _mi_segment_of(p) != NULL);
134
0
}
135
136
0
mi_decl_nodiscard mi_decl_export bool mi_is_in_heap_region(const void* p) mi_attr_noexcept {
137
0
  return mi_is_valid_pointer(p);
138
0
}
139
140
0
void _mi_segment_map_unsafe_destroy(void) {
141
0
  for (size_t i = 0; i < MI_SEGMENT_MAP_MAX_PARTS; i++) {
142
0
    mi_segmap_part_t* part = mi_atomic_exchange_ptr_relaxed(mi_segmap_part_t, &mi_segment_map[i], NULL);
143
0
    if (part != NULL) {
144
0
      _mi_os_free(part, sizeof(mi_segmap_part_t), part->memid);
145
0
    }
146
0
  }
147
0
}