mi-malloc 3.6/2.5/1.15
Loading...
Searching...
No Matches
Security

There are various build options to make mimalloc more secure.

Environment Options

With -DMI_SECURE_ENV=ON, only MIMALLOC_SHOW_STATS, MIMALLOC_VERBOSE, and MIMALLOC_ALLOW_THP are accepted from the environment – all other options are ignored.

This is by default enabled for secure mode, MTE, and PAC. This can be overridden by explicitly building with -DMI_SECURE_ENV=OFF. Note: even with OFF, an elevated process ignores all environment options. Moreover, on platforms with guard pages, a program built with secure mode always ignores settings for large or huge OS pages (as pinned memory cannot be guarded). You can still set options programmatically for trusted programs.

Secure Mode

mimalloc can be built in secure mode using -DMI_SECURE=OFF|MINIMAL|CHECKED|ON|FULL with CMake. These modes enable software mitigations to make mimalloc more robust against exploits.

With MINIMAL, only mitigations with a small performance impact are added (usually 1-3%):

  • Internal metadata, including the page map and static data, is surrounded by guard pages so that a buffer overflow or underflow cannot reach allocator metadata. Static pointers into metadata are kept in a sealed read-only area. Free list entries are not encoded at this level and can still be corrupted through a heap block buffer overflow.
  • OS allocation addresses are randomized where possible, and detected metadata corruption aborts the process.
  • Large blocks (over 512 KiB) are followed by a guard page.
  • Pinned memory, including large and huge OS pages, is neither allocated nor accepted as arena memory on platforms with guard pages.
  • MI_SECURE_ENV is enabled by default.

With CHECKED (the default with -DMI_MTE or -DMI_PAC), the performance and memory impact is still relatively small (usually less than 10%, but can be 2x slower):

  • Free list pointers are encoded with per-page keys to prevent overwrites with a known pointer and detect corruption. With PAC, hardware keys can be used.
  • Pointers passed to mi_free() and related functions are validated. Pointers that do not originate from mimalloc are ignored, and interior pointers are adjusted to the start of their block.
  • Free lists are initialized in random order, and allocation randomly chooses between extension and reuse within a page to make allocation order less predictable.
  • Reuse of freed blocks is delayed until one eighth of a page is free, and a random number of recently freed blocks is held back to make reclaiming a just-freed block harder.

With ON, performance and memory usage are impacted further (usually less than 25%, but can be 2 to 3x slower):

  • Eight bytes of padding are added to each heap block to detect overflows when the block is freed, and to detect double frees.
  • Some use-after-free accesses that modify the first word of a block are detected.
  • The first 1 KiB of a block is zeroed on free.
  • Free lists are randomized further.
  • The library is built as mimalloc-secure.

The FULL mode additionally places a guard page at the end of each mimalloc page (normally 64 KiB) and checks for byte-precise heap block overflows when freeing. This mode is generally not recommended because it costs more time and memory, and large heaps can reach the maximum VMA limit on Linux.

As always, evaluate with care as part of an overall security strategy as all of the above are mitigations but not guarantees.

Memory Tagging (MTE)

(v3 only, experimental)

On recent arm64 hardware with the memory tagging extension (MTE), build with -DMI_MTE=ON to give heap blocks randomized memory tags. Memory can only be accessed through a pointer with the matching tag. Adjacent blocks almost always receive different tags, and freed blocks are retagged, causing most double-free and use-after-free accesses to fault. Tags are chosen from 15 values, excluding the tags of neighboring blocks. Blocks up to 512 KiB are tagged; larger blocks use a trailing guard page because tagging takes time proportional to the block size.

Performance depends on the hardware and workload. On an M5, an MTE build is usually within about 15% of a release build, but can be 2 to 3x slower. -DMI_MTE=ON requires CPU and OS support at runtime and aborts at initialization otherwise. On Linux, this also requires enabling tag checking for all threads. -DMI_MTE=PREFER uses tagging when available and otherwise runs untagged, with performance close to a regular release build. MTE builds have an -mte library suffix, for example mimalloc-mte.o.

