Skip to content

Commit a26a5ca

Browse files
mjp41nwf-msr
andcommitted
Factor checks under separate feature flags.
All the checks and mitigations have been placed under feature flags. These can be controlled by defining SNMALLOC_CHECK_CLIENT_MITIGATIONS This can take a term that represents the mitigations that should be enabled. E.g. -DSNMALLOC_CHECK_CLIENT_MITIGATIONS=nochecks+random_pagemap The CMake uses this to build numerous versions of the LD_PRELOAD library and tests to allow individual features to be benchmarked. Co-authored-by: Nathaniel Wesley Filardo <[email protected]>
1 parent b357165 commit a26a5ca

25 files changed

+759
-434
lines changed

.github/workflows/main.yml

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ jobs:
5858
build-type: Debug
5959
self-host: true
6060
extra-cmake-flags: "-DSNMALLOC_USE_PTHREAD_DESTRUCTORS=On"
61+
# Extra build to check using individual mitigations works.
62+
- os: "ubuntu-latest"
63+
variant: "individual mitigations"
64+
dependencies: "sudo apt install ninja-build"
65+
build-type: Release
66+
self-host: true
67+
extra-cmake-flags: "-DSNMALLOC_BENCHMARK_INDIVIDUAL_MITIGATIONS=On -DBUILD_TESTING=Off"
6168
# Check that we can build specifically with libstdc++
6269
- os: "ubuntu-latest"
6370
variant: "libstdc++ (Build only)"
@@ -92,7 +99,7 @@ jobs:
9299
run: NINJA_STATUS="%p [%f:%s/%t] %o/s, %es" ninja
93100
- name: Test file size of binaries is sane
94101
working-directory: ${{github.workspace}}/build
95-
run: "ls -l func-first_operation-fast ; [ $(ls -l func-first_operation-fast | awk '{ print $5}') -lt 10000000 ]"
102+
run: "ls -l libsnmallocshim.* ; [ $(ls -l libsnmallocshim.* | awk '{ print $5}') -lt 10000000 ]"
96103
# If the tests are enabled for this job, run them
97104
- name: Test
98105
if: ${{ matrix.build-only != 'yes' }}
@@ -102,12 +109,9 @@ jobs:
102109
if: ${{ matrix.self-host }}
103110
working-directory: ${{github.workspace}}/build
104111
run: |
105-
sudo cp libsnmallocshim.so libsnmallocshim-checks.so /usr/local/lib/
106-
ninja clean
107-
LD_PRELOAD=/usr/local/lib/libsnmallocshim.so ninja
108-
ninja clean
109-
LD_PRELOAD=/usr/local/lib/libsnmallocshim-checks.so ninja
110-
112+
mkdir libs
113+
cp libsnmallocshim*.so libs
114+
for lib in `ls libs`; do echo; echo Testing $lib; ninja clean; LD_PRELOAD=libs/$lib ninja libsnmallocshim.so; done
111115
# GitHub doesn't natively support *BSD, but we can run them in VMs on Mac /
112116
# Linux runners
113117
freebsd:

CMakeLists.txt

Lines changed: 136 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ option(SNMALLOC_NO_REALLOCARRAY "Build without reallocarray exported" ON)
2525
option(SNMALLOC_NO_REALLOCARR "Build without reallocarr exported" ON)
2626
option(SNMALLOC_LINK_ICF "Link with Identical Code Folding" ON)
2727
option(SNMALLOC_IPO "Link with IPO/LTO support" OFF)
28+
option(SNMALLOC_BENCHMARK_INDIVIDUAL_MITIGATIONS "Build tests and ld_preload for individual mitigations" OFF)
2829
# Options that apply only if we're not building the header-only library
2930
cmake_dependent_option(SNMALLOC_RUST_SUPPORT "Build static library for rust" OFF "NOT SNMALLOC_HEADER_ONLY_LIBRARY" OFF)
3031
cmake_dependent_option(SNMALLOC_STATIC_LIBRARY "Build static libraries" ON "NOT SNMALLOC_HEADER_ONLY_LIBRARY" OFF)
@@ -271,7 +272,6 @@ function(add_warning_flags name)
271272
$<$<PLATFORM_ID:Windows>:$<${ci_or_debug}:/DEBUG>>)
272273
endfunction()
273274

