Skip to content

Commit d5cc6e2

Browse files
committed
introduce secure level 5 which adds guard pages around each mimalloc page; use default secure level 4 as too many guard pages can reach OS limits (on VLA's for example)
1 parent 200b5b2 commit d5cc6e2

5 files changed

Lines changed: 27 additions & 21 deletions

File tree

CMakeLists.txt

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ project(libmimalloc C CXX)
44
set(CMAKE_C_STANDARD 11)
55
set(CMAKE_CXX_STANDARD 17)
66

7-
option(MI_SECURE "Use full security mitigations (like guard pages, allocation randomization, double-free mitigation, and free-list corruption detection)" OFF)
7+
option(MI_SECURE "Use security mitigations (like meta data guard pages, allocation randomization, double-free mitigation, and free-list corruption detection)" OFF)
8+
option(MI_SECURE_FULL "Use full security mitigations including guard pages at the end of each mimalloc page (may be expensive)" OFF)
89
option(MI_PADDING "Enable padding to detect heap block overflow (always on in DEBUG or SECURE mode, or with Valgrind/ASAN)" OFF)
910
option(MI_OVERRIDE "Override the standard malloc interface (i.e. define entry points for 'malloc', 'free', etc)" ON)
1011
option(MI_XMALLOC "Enable abort() call on memory allocation failure by default" OFF)
@@ -106,7 +107,7 @@ endif()
106107
# -----------------------------------------------------------------------------
107108

108109
message(STATUS "")
109-
if (NOT CMAKE_BUILD_TYPE)
110+
if(NOT CMAKE_BUILD_TYPE)
110111
if ("${CMAKE_BINARY_DIR}" MATCHES ".*((D|d)ebug|asan|tsan|ubsan|valgrind)$")
111112
message(STATUS "No build type selected, default to 'Debug'")
112113
set(CMAKE_BUILD_TYPE "Debug")
@@ -116,12 +117,12 @@ if (NOT CMAKE_BUILD_TYPE)
116117
endif()
117118
endif()
118119

119-
if (CMAKE_GENERATOR MATCHES "^Visual Studio.*$")
120+
if(CMAKE_GENERATOR MATCHES "^Visual Studio.*$")
120121
message(STATUS "Note: when building with Visual Studio the build type is specified when building.")
121122
message(STATUS "For example: 'cmake --build . --config=Release")
122123
endif()
123124

124-
if("${CMAKE_BINARY_DIR}" MATCHES ".*(S|s)ecure$")
125+
if(NOT MI_SECURE AND NOT MI_SECURE_FULL AND "${CMAKE_BINARY_DIR}" MATCHES ".*(S|s)ecure$")
125126
message(STATUS "Default to secure build")
126127
set(MI_SECURE "ON")
127128
endif()
@@ -221,8 +222,12 @@ if(WIN32)
221222
endif()
222223
endif()
223224

224-
if(MI_SECURE)
225-
message(STATUS "Set full secure build (MI_SECURE=ON)")
225+
if(MI_SECURE_FULL)
226+
set(MI_SECURE ON)
227+
message(STATUS "Set full secure build (MI_SECURE_FULL=ON)")
228+
list(APPEND mi_defines MI_SECURE=5)
229+
elseif(MI_SECURE)
230+
message(STATUS "Set secure build (MI_SECURE=ON)")
226231
list(APPEND mi_defines MI_SECURE=4)
227232
endif()
228233

include/mimalloc/types.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,11 @@ terms of the MIT license. A copy of the license can be found in the file
5353
// #define MI_STAT 1
5454

5555
// Define MI_SECURE to enable security mitigations
56-
// #define MI_SECURE 1 // guard page around metadata
57-
// #define MI_SECURE 2 // guard page around each mimalloc page
56+
// #define MI_SECURE 1 // guard pages around meta data, randomize arena allocation addresses (like ASLR), abort on detected meta data corruption
57+
// #define MI_SECURE 2 // randomize relative allocation addresses (within mimalloc pages)
5858
// #define MI_SECURE 3 // encode free lists (detect corrupted free list (buffer overflow), and invalid pointer free)
59-
// #define MI_SECURE 4 // checks for double free. (may be more expensive)
59+
// #define MI_SECURE 4 // checks for double free (may be more expensive) (`-DMI_SECURE=ON`)
60+
// #define MI_SECURE 5 // guard page at the end of each mimalloc page (expensive!) (`-DMI_SECURE_FULL=ON`)
6061

6162
#if !defined(MI_SECURE)
6263
#define MI_SECURE 0

src/page.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -619,8 +619,8 @@ static mi_decl_noinline void mi_page_free_list_extend( mi_page_t* const page, co
619619
----------------------------------------------------------- */
620620

621621
#define MI_MAX_EXTEND_SIZE (4*1024) // heuristic, one OS page seems to work well.
622-
#if (MI_SECURE>0)
623-
#define MI_MIN_EXTEND (8*MI_SECURE) // extend at least by this many
622+
#if (MI_SECURE>=2)
623+
#define MI_MIN_EXTEND (8*MI_SECURE) // extend at least by this many blocks
624624
#else
625625
#define MI_MIN_EXTEND (1)
626626
#endif
@@ -663,7 +663,7 @@ static bool mi_page_extend_free(mi_heap_t* heap, mi_page_t* page, mi_tld_t* tld)
663663
mi_assert_internal(extend < (1UL<<16));
664664

665665
// and append the extend the free list
666-
if (extend < MI_MIN_SLICES || MI_SECURE==0) { //!mi_option_is_enabled(mi_option_secure)) {
666+
if (extend < MI_MIN_SLICES || MI_SECURE<2) { //!mi_option_is_enabled(mi_option_secure)) {
667667
mi_page_free_list_extend(page, bsize, extend, &tld->stats );
668668
}
669669
else {
@@ -860,7 +860,7 @@ static inline mi_page_t* mi_find_free_page(mi_heap_t* heap, size_t size) {
860860
// check the first page: we even do this with candidate search or otherwise we re-search every time
861861
mi_page_t* page = pq->first;
862862
if (page != NULL) {
863-
#if (MI_SECURE>=3) // in secure mode, we extend half the time to increase randomness
863+
#if (MI_SECURE>=2) // in secure mode, we extend half the time to increase randomness
864864
if (page->capacity < page->reserved && ((_mi_heap_random_next(heap) & 1) == 1)) {
865865
mi_page_extend_free(heap, page, heap->tld);
866866
mi_assert_internal(mi_page_immediate_available(page));

src/prim/unix/prim.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ int _mi_prim_alloc(void* hint_addr, size_t size, size_t try_alignment, bool comm
427427
//---------------------------------------------
428428

429429
static void unix_mprotect_hint(int err) {
430-
#if defined(__linux__) && (MI_SECURE>=2) // guard page around every mimalloc page
430+
#if defined(__linux__) && (MI_SECURE>=5) // guard page around every mimalloc page
431431
if (err == ENOMEM) {
432432
_mi_warning_message("The next warning may be caused by a low memory map limit.\n"
433433
" On Linux this is controlled by the vm.max_map_count -- maybe increase it?\n"

src/segment.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ static void mi_segment_insert_in_free_queue(mi_segment_t* segment, mi_segments_t
113113
Invariant checking
114114
----------------------------------------------------------- */
115115

116-
#if (MI_DEBUG >= 2) || (MI_SECURE >= 2)
116+
#if (MI_DEBUG >= 2) || (MI_SECURE >= 5)
117117
static size_t mi_segment_page_size(const mi_segment_t* segment) {
118118
if (segment->capacity > 1) {
119119
mi_assert_internal(segment->page_kind <= MI_PAGE_MEDIUM);
@@ -199,7 +199,7 @@ static void mi_segment_protect(mi_segment_t* segment, bool protect) {
199199
mi_assert_internal((segment->segment_info_size - os_psize) >= (sizeof(mi_segment_t) + ((segment->capacity - 1) * sizeof(mi_page_t))));
200200
mi_assert_internal(((uintptr_t)segment + segment->segment_info_size) % os_psize == 0);
201201
mi_segment_protect_range((uint8_t*)segment + segment->segment_info_size - os_psize, os_psize, protect);
202-
#if (MI_SECURE >= 2)
202+
#if (MI_SECURE >= 5)
203203
if (segment->capacity == 1)
204204
#endif
205205
{
@@ -218,7 +218,7 @@ static void mi_segment_protect(mi_segment_t* segment, bool protect) {
218218
mi_segment_protect_range(start, os_psize, protect);
219219
}
220220
}
221-
#if (MI_SECURE >= 2)
221+
#if (MI_SECURE >= 5)
222222
else {
223223
// or protect every page
224224
const size_t page_size = mi_segment_page_size(segment);
@@ -237,7 +237,7 @@ static void mi_segment_protect(mi_segment_t* segment, bool protect) {
237237
----------------------------------------------------------- */
238238

239239
static void mi_page_purge(mi_segment_t* segment, mi_page_t* page, mi_segments_tld_t* tld) {
240-
// todo: should we purge the guard page as well when MI_SECURE>=2 ?
240+
// todo: should we purge the guard page as well when MI_SECURE>=5 ?
241241
mi_assert_internal(page->is_committed);
242242
mi_assert_internal(!page->segment_in_use);
243243
if (!segment->allow_purge) return;
@@ -258,7 +258,7 @@ static bool mi_page_ensure_committed(mi_segment_t* segment, mi_page_t* page, mi_
258258
size_t psize;
259259
uint8_t* start = mi_segment_raw_page_start(segment, page, &psize);
260260
bool is_zero = false;
261-
const size_t gsize = (MI_SECURE >= 2 ? _mi_os_page_size() : 0);
261+
const size_t gsize = (MI_SECURE >= 5 ? _mi_os_page_size() : 0);
262262
bool ok = _mi_os_commit(start, psize + gsize, &is_zero);
263263
if (!ok) return false; // failed to commit!
264264
page->is_committed = true;
@@ -408,9 +408,9 @@ static uint8_t* mi_segment_raw_page_start(const mi_segment_t* segment, const mi_
408408
psize -= segment->segment_info_size;
409409
}
410410

411-
#if (MI_SECURE > 1) // every page has an os guard page
411+
#if (MI_SECURE>=5) // every page has an os guard page
412412
psize -= _mi_os_page_size();
413-
#elif (MI_SECURE==1) // the last page has an os guard page at the end
413+
#elif (MI_SECURE>=1) // the last page has an os guard page at the end
414414
if (page->segment_idx == segment->capacity - 1) {
415415
psize -= _mi_os_page_size();
416416
}

0 commit comments

Comments
 (0)