MapGuard is a page allocation proxy and cache that aims to mitigate some memory safety exploits by intercepting, modifying, and logging mmap based page allocations. It enforces a simple set of allocation security policies configurable via environment variables. It works transparently on open and closed source programs with no source modifications in the target required. It can be used along side any mmap based memory allocator.
MapGuard uses the dynamic linker interface via dlsym to hook libc functions. When calls to those functions are intercepted MapGuard will inspect their arguments and then consult runtime policies for whether that behavior should be allowed, denied, or logged.
The library requires hooking mmap, munmap, mprotect, and mremap. Enabling all protections may introduce some performance and memory overhead, especially if guard pages are enabled.
MapGuard can introduce performance overhead when allocating many raw pages. This is particulary true when MG_USE_MAPPING_CACHE is enabled because it has to manage metadata for each page allocation and tracking this data introduces CPU and memory overhead. Faster data structures are available for managing this metadata but they all rely on malloc which makes it easier to bypass the security controls the library introduces.
The following functionality can be enabled/disabled via environment variables:
MG_PREVENT_RWX- Prevent PROT_READ, PROT_WRITE, PROT_EXEC mappingsMG_PREVENT_TRANSITION_TO_X- Prevent RW- allocations to ever transition to PROT_EXECMG_PREVENT_TRANSITION_FROM_X- Prevent R-X allocations to ever transition to PROT_WRITEMG_PREVENT_STATIC_ADDRESS- Prevent page allocations at a set address (enforces ASLR)MG_ENABLE_GUARD_PAGES- Force guard page allocations on either side of all mappingsMG_PANIC_ON_VIOLATION- Abort the process when any policies are violatedMG_POISON_ON_ALLOCATION- Fill all allocated pages with a byte pattern 0xdeMG_USE_MAPPING_CACHE- Enable the mapping cache, required for guard pages and other protectionsMG_ENABLE_SYSLOG- Enable logging of policy violations to syslog
make library - Compiles the library
make tests - Compiles a debug version of the library
make perf_tests - Compiles the performance tests
make format - Run clang format on the code base
Now run your own program with the library:
MG_PANIC_ON_VIOLATION=0 \
MG_USE_MAPPING_CACHE=1 \
MG_PREVENT_RWX=1 \
MG_PREVENT_STATIC_ADDRESS=1 \
MG_ENABLE_GUARD_PAGES=1 \
MG_PREVENT_TRANSITION_TO_X=1 \
MG_PREVENT_TRANSITION_FROM_X=1 \
MG_POISON_ON_ALLOCATION=1 \
LD_PRELOAD=build/libmapguard.so ./your_program
You can test MapGuard by running ./run_tests.sh:
# ./run_tests.sh
Running mapguard_test test... Succeeded
Running mapguard_thread_test test... Succeeded
# ./run_perf_test.sh
================================================================
Running baseline performance (no MapGuard)...
================================================================
test_name,iterations,time_ms,ops_per_sec
simple_alloc_free,10000,8.47,1180504.02
alloc_write_free,10000,26.84,372601.38
batch_alloc_then_free,10000,7.34,1362606.67
varied_sizes,1000,0.76,1323043.58
mprotect_transitions,1000,1.31,764720.68
large_allocations,1000,0.80,1246040.71
partial_munmap,1000,1.32,755381.72
================================================================
Running with MapGuard (no config)...
================================================================
test_name,iterations,time_ms,ops_per_sec
simple_alloc_free,10000,8.22,1216705.32
alloc_write_free,10000,27.75,360331.68
batch_alloc_then_free,10000,7.49,1335589.05
varied_sizes,1000,0.82,1218522.52
mprotect_transitions,1000,1.39,717703.52
large_allocations,1000,0.82,1215744.87
partial_munmap,1000,1.37,731707.14
================================================================
Running with MapGuard (cache enabled)...
================================================================
test_name,iterations,time_ms,ops_per_sec
simple_alloc_free,10000,8.57,1166271.34
alloc_write_free,10000,27.52,363336.88
batch_alloc_then_free,10000,12.19,820263.26
varied_sizes,1000,0.86,1168907.07
mprotect_transitions,1000,1.47,681605.18
large_allocations,1000,0.86,1167712.74
partial_munmap,1000,1.45,691044.89
================================================================
Running with MapGuard (full protection)...
================================================================
test_name,iterations,time_ms,ops_per_sec
simple_alloc_free,10000,38.12,262334.37
alloc_write_free,10000,38.36,260671.51
batch_alloc_then_free,10000,62.45,160123.29
varied_sizes,1000,5.01,199401.79
mprotect_transitions,1000,4.64,215372.19
large_allocations,1000,97.27,10280.64
partial_munmap,1000,5.64,177332.48
================================================================
Performance Summary
================================================================
Test Baseline Minimal Cache Full Overhead %
====================================================================================================
simple_alloc_free 1202369 1230700 1207353 258400 78.5%
alloc_write_free 365176 370031 341715 254845 30.2%
batch_alloc_then_free 1336228 1319573 820698 186382 86.1%
varied_sizes 1301023 1248829 1186123 202392 84.4%
mprotect_transitions 743564 754196 694666 197813 73.4%
large_allocations 1264090 1171818 1178666 10289 99.2%
partial_munmap 783827 794834 701816 176223 77.5%
Raw CSV files saved in /tmp/*_perf.csv
# ./run_fuzz_tests
...
(see if anything crashes!)
MapGuard is written and maintained by Chris Rohlf - chris.rohlf@gmail.com