AddressSanitizer
Address Sanitizer or ASAN is a compiler feature in GCC and Clang that is able to detect several memory access errors. It thereby adds a certain level of memory safety to C/C++ code. Address Sanitizer is enabled with the compiler flag -fsanitize=address
.
Why use AddressSanitizer?
Address Sanitizer is a very powerful debugging feature. It is not a hardening feature.
It provides two related benefits:
- Discovery of memory safety issues which weren't visible before
- Reducing the impact of a memory safety issue to denial of service (because the program will abort)
It is much faster than Valgrind and works fine with newer instruction sets like AVX512.
Disadvantages and limitations
Address Sanitizer has significant performance and memory costs. Expect a system that is 50-100% slower and needs a lot of RAM.
It is incompatible with Valgrind, but it can't detect everything Valgrind can (like uninitialized memory use).
It is not possible to use an application that is not using Address Sanitizer with a library that has been compiled with Address Sanitizer. Therefore it is often not possible to exclude single applications from Address Sanitizer usage - you also have to avoid using Address Sanitizer for all dependencies.
It is unsafe to use in production with suid binaries especially because the libsanitizer runtime uses environment variables that may allow privilege escalation.
Configuration
ASan can only be configured via two methods:
- additional flags passed to the compiler at build time (see the relevant compiler's documentation for examples)
- ASAN_OPTIONS if not using the minimal runtime, options are delimited by colon (:)
It may be useful to run the following to disable LeakSanitizer which reports memory leaks:
user $
export ASAN_OPTIONS="detect_leaks=0"
Use
Sanitizers are not yet compatible with FORTIFY_SOURCE and may prevent some issues being found. On balance, sanitizers may be better for debugging, while _FORTIFY_SOURCE (which Gentoo enables by default) is better for production use. If ASan misses a result unexpectedly, try compiling with -U_FORTIFY_SOURCE.
The compiler must be built with sanitizer support:
- For GCC, the sanitize USE flag must be enabled on sys-devel/gcc
- For Clang, asan must be enabled on sys-libs/compiler-rt-sanitizers
Example
First, create the following incorrect C program:
#include <stdio.h>
/* Contains 3 elements:
- numbers[0] = 1
- numbers[1] = 2
- numbers[2] = 3
*/
int numbers[] = { 1, 2, 3 };
int main() {
/* numbers[3] is out of bounds, as it doesn't exist. This is a classic
off-by-one error.
*/
printf("The 4th number in my array is: %i\n", numbers[4]);
}
Compile it:
user $
cc -O2 -g -fsanitize=address /tmp/outofbounds.c -o /tmp/outofbounds
Run it:
user $
/tmp/outofbounds
==705829==ERROR: AddressSanitizer: global-buffer-overflow on address 0x5618b592d030 at pc 0x5618b592a16a bp 0x7ffc395483d0 sp 0x7ffc395483c0 READ of size 4 at 0x5618b592d030 thread T0 #0 0x5618b592a169 in main /tmp/outofbounds.c:14 #1 0x7f5f02009d4f (/usr/lib64/libc.so.6+0x23d4f) #2 0x7f5f02009e08 in __libc_start_main (/usr/lib64/libc.so.6+0x23e08) #3 0x5618b592a1c4 in _start (/tmp/outofbounds+0x11c4) 0x5618b592d030 is located 4 bytes after global variable 'numbers' defined in '/tmp/outofbounds.c' (0x5618b592d020) of size 12 SUMMARY: AddressSanitizer: global-buffer-overflow /tmp/outofbounds.c:14 in main Shadow bytes around the buggy address: 0x5618b592cd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x5618b592ce00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x5618b592ce80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x5618b592cf00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x5618b592cf80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x5618b592d000: 00 00 00 00 00 04[f9]f9 f9 f9 f9 f9 00 00 00 00 0x5618b592d080: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 0x5618b592d100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x5618b592d180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x5618b592d200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x5618b592d280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb ==705829==ABORTING
ASan has intercepted the allocator and trapped the out-of-bounds access at runtime.
Per-package
The most sensible use of Address Sanitizer (or other sanitizers) is temporary and per-package using /etc/portage/package.env.
Create a file in /etc/portage/env as follows:
CFLAGS="${CFLAGS} -fsanitize=address"
CXXFLAGS="${CXXFLAGS} -fsanitize=address"
LDFLAGS="${LDFLAGS} -fsanitize=address"
# Only relevant if package uses Meson
# For Meson packages, it might be necessary to comment out the *FLAGS lines above
# and only set MYMESONARGS.
MYMESONARGS="-Db_sanitize=address"
# Needed because ASAN uses interposition (LD_PRELOAD)
# Other sanitizers don't need this.
FEATURES="${FEATURES} -sandbox -usersandbox"
Next, add an entry to package.env followed by the name of the file created in the previous step:
app-misc/hello asan.conf
Then re-emerge the relevant package(s):
root #
emerge --oneshot --usepkg=n app-misc/hello
System-wide use
Using Gentoo with Address Sanitizer is highly experimental. You will almost certainly face problems. If you want to try this you should be willing to play around with things and help fixing bugs. You probably don't want to use this for any production systems. It also introduces security risks like local root exploits and is not suitable for production use.
It is possible to compile a Gentoo system with Address Sanitizer for experimentation, but not advised for production environments. Just trying to do this will automatically uncover many bugs in upstream applications. This project helps to improve the general quality of the upstream software Gentoo uses.
Asantoo stage tarball
The easiest way to test Gentoo with Address Sanitizer is using a prepared asantoo stage 3 tarball. With this tarball, a normal Gentoo installation can be started.
Differences between the "normal" Gentoo stage 3 tarball and the asantoo tarball:
- It includes the asantoo overlay. The overlay contains several packages with fixes or workarounds needed to let them compile with Address Sanitizer
- It has git installed (to sync the overlay).
- Address Sanitizer CFLAGS/CXXFLAGS/LDFLAGS have been added to make.conf.
- Some core packages (gcc, glibc, their dependencies and some other problematic packages) have been excluded from the compilation with Address Sanitizer through package.env.
- Some quirks workaround existing problems, these include a Portage hook that fixes libtool scripts and a workaround for pthread detection for some packages.
- An env file causes Address Sanitizer errors to be logged to /var/log/asan.
Booting
The easiest way to play around with this is to just use it in a chroot environment. Booting such a system poses some challenges.
Address Sanitizer only works if /proc is mounted. However as you will compile everything with Address Sanitizer the init system can't work that way. You will need an initramfs. This can be created with genkernel, however please note that you need to create the initramfs on a normal Gentoo system.
Bugs
You will almost certainly run into various compilation and runtime issues with many software packages. Some common issues are described on the Problems page.
See also
- UndefinedBehaviorSanitizer — a compiler feature in GCC and Clang that is able to detect various forms of undefined behaviour (UB).
- Valgrind — dynamic analysis tool which detects memory errors and memory leaks.