| # AddressSanitizer |
| |
| ```{contents} |
| :local: true |
| ``` |
| |
| ## Introduction |
| |
| AddressSanitizer is a fast memory error detector. It consists of a compiler |
| instrumentation module and a run-time library. The tool can detect the |
| following types of bugs: |
| |
| - Out-of-bounds accesses to heap, stack and globals |
| - Use-after-free |
| - Use-after-return (clang flag `-fsanitize-address-use-after-return=(never|runtime|always)` default: `runtime`) |
| : - Enable with: `ASAN_OPTIONS=detect_stack_use_after_return=1` (already enabled on Linux). |
| - Disable with: `ASAN_OPTIONS=detect_stack_use_after_return=0`. |
| - Use-after-scope (clang flag `-fsanitize-address-use-after-scope`) |
| - Double-free, invalid free |
| - Memory leaks (experimental) |
| |
| Typical slowdown introduced by AddressSanitizer is **2x**. |
| |
| ## How to build |
| |
| Build LLVM/Clang with [CMake](https://llvm.org/docs/CMake.html) and enable |
| the `compiler-rt` runtime. An example CMake configuration that will allow |
| for the use/testing of AddressSanitizer: |
| |
| ```console |
| $ cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang" -DLLVM_ENABLE_RUNTIMES="compiler-rt" <path to source>/llvm |
| ``` |
| |
| ## Usage |
| |
| Simply compile and link your program with `-fsanitize=address` flag. The |
| AddressSanitizer 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 AddressSanitizer run-time is not linked, so |
| `-Wl,-z,defs` may cause link errors (don't use it with AddressSanitizer). To |
| get a reasonable performance add `-O1` or higher. To get nicer 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 example_UseAfterFree.cc |
| int main(int argc, char **argv) { |
| int *array = new int[100]; |
| delete [] array; |
| return array[argc]; // BOOM |
| } |
| |
| # Compile and link |
| % clang++ -O1 -g -fsanitize=address -fno-omit-frame-pointer example_UseAfterFree.cc |
| ``` |
| |
| or: |
| |
| ```console |
| # Compile |
| % clang++ -O1 -g -fsanitize=address -fno-omit-frame-pointer -c example_UseAfterFree.cc |
| # Link |
| % clang++ -g -fsanitize=address example_UseAfterFree.o |
| ``` |
| |
| If a bug is detected, the program will print an error message to stderr and |
| exit with a non-zero exit code. AddressSanitizer exits on the first detected error. |
| This is by design: |
| |
| - This approach allows AddressSanitizer to produce faster and smaller generated code |
| (both by ~5%). |
| - Fixing bugs becomes unavoidable. AddressSanitizer does not produce |
| false alarms. Once a memory corruption occurs, the program is in an inconsistent |
| state, which could lead to confusing results and potentially misleading |
| subsequent reports. |
| |
| If your process is sandboxed and you are running on OS X 10.10 or earlier, you |
| will need to set `DYLD_INSERT_LIBRARIES` environment variable and point it to |
| the ASan library that is packaged with the compiler used to build the |
| executable. (You can find the library by searching for dynamic libraries with |
| `asan` in their name.) If the environment variable is not set, the process will |
| try to re-exec. Also keep in mind that when moving the executable to another machine, |
| the ASan library will also need to be copied over. |
| |
| ## Symbolizing the Reports |
| |
| To make AddressSanitizer symbolize its output |
| you need to set the `ASAN_SYMBOLIZER_PATH` environment variable to point to |
| the `llvm-symbolizer` binary (or make sure `llvm-symbolizer` is in your |
| `$PATH`): |
| |
| ```console |
| % ASAN_SYMBOLIZER_PATH=/usr/local/bin/llvm-symbolizer ./a.out |
| ==9442== ERROR: AddressSanitizer heap-use-after-free on address 0x7f7ddab8c084 at pc 0x403c8c bp 0x7fff87fb82d0 sp 0x7fff87fb82c8 |
| READ of size 4 at 0x7f7ddab8c084 thread T0 |
| #0 0x403c8c in main example_UseAfterFree.cc:4 |
| #1 0x7f7ddabcac4d in __libc_start_main ??:0 |
| 0x7f7ddab8c084 is located 4 bytes inside of 400-byte region [0x7f7ddab8c080,0x7f7ddab8c210) |
| freed by thread T0 here: |
| #0 0x404704 in operator delete[](void*) ??:0 |
| #1 0x403c53 in main example_UseAfterFree.cc:4 |
| #2 0x7f7ddabcac4d in __libc_start_main ??:0 |
| previously allocated by thread T0 here: |
| #0 0x404544 in operator new[](unsigned long) ??:0 |
| #1 0x403c43 in main example_UseAfterFree.cc:2 |
| #2 0x7f7ddabcac4d in __libc_start_main ??:0 |
| ==9442== ABORTING |
| ``` |
| |
| If that does not work for you (e.g. your process is sandboxed), you can use a |
| separate script to symbolize the result offline (online symbolization can be |
| force disabled by setting `ASAN_OPTIONS=symbolize=0`): |
| |
| ```console |
| % ASAN_OPTIONS=symbolize=0 ./a.out 2> log |
| % projects/compiler-rt/lib/asan/scripts/asan_symbolize.py / < log | c++filt |
| ==9442== ERROR: AddressSanitizer heap-use-after-free on address 0x7f7ddab8c084 at pc 0x403c8c bp 0x7fff87fb82d0 sp 0x7fff87fb82c8 |
| READ of size 4 at 0x7f7ddab8c084 thread T0 |
| #0 0x403c8c in main example_UseAfterFree.cc:4 |
| #1 0x7f7ddabcac4d in __libc_start_main ??:0 |
| ... |
| ``` |
| |
| Note that on macOS you may need to run `dsymutil` on your binary to have the |
| file:line info in the AddressSanitizer reports. |
| |
| ## Additional Checks |
| |
| ### Initialization order checking |
| |
| AddressSanitizer can optionally detect dynamic initialization order problems, |
| when initialization of globals defined in one translation unit uses |
| globals defined in another translation unit. To enable this check at runtime, |
| you should set environment variable |
| `ASAN_OPTIONS=check_initialization_order=1`. |
| |
| Note that this option is not supported on macOS. |
| |
| ### Stack Use After Return (UAR) |
| |
| AddressSanitizer can optionally detect stack use after return problems. |
| This is available by default, or explicitly |
| (`-fsanitize-address-use-after-return=runtime`). |
| To disable this check at runtime, set the environment variable |
| `ASAN_OPTIONS=detect_stack_use_after_return=0`. |
| |
| Enabling this check (`-fsanitize-address-use-after-return=always`) will |
| reduce code size. The code size may be reduced further by completely |
| eliminating this check (`-fsanitize-address-use-after-return=never`). |
| |
| To summarize: `-fsanitize-address-use-after-return=<mode>` |
| : - `never`: Completely disables detection of UAR errors (reduces code size). |
| - `runtime`: Adds the code for detection, but it can be disabled via the |
| runtime environment (`ASAN_OPTIONS=detect_stack_use_after_return=0`). |
| - `always`: Enables detection of UAR errors in all cases. (reduces code |
| size, but not as much as `never`). |
| |
| ### Container Overflow Detection |
| |
| AddressSanitizer can detect overflows in containers with custom allocators |
| (such as std::vector) where the library developers have added calls into the |
| AddressSanitizer runtime to indicate which memory is poisoned etc. |
| |
| Note that this feature is prone to false positives: |
| : - Partially poisoning objects on stack, e.g. for small string optimization, can |
| cause both false positives and negatives. |
| - If the binary is partially AddressSanitizer instrumented, these |
| checks can cause false positives. |
| |
| See [Disabling container overflow checks](#disabling-container-overflow-checks) for details on suppressing checks. |
| |
| ### Memory leak detection |
| |
| For more information on leak detector in AddressSanitizer, see |
| {doc}`LeakSanitizer`. The leak detection is turned on by default on Linux, |
| and can be enabled using `ASAN_OPTIONS=detect_leaks=1` on macOS; |
| however, it is not yet supported on other platforms. |
| |
| ## Issue Suppression |
| |
| AddressSanitizer is not expected to produce false positives. If you see one, |
| look again; most likely it is a true positive! |
| |
| ### Suppressing Reports in External Libraries |
| |
| Runtime interposition allows AddressSanitizer to find bugs in code that is |
| not being recompiled. If you run into an issue in external libraries, we |
| recommend immediately reporting it to the library maintainer so that it |
| gets addressed. However, you can use the following suppression mechanism |
| to unblock yourself and continue on with the testing. This suppression |
| mechanism should only be used for suppressing issues in external code; it |
| does not work on code recompiled with AddressSanitizer. To suppress errors |
| in external libraries, set the `ASAN_OPTIONS` environment variable to point |
| to a suppression file. You can either specify the full path to the file or the |
| path of the file relative to the location of your executable. |
| |
| ```bash |
| ASAN_OPTIONS=suppressions=MyASan.supp |
| ``` |
| |
| Use the following format to specify the names of the functions or libraries |
| you want to suppress. You can see these in the error report. Remember that |
| the narrower the scope of the suppression, the more bugs you will be able to |
| catch. |
| |
| ```bash |
| interceptor_via_fun:NameOfCFunctionToSuppress |
| interceptor_via_fun:-[ClassName objCMethodToSuppress:] |
| interceptor_via_lib:NameOfTheLibraryToSuppress |
| ``` |
| |
| ### Conditional Compilation with `__has_feature(address_sanitizer)` |
| |
| In some cases one may need to execute different code depending on whether |
| AddressSanitizer is enabled. |
| {ref}`__has_feature <langext-__has_feature-__has_extension>` can be used for |
| this purpose. |
| |
| ```c |
| #if defined(__has_feature) |
| # if __has_feature(address_sanitizer) |
| // code that builds only under AddressSanitizer |
| # endif |
| #endif |
| ``` |
| |
| ### Disabling Instrumentation with `__attribute__((no_sanitize("address")))` |
| |
| Some code should not be instrumented by AddressSanitizer. One may use |
| the attribute `__attribute__((no_sanitize("address")))` (which has |
| deprecated synonyms `no_sanitize_address` and |
| `no_address_safety_analysis`) to disable instrumentation of a |
| particular function. This attribute may not be supported by other |
| compilers, so we suggest to use it together with |
| `__has_feature(address_sanitizer)`. |
| |
| The same attribute used on a global variable prevents AddressSanitizer |
| from adding redzones around it and detecting out of bounds accesses. |
| |
| AddressSanitizer also supports |
| `__attribute__((disable_sanitizer_instrumentation))`. This attribute |
| works similarly to `__attribute__((no_sanitize("address")))`, but it also |
| prevents instrumentation performed by other sanitizers. |
| |
| ### 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(address_sanitizer) || defined(__SANITIZE_ADDRESS__) || ... <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("address")` builtin can be used to |
| conditionally execute code depending on whether AddressSanitizer 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 __asan_load8(void *); |
| |
| inline __attribute__((always_inline)) |
| void my_helper(void *addr) { |
| if (__builtin_allow_sanitize_check("address")) |
| __asan_load8(addr); |
| // ... actual logic, e.g. inline assembly ... |
| asm volatile ("..." : : "r" (addr) : "memory"); |
| } |
| |
| void instrumented_function() { |
| ... |
| my_helper(buf); // checks are active |
| ... |
| } |
| |
| __attribute__((no_sanitize("address"))) |
| void uninstrumented_function() { |
| ... |
| my_helper(buf); // checks are skipped |
| ... |
| } |
| ``` |
| |
| ### Disabling container overflow checks |
| |
| #### Runtime suppression |
| |
| Container overflow checks can be disabled at runtime using the |
| `ASAN_OPTIONS=detect_container_overflow=0` environment variable. |
| |
| #### Compile time suppression |
| |
| `-D__SANITIZER_DISABLE_CONTAINER_OVERFLOW__` can be used at compile time to |
| disable container overflow checks if the container library has added support |
| for this define. |
| |
| To support a standard way to disable container overflow checks at compile time, |
| library developers should use this definition in conjunction with the |
| AddressSanitizer feature test to conditionally include container overflow |
| related code compiled into user code: |
| |
| The recommended form is |
| |
| ```c |
| // include the sanitizer common interfaces |
| #include <sanitizer/common_interface_defs.h> |
| |
| #if __has_feature(address_sanitizer) |
| // Container overflow detection enabled - include annotations |
| __sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid); |
| #endif |
| ``` |
| |
| This pattern ensures that: |
| |
| - Container overflow annotations are only included when AddressSanitizer is |
| enabled |
| - Container overflow detection can be disabled by passing |
| `-D__SANITIZER_DISABLE_CONTAINER_OVERFLOW__` to the compiler |
| |
| ### Suppressing Errors in Recompiled Code (Ignorelist) |
| |
| AddressSanitizer supports `src` and `fun` entity types in |
| {doc}`SanitizerSpecialCaseList`, that can be used to suppress error reports |
| in the specified source files or functions. Additionally, AddressSanitizer |
| introduces `global` and `type` entity types that can be used to |
| suppress error reports for out-of-bound access to globals with certain |
| names and types (you may only specify class or struct types). |
| |
| You may use an `init` category to suppress reports about initialization-order |
| problems happening in certain source files or with certain global variables. |
| |
| ```bash |
| # Suppress error reports for code in a file or in a function: |
| src:bad_file.cpp |
| # Ignore all functions with names containing MyFooBar: |
| fun:*MyFooBar* |
| # Disable out-of-bound checks for global: |
| global:bad_array |
| # Disable out-of-bound checks for global instances of a given class ... |
| type:Namespace::BadClassName |
| # ... or a given struct. Use wildcard to deal with anonymous namespace. |
| type:Namespace2::*::BadStructName |
| # Disable initialization-order checks for globals: |
| global:bad_init_global=init |
| type:*BadInitClassSubstring*=init |
| src:bad/init/files/*=init |
| ``` |
| |
| ### Suppressing memory leaks |
| |
| Memory leak reports produced by {doc}`LeakSanitizer` (if it is run as a part |
| of AddressSanitizer) can be suppressed by a separate file passed as |
| |
| ```bash |
| LSAN_OPTIONS=suppressions=MyLSan.supp |
| ``` |
| |
| which contains lines of the form `leak:<pattern>`. Memory leak will be |
| suppressed if pattern matches any function name, source file name, or |
| library name in the symbolized stack trace of the leak report. See |
| [full documentation](https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer#suppressions) |
| for more details. |
| |
| ## Code generation control |
| |
| ### Instrumentation code outlining |
| |
| By default AddressSanitizer inlines the instrumentation code to improve the |
| run-time performance, which leads to increased binary size. Using the |
| (clang flag `-fsanitize-address-outline-instrumentation` default: `false`) |
| flag forces all code instrumentation to be outlined, which reduces the size |
| of the generated code, but also reduces the run-time performance. |
| |
| ## Limitations |
| |
| - AddressSanitizer uses more real memory than a native run. Exact overhead |
| depends on the allocation sizes. The smaller the allocations you make the |
| bigger the overhead is. |
| - AddressSanitizer uses more stack memory. We have seen up to 3x increase. |
| - On 64-bit platforms AddressSanitizer maps (but not reserves) 16+ Terabytes of |
| virtual address space. This means that tools like `ulimit` may not work as |
| usually expected. |
| - Static linking of executables is not supported. |
| |
| ## Security Considerations |
| |
| AddressSanitizer is a bug detection tool and its runtime is not meant to be |
| linked against production executables. While it may be useful for testing, |
| AddressSanitizer's runtime was not developed with security-sensitive |
| constraints in mind and may compromise the security of the resulting executable. |
| |
| ## Supported Platforms |
| |
| AddressSanitizer is supported on: |
| |
| - Linux |
| - macOS |
| - iOS Simulator |
| - Android |
| - NetBSD |
| - FreeBSD |
| - Windows 8.1+ |
| |
| ## Current Status |
| |
| AddressSanitizer is fully functional on supported platforms starting from LLVM |
| 3.1. The test suite is integrated into CMake build and can be run with `make |
| check-asan` command. |
| |
| The Windows port is functional and is used by Chrome and Firefox, but it is not |
| as well supported as the other ports. |
| |
| ## More Information |
| |
| [https://github.com/google/sanitizers/wiki/AddressSanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer) |