MTE enables -DMI_SECURE=CHECKED and -DMI_SECURE_ENV=ON by default. It provides similar and often stronger mitigations than -DMI_SECURE=ON, so combining the two is generally unnecessary.

  • macOS: requires an Apple M5 or later with macOS 26+. Executables need the hardened-process entitlements; mimalloc tests are signed automatically:
    > cmake -DMI_MTE=ON ../.. && make -j
    > codesign -s - -f --entitlements ../../cmake/mte-entitlements.plist myprogram
  • Linux: requires an arm64 kernel with MTE support; check with grep mte /proc/cpuinfo. Where environment policy permits it, MIMALLOC_MTE=sync, async, or preferred selects the checking mode (sync by default). Without MTE hardware, QEMU can be used for testing: QEMU_CPU=max qemu-aarch64 ./myprogram. Tag checking is enabled per thread and inherited by newly created threads, so mimalloc should initialize before other threads are created, as happens when dynamically linked or preloaded. Otherwise initialization reports a fatal error, or a warning with PREFER, because existing threads may remain untagged until they allocate and system calls with tagged pointers may fail in threads that never call mimalloc.
  • Android: requires an MTE-capable device, such as a Pixel 8 or later, with Memory Tagging Extension enabled in Developer options. Build with the Android NDK:
    > cmake -DMI_MTE=ON -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake \
    -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-30 ../..
  • iOS (experimental, untested): requires an A19 or later with iOS 26+ and the Enhanced Security capability enabling hardware memory tagging. Build with the iOS SDK:
    > cmake -DMI_MTE=ON -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_ARCHITECTURES=arm64 -DCMAKE_OSX_DEPLOYMENT_TARGET=26.0 \
    -DMI_BUILD_SHARED=OFF -DMI_BUILD_OBJECT=OFF -DMI_BUILD_TESTS=OFF ../..
    Link libmimalloc-mte.a into the app and use the hardened-process entitlements.

Pointer Authentication (PAC)

(v3 only, experimental)

On arm64 hardware with pointer authentication (PAC), build with -DMI_PAC=ON. Mimalloc uses secret hardware keys to sign pointers and validates them on access to detect tampering. On an M5, the overhead is usually about 10%, but can be 1 to 2x slower.

Combining -DMI_MTE=ON -DMI_PAC=ON provides strong mitigation against common heap exploitation techniques. Combined performance is usually within 5% of MTE alone. Mimalloc signs:

  • Free list links, so heap block overflows or use-after-free writes cannot forge links even when the per-page keys are known.
  • Page map entries, thread-local theap pointers, and other root pointers that may reside adjacent to user memory.

Authentication failures abort the process. PAC enables -DMI_SECURE=CHECKED and -DMI_SECURE_ENV=ON by default. -DMI_PAC=ON requires CPU and OS support at runtime and aborts at initialization otherwise. On other platforms, or with MSVC, -DMI_PAC=WEAK signs pointers using a random software process key. An attacker who can read memory may recover that key, but it still prevents forging pointers through blind overwrites.

PAC is part of Armv8.3-A and later. Supported examples include:

  • Apple A12 and M1 or later processors, on macOS and iOS.
  • Linux 5.0+ and Android on Arm Cortex-A710/X2 and later Armv9 cores, Arm Neoverse V1/V2/N2, AmpereOne, and Qualcomm Oryon. Check with grep pacg /proc/cpuinfo.

Older cores such as Cortex-A76/A78/X1, AWS Graviton2, Ampere Altra, and Raspberry Pi 4 and 5 do not support it.

Pointer Capabilities (CHERI)

(v3 only, experimental)

CHERI extends an architecture with capabilities: 128-bit pointers carrying bounds, permissions, and an unforgeable validity tag. Integers cannot be turned into valid pointers. Mimalloc supports the pure-capability ABI, bounding allocated blocks and keeping internal metadata unreachable through user capabilities. The default secure level is 3: CHECKED plus zeroing the first 1 KiB on free and double-free detection. Unlike MTE, CHERI does not prevent use-after-free by itself; that requires capability revocation.

CHERI is available on the Arm Morello research platform and some CHERI RISC-V cores, such as the Codasip X730, running CheriBSD. For a native pure-capability CheriBSD build, use cmake ../.. && make -j. Cross-compilation with CHERI LLVM requires the pure-capability flags, for example:

  • Morello: -DCMAKE_C_FLAGS="-march=morello -mabi=purecap".
  • CHERI-RISC-V: -DCMAKE_C_FLAGS="-march=rv64imafdcxcheri -mabi=l64pc128d".

We thank ARM and Ian Stark for providing a Morello system to test CHERI with mimalloc.