| # MemorySanitizer |
| |
| ```{contents} |
| :local: true |
| ``` |
| |
| ## Introduction |
| |
| MemorySanitizer is a detector of uninitialized memory use. It consists of a |
| compiler instrumentation module and a run-time library. |
| |
| Typical slowdown introduced by MemorySanitizer is **3x**. |
| |
| Here is a not comprehensive of list cases when MemorySanitizer will report an error: |
| |
| - Uninitialized value was used in a conditional branch. |
| - Uninitialized pointer was used for memory accesses. |
| - Uninitialized value was passed or returned from a function call, which is considered an undefined behavior. The check can be disabled with `-fno-sanitize-memory-param-retval`. |
| - Uninitialized data was passed into some libc calls. |
| |
| ## How to build |
| |
| Build LLVM/Clang with [CMake](https://llvm.org/docs/CMake.html). |
| |
| ## Usage |
| |
| Simply compile and link your program with `-fsanitize=memory` flag. |
| The MemorySanitizer run-time library should be linked to the final |
| executable, so make sure to use `clang` (not `ld`) for the final |
| link step. When linking shared libraries, the MemorySanitizer run-time |
| is not linked, so `-Wl,-z,defs` may cause link errors (don't use it |
| with MemorySanitizer). To get a reasonable performance add `-O1` or |
| higher. To get meaningful stack traces in error messages add |
| `-fno-omit-frame-pointer`. To get perfect stack traces you may need |
| to disable inlining (just use `-O1`) and tail call elimination |
| (`-fno-optimize-sibling-calls`). |
| |
| ```console |
| % cat umr.cc |
| #include <stdio.h> |
| |
| int main(int argc, char** argv) { |
| int* a = new int[10]; |
| a[5] = 0; |
| if (a[argc]) |
| printf("xx\n"); |
| return 0; |
| } |
| |
| % clang -fsanitize=memory -fno-omit-frame-pointer -g -O2 umr.cc |
| ``` |
| |
| If a bug is detected, the program will print an error message to |
| stderr and exit with a non-zero exit code. |
| |
| ```console |
| % ./a.out |
| WARNING: MemorySanitizer: use-of-uninitialized-value |
| #0 0x7f45944b418a in main umr.cc:6 |
| #1 0x7f45938b676c in __libc_start_main libc-start.c:226 |
| ``` |
| |
| By default, MemorySanitizer exits on the first detected error. If you |
| find the error report hard to understand, try enabling |
| {ref}`origin tracking <msan-origins>`. |
| |
| ### `__has_feature(memory_sanitizer)` |
| |
| In some cases one may need to execute different code depending on |
| whether MemorySanitizer is enabled. {ref}`__has_feature |
| <langext-__has_feature-__has_extension>` can be used for this purpose. |
| |
| ```c |
| #if defined(__has_feature) |
| # if __has_feature(memory_sanitizer) |
| // code that builds only under MemorySanitizer |
| # endif |
| #endif |
| ``` |
| |
| ### `__attribute__((no_sanitize("memory")))` |
| |
| Some code should not be checked by MemorySanitizer. One may use the function |
| attribute `no_sanitize("memory")` to disable uninitialized checks in a |
| particular function. MemorySanitizer may still instrument such functions to |
| avoid false positives. This attribute may not be supported by other compilers, |
| so we suggest to use it together with `__has_feature(memory_sanitizer)`. |
| |
| ### `__attribute__((disable_sanitizer_instrumentation))` |
| |
| The `disable_sanitizer_instrumentation` attribute can be applied to functions |
| to prevent all kinds of instrumentation. As a result, it may introduce false |
| positives and therefore should be used with care, and only if absolutely |
| required; for example for certain code that cannot tolerate any instrumentation |
| and resulting side-effects. This attribute overrides `no_sanitize("memory")`. |
| |
| ### Interaction of Inlining with Disabling Sanitizer Instrumentation |
| |
| - A `no_sanitize` function will not be inlined heuristically by the compiler into a sanitized function. |
| - An `always_inline` function will adopt the instrumentation status of the function it is inlined into. |
| - Forcibly combining `no_sanitize` and `__attribute__((always_inline))` is not supported, and will often lead to unexpected results. To avoid mixing these attributes, use: |
| |
| ```c |
| // Note, __has_feature test for sanitizers is deprecated, and Clang will support __SANITIZE_<sanitizer>__ similar to GCC. |
| #if __has_feature(memory_sanitizer) || defined(__SANITIZE_MEMORY__) || ... <other sanitizers> |
| #define ALWAYS_INLINE_IF_UNINSTRUMENTED |
| #else |
| #define ALWAYS_INLINE_IF_UNINSTRUMENTED __attribute__((always_inline)) |
| #endif |
| ``` |
| |
| ### Explicit Sanitizer Checks with `__builtin_allow_sanitize_check` |
| |
| The `__builtin_allow_sanitize_check("memory")` builtin can be used to |
| conditionally execute code depending on whether MemorySanitizer checks are |
| enabled and permitted by the current policy (after inlining). This is |
| particularly useful for inserting explicit, sanitizer-specific checks around |
| operations like syscalls or inline assembly, which might otherwise be unchecked |
| by the sanitizer. |
| |
| Example: |
| |
| ```c |
| void __msan_check_mem_is_initialized(const void *, size_t); |
| |
| inline __attribute__((always_inline)) |
| void my_send(void *addr, size_t size) { |
| if (__builtin_allow_sanitize_check("memory")) |
| __msan_check_mem_is_initialized(addr, size); |
| // ... syscall or other logic where MSan may not see the access ... |
| send(addr, size); |
| } |
| |
| void instrumented_function() { |
| ... |
| my_send(buf, sizeof(buf)); // checks are active |
| ... |
| } |
| |
| __attribute__((no_sanitize("memory"))) |
| void uninstrumented_function() { |
| ... |
| my_send(buf, sizeof(buf)); // checks are skipped |
| ... |
| } |
| ``` |
| |
| ### Ignorelist |
| |
| MemorySanitizer supports `src` and `fun` entity types in |
| {doc}`SanitizerSpecialCaseList`, that can be used to relax MemorySanitizer |
| checks for certain source files and functions. All "Use of uninitialized value" |
| warnings will be suppressed and all values loaded from memory will be |
| considered fully initialized. |
| |
| ## Report symbolization |
| |
| MemorySanitizer uses an external symbolizer to print files and line numbers in |
| reports. Make sure that `llvm-symbolizer` binary is in `PATH`, |
| or set environment variable `MSAN_SYMBOLIZER_PATH` to point to it. |
| |
| (msan-origins)= |
| |
| ## Origin Tracking |
| |
| MemorySanitizer can track origins of uninitialized values, similar to |
| Valgrind's --track-origins option. This feature is enabled by |
| `-fsanitize-memory-track-origins=2` (or simply |
| `-fsanitize-memory-track-origins`) Clang option. With the code from |
| the example above, |
| |
| ```console |
| % cat umr2.cc |
| #include <stdio.h> |
| |
| int main(int argc, char** argv) { |
| int* a = new int[10]; |
| a[5] = 0; |
| volatile int b = a[argc]; |
| if (b) |
| printf("xx\n"); |
| return 0; |
| } |
| |
| % clang -fsanitize=memory -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer -g -O2 umr2.cc |
| % ./a.out |
| WARNING: MemorySanitizer: use-of-uninitialized-value |
| #0 0x7f7893912f0b in main umr2.cc:7 |
| #1 0x7f789249b76c in __libc_start_main libc-start.c:226 |
| |
| Uninitialized value was stored to memory at |
| #0 0x7f78938b5c25 in __msan_chain_origin msan.cc:484 |
| #1 0x7f7893912ecd in main umr2.cc:6 |
| |
| Uninitialized value was created by a heap allocation |
| #0 0x7f7893901cbd in operator new[](unsigned long) msan_new_delete.cc:44 |
| #1 0x7f7893912e06 in main umr2.cc:4 |
| ``` |
| |
| By default, MemorySanitizer collects both allocation points and all |
| intermediate stores the uninitialized value went through. Origin |
| tracking has proved to be very useful for debugging MemorySanitizer |
| reports. It slows down program execution by a factor of 1.5x-2x on top |
| of the usual MemorySanitizer slowdown and increases memory overhead. |
| |
| Clang option `-fsanitize-memory-track-origins=1` enables a slightly |
| faster mode when MemorySanitizer collects only allocation points but |
| not intermediate stores. |
| |
| ## Use-after-destruction detection |
| |
| MemorySanitizer includes use-after-destruction detection. After invocation of |
| the destructor, the object will be considered no longer readable, and using |
| underlying memory will lead to error reports in runtime. Refer to the standard |
| for [lifetime](https://eel.is/c++draft/basic.life#1) definition. |
| |
| This feature can be disabled with either: |
| |
| 1. Pass additional Clang option `-fno-sanitize-memory-use-after-dtor` during |
| compilation. |
| 2. Set environment variable `MSAN_OPTIONS=poison_in_dtor=0` before running |
| the program. |
| |
| ## Handling external code |
| |
| MemorySanitizer requires that all program code is instrumented. This |
| also includes any libraries that the program depends on, even libc. |
| Failing to achieve this may result in false reports. |
| For the same reason you may need to replace all inline assembly code that writes to memory |
| with a pure C/C++ code. |
| |
| Full MemorySanitizer instrumentation is very difficult to achieve. To |
| make it easier, MemorySanitizer runtime library includes 70+ |
| interceptors for the most common libc functions. They make it possible |
| to run MemorySanitizer-instrumented programs linked with |
| uninstrumented libc. For example, the authors were able to bootstrap |
| MemorySanitizer-instrumented Clang compiler by linking it with |
| self-built instrumented libc++ (as a replacement for libstdc++). |
| |
| ## Security Considerations |
| |
| MemorySanitizer is a bug detection tool and its runtime is not meant to be |
| linked against production executables. While it may be useful for testing, |
| MemorySanitizer's runtime was not developed with security-sensitive |
| constraints in mind and may compromise the security of the resulting executable. |
| |
| ## Supported Platforms |
| |
| MemorySanitizer is supported on the following OS: |
| |
| - Linux |
| - NetBSD |
| - FreeBSD |
| |
| ## Limitations |
| |
| - MemorySanitizer uses 2x more real memory than a native run, 3x with |
| origin tracking. |
| - MemorySanitizer maps (but not reserves) 64 Terabytes of virtual |
| address space. This means that tools like `ulimit` may not work as |
| usually expected. |
| - Static linking is not supported. |
| - Older versions of MSan (LLVM 3.7 and older) didn't work with |
| non-position-independent executables, and could fail on some Linux |
| kernel versions with disabled ASLR. Refer to documentation for older versions |
| for more details. |
| - MemorySanitizer might be incompatible with position-independent executables |
| from FreeBSD 13 but there is a check done at runtime and throws a warning |
| in this case. |
| |
| ## Current Status |
| |
| MemorySanitizer is known to work on large real-world programs |
| (like Clang/LLVM itself) that can be recompiled from source, including all |
| dependent libraries. |
| |
| ## More Information |
| |
| [https://github.com/google/sanitizers/wiki/MemorySanitizer](https://github.com/google/sanitizers/wiki/MemorySanitizer) |