274-
275275
# To build with just the header library target define SNMALLOC_HEADER_ONLY_LIBRARY
276276
if(NOT SNMALLOC_HEADER_ONLY_LIBRARY)
277277

@@ -286,6 +286,78 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY)
286286
set(${result} ${dirlist} PARENT_SCOPE)
287287
endfunction()
288288

289+
set(TESTDIR ${CMAKE_CURRENT_SOURCE_DIR}/src/test)
290+
291+
if(BUILD_TESTING)
292+
enable_testing()
293+
subdirlist(TEST_CATEGORIES ${TESTDIR})
294+
else()
295+
set(TEST_CATEGORIES "")
296+
endif()
297+
list(REVERSE TEST_CATEGORIES)
298+
299+
if (${SNMALLOC_CLEANUP} STREQUAL THREAD_CLEANUP)
300+
set(TEST_CLEANUP PTHREAD_DESTRUCTORS)
301+
else ()
302+
set(TEST_CLEANUP ${SNMALLOC_CLEANUP})
303+
endif()
304+
305+
function(make_tests TAG DEFINES)
306+
foreach(TEST_CATEGORY ${TEST_CATEGORIES})
307+
message(STATUS "Adding ${TAG}/${TEST_CATEGORY} tests")
308+
subdirlist(TESTS ${TESTDIR}/${TEST_CATEGORY})
309+
foreach(TEST ${TESTS})
310+
unset(SRC)
311+
aux_source_directory(${TESTDIR}/${TEST_CATEGORY}/${TEST} SRC)
312+
set(TESTNAME "${TEST_CATEGORY}-${TEST}-${TAG}")
313+
314+
add_executable(${TESTNAME} ${SRC})
315+
316+
if(SNMALLOC_SANITIZER)
317+
target_compile_options(${TESTNAME} PRIVATE -g -fsanitize=${SNMALLOC_SANITIZER} -fno-omit-frame-pointer)
318+
target_link_libraries(${TESTNAME} -fsanitize=${SNMALLOC_SANITIZER})
319+
endif()
320+
321+
add_warning_flags(${TESTNAME})
322+
323+
target_link_libraries(${TESTNAME} snmalloc)
324+
target_compile_definitions(${TESTNAME} PRIVATE "SNMALLOC_USE_${TEST_CLEANUP}")
325+
326+
if (NOT DEFINES STREQUAL " ")
327+
target_compile_definitions(${TESTNAME} PRIVATE ${DEFINES})
328+
endif()
329+
330+
if (${TEST} MATCHES "release-.*")
331+
message(VERBOSE "Adding test: ${TESTNAME} only for release configs")
332+
add_test(NAME ${TESTNAME} COMMAND ${TESTNAME} CONFIGURATIONS "Release")
333+
else()
334+
message(VERBOSE "Adding test: ${TESTNAME}")
335+
add_test(${TESTNAME} ${TESTNAME})
336+
endif()
337+
if (${TEST_CATEGORY} MATCHES "perf")
338+
message(VERBOSE "Single threaded test: ${TESTNAME}")
339+
set_tests_properties(${TESTNAME} PROPERTIES PROCESSORS 4)
340+
endif()
341+
if(WIN32)
342+
# On Windows these tests use a lot of memory as it doesn't support
343+
# lazy commit.
344+
if (${TEST} MATCHES "two_alloc_types")
345+
message(VERBOSE "Single threaded test: ${TESTNAME}")
346+
set_tests_properties(${TESTNAME} PROPERTIES PROCESSORS 4)
347+
endif()
348+
if (${TEST} MATCHES "fixed_region")
349+
message(VERBOSE "Single threaded test: ${TESTNAME}")
350+
set_tests_properties(${TESTNAME} PROPERTIES PROCESSORS 4)
351+
endif()
352+
if (${TEST} MATCHES "memory")
353+
message(VERBOSE "Single threaded test: ${TESTNAME}")
354+
set_tests_properties(${TESTNAME} PROPERTIES PROCESSORS 4)
355+
endif()
356+
endif()
357+
endforeach()
358+
endforeach()
359+
endfunction()
360+
289361
if(NOT (DEFINED SNMALLOC_LINKER_FLAVOUR) OR ("${SNMALLOC_LINKER_FLAVOUR}" MATCHES "^$"))
290362
# Linker not specified externally; probe to see if we can make lld work
291363
set(CMAKE_REQUIRED_LINK_OPTIONS -fuse-ld=lld -Wl,--icf=all)
@@ -376,89 +448,75 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY)
376448
target_compile_definitions(snmallocshim-checks-rust PRIVATE SNMALLOC_CHECK_CLIENT)
377449
endif()
378450

379-
set(TESTDIR ${CMAKE_CURRENT_SOURCE_DIR}/src/test)
380-
381-
if(BUILD_TESTING)
382-
enable_testing()
383-
subdirlist(TEST_CATEGORIES ${TESTDIR})
384-
else()
385-
set(TEST_CATEGORIES "")
386-
endif()
451+
if (BUILD_TESTING)
452+
if (WIN32
453+
OR (CMAKE_SYSTEM_NAME STREQUAL NetBSD)
454+
OR (CMAKE_SYSTEM_NAME STREQUAL OpenBSD)
455+
OR (CMAKE_SYSTEM_NAME STREQUAL DragonFly)
456+
OR (CMAKE_SYSTEM_NAME STREQUAL SunOS))
457+
# Windows does not support aligned allocation well enough
458+
# for pass through.
459+
# NetBSD, OpenBSD and DragonFlyBSD do not support malloc*size calls.
460+
set(FLAVOURS fast;check)
461+
else()
462+
set(FLAVOURS fast;check;malloc)
463+
endif()
387464

388-
list(REVERSE TEST_CATEGORIES)
389-
if (${SNMALLOC_CLEANUP} STREQUAL THREAD_CLEANUP)
390-
set(TEST_CLEANUP PTHREAD_DESTRUCTORS)
391-
else ()
392-
set(TEST_CLEANUP ${SNMALLOC_CLEANUP})
393-
endif()
394-
foreach(TEST_CATEGORY ${TEST_CATEGORIES})
395-
message(STATUS "Adding ${TEST_CATEGORY} tests")
396-
subdirlist(TESTS ${TESTDIR}/${TEST_CATEGORY})
397-
foreach(TEST ${TESTS})
398-
if (WIN32
399-
OR (CMAKE_SYSTEM_NAME STREQUAL NetBSD)
400-
OR (CMAKE_SYSTEM_NAME STREQUAL OpenBSD)
401-
OR (CMAKE_SYSTEM_NAME STREQUAL DragonFly)
402-
OR (CMAKE_SYSTEM_NAME STREQUAL SunOS))
403-
# Windows does not support aligned allocation well enough
404-
# for pass through.
405-
# NetBSD, OpenBSD and DragonFlyBSD do not support malloc*size calls.
406-
set(FLAVOURS fast;check)
407-
else()
408-
set(FLAVOURS fast;check;malloc)
465+
foreach(FLAVOUR ${FLAVOURS})
466+
if (${FLAVOUR} STREQUAL "malloc")
467+
set(DEFINES SNMALLOC_PASS_THROUGH)
468+
endif()
469+
if (${FLAVOUR} STREQUAL "check")
470+
set(DEFINES SNMALLOC_CHECK_CLIENT)
471+
endif()
472+
if (${FLAVOUR} STREQUAL "fast")
473+
set(DEFINES " ")
409474
endif()
410-
foreach(FLAVOUR ${FLAVOURS})
411-
unset(SRC)
412-
aux_source_directory(${TESTDIR}/${TEST_CATEGORY}/${TEST} SRC)
413-
set(TESTNAME "${TEST_CATEGORY}-${TEST}-${FLAVOUR}")
414-
415-
add_executable(${TESTNAME} ${SRC})
416475

417-
if(SNMALLOC_SANITIZER)
418-
target_compile_options(${TESTNAME} PRIVATE -g -fsanitize=${SNMALLOC_SANITIZER} -fno-omit-frame-pointer)
419-
target_link_libraries(${TESTNAME} -fsanitize=${SNMALLOC_SANITIZER})
420-
endif()
476+
make_tests(${FLAVOUR} ${DEFINES})
477+
endforeach()
478+
endif()
421479

422-
add_warning_flags(${TESTNAME})
480+
if (SNMALLOC_BENCHMARK_INDIVIDUAL_MITIGATIONS)
481+
set (MITIGATIONS
482+
metadata_protection;
483+
pal_enforce_access;
484+
random_pagemap;
485+
sanity_checks;
486+
freelist_forward_edge;
487+
freelist_backward_edge;
488+
freelist_teardown_validate;
489+
reuse_LIFO;
490+
random_larger_thresholds;
491+
random_initial;
492+
random_preserve;
493+
random_extra_slab)
494+
495+
496+
foreach (MITIGATION ${MITIGATIONS})
497+
set(DEFINES "SNMALLOC_CHECK_CLIENT_MITIGATIONS=${MITIGATION}")
498+
add_shim(snmallocshim-${MITIGATION} SHARED ${SHIM_FILES})
499+
target_compile_definitions(snmallocshim-${MITIGATION} PRIVATE ${DEFINES})
500+
if (BUILD_TESTING)
501+
make_tests(${MITIGATION} ${DEFINES})
502+
endif()
503+
endforeach()
423504

424-
if (${FLAVOUR} STREQUAL "malloc")
425-
target_compile_definitions(${TESTNAME} PRIVATE SNMALLOC_PASS_THROUGH)
426-
endif()
427-
if (${FLAVOUR} STREQUAL "check")
428-
target_compile_definitions(${TESTNAME} PRIVATE SNMALLOC_CHECK_CLIENT)
429-
endif()
430-
target_link_libraries(${TESTNAME} snmalloc)
431-
target_compile_definitions(${TESTNAME} PRIVATE "SNMALLOC_USE_${TEST_CLEANUP}")
432-
if (${TEST} MATCHES "release-.*")
433-
message(VERBOSE "Adding test: ${TESTNAME} only for release configs")
434-
add_test(NAME ${TESTNAME} COMMAND ${TESTNAME} CONFIGURATIONS "Release")
435-
else()
436-
message(VERBOSE "Adding test: ${TESTNAME}")
437-
add_test(${TESTNAME} ${TESTNAME})
438-
endif()
439-
if (${TEST_CATEGORY} MATCHES "perf")
440-
message(VERBOSE "Single threaded test: ${TESTNAME}")
441-
set_tests_properties(${TESTNAME} PROPERTIES PROCESSORS 4)
442-
endif()
443-
if(WIN32)
444-
# On Windows these tests use a lot of memory as it doesn't support
445-
# lazy commit.
446-
if (${TEST} MATCHES "two_alloc_types")
447-
message(VERBOSE "Single threaded test: ${TESTNAME}")
448-
set_tests_properties(${TESTNAME} PROPERTIES PROCESSORS 4)
449-
endif()
450-
if (${TEST} MATCHES "fixed_region")
451-
message(VERBOSE "Single threaded test: ${TESTNAME}")
452-
set_tests_properties(${TESTNAME} PROPERTIES PROCESSORS 4)
453-
endif()
454-
if (${TEST} MATCHES "memory")
455-
message(VERBOSE "Single threaded test: ${TESTNAME}")
456-
set_tests_properties(${TESTNAME} PROPERTIES PROCESSORS 4)
457-
endif()
458-
endif()
459-
endforeach()
505+
set(MITIGATIONSET "no_checks")
506+
set(COUNT 0)
507+
foreach (MITIGATION ${MITIGATIONS})
508+
MATH(EXPR COUNT "${COUNT} + 1")
509+
set(MITIGATIONNAME "mitigations-${COUNT}")
510+
set(MITIGATIONSET "${MITIGATIONSET}+${MITIGATION}")
511+
message(STATUS "MITIGATIONSET: ${COUNT} -> ${MITIGATIONSET}")
512+
set(DEFINES "-DSNMALLOC_CHECK_CLIENT_MITIGATIONS=${MITIGATIONSET}")
513+
add_shim(snmallocshim-${MITIGATIONNAME} SHARED ${SHIM_FILES})
514+
target_compile_definitions(snmallocshim-${MITIGATIONNAME} PRIVATE ${DEFINES})
515+
if (BUILD_TESTING)
516+
make_tests(${MITIGATIONNAME} ${DEFINES})
517+
endif()
460518
endforeach()
461-
endforeach()
519+
endif()
462520

463521
clangformat_targets()
464522
endif()

docs/PORTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ If memory is not required any more, then `snmalloc` will change the state to
3030
`not using`, and will ensure that it notifies the `Pal` again
3131
before it every accesses that memory again.
3232
The `not using` state allows the `Pal` to recycle the memory for other purposes.
33-
If `PalEnforceAccess` is set to true, then accessing that has not been notified
33+
If `pal_enforce_access` is set as a mitigation, then accessing memory that has not been notified
3434
correctly should trigger an exception/segfault.
3535
3636
The state for a particular region of memory is set with

src/snmalloc/backend/globalconfig.h

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,6 @@
99
# include "meta_protected_range.h"
1010
# include "standard_range.h"
1111

12-
# if defined(SNMALLOC_CHECK_CLIENT) && !defined(OPEN_ENCLAVE)
13-
/**
14-
* Protect meta data blocks by allocating separate from chunks for
15-
* user allocations. This involves leaving gaps in address space.
16-
* This is less efficient, so should only be applied for the checked
17-
* build.
18-
*
19-
* On Open Enclave the address space is limited, so we disable this
20-
* feature.
21-
*/
22-
# define SNMALLOC_META_PROTECTED
23-
# endif
24-
2512
namespace snmalloc
2613
{
2714
// Forward reference to thread local cleanup.
@@ -79,11 +66,10 @@ namespace snmalloc
7966
/**
8067
* Use one of the default range configurations
8168
*/
82-
# ifdef SNMALLOC_META_PROTECTED
83-
using LocalState = MetaProtectedRangeLocalState<Pal, Pagemap, Base>;
84-
# else
85-
using LocalState = StandardLocalState<Pal, Pagemap, Base>;
86-
# endif
69+
using LocalState = std::conditional_t<
70+
mitigations(metadata_protection),
71+
MetaProtectedRangeLocalState<Pal, Pagemap, Base>,
72+
StandardLocalState<Pal, Pagemap, Base>>;
8773

8874
/**
8975
* Use the default backend.
@@ -136,14 +122,11 @@ namespace snmalloc
136122
// Initialise key for remote deallocation lists
137123
key_global = FreeListKey(entropy.get_free_list_key());
138124

139-
// Need to initialise pagemap. If SNMALLOC_CHECK_CLIENT is set and this
140-
// isn't a StrictProvenance architecture, randomize its table's location
141-
// within a significantly larger address space allocation.
142-
# if defined(SNMALLOC_CHECK_CLIENT)
143-
static constexpr bool pagemap_randomize = !aal_supports<StrictProvenance>;
144-
# else
145-
static constexpr bool pagemap_randomize = false;
146-
# endif
125+
// Need to randomise pagemap location. If requested and not a
126+
// StrictProvenance architecture, randomize its table's location within a
127+
// significantly larger address space allocation.
128+
static constexpr bool pagemap_randomize =
129+
mitigations(random_pagemap) && !aal_supports<StrictProvenance>;
147130

148131
Pagemap::concretePagemap.template init<pagemap_randomize>();
149132

src/snmalloc/ds/allocconfig.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,8 @@ namespace snmalloc
4949
static constexpr size_t MIN_CHUNK_SIZE = bits::one_at_bit(MIN_CHUNK_BITS);
5050

5151
// Minimum number of objects on a slab
52-
#ifdef SNMALLOC_CHECK_CLIENT
53-
static constexpr size_t MIN_OBJECT_COUNT = 13;
54-
#else
55-
static constexpr size_t MIN_OBJECT_COUNT = 4;
56-
#endif
52+
static constexpr size_t MIN_OBJECT_COUNT =
53+
mitigations(random_larger_thresholds) ? 13 : 4;
5754

5855
// Maximum size of an object that uses sizeclasses.
5956
#if defined(SNMALLOC_QEMU_WORKAROUND) && defined(SNMALLOC_VA_BITS_64)

src/snmalloc/ds/pagemap.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,10 @@ namespace snmalloc
213213
}
214214
else
215215
{
216+
if constexpr (pal_supports<LazyCommit, PAL>)
217+
{
218+
PAL::notify_using_readonly(new_body_untyped, REQUIRED_SIZE);
219+
}
216220
new_body = static_cast<T*>(new_body_untyped);
217221
}
218222
// Ensure bottom page is committed

0 commit comments

Comments
 (0)