AddressSanitizer (ASan) is a fast compiler-based tool for detecting
memory bugs in native code.
ASan detects:
- Stack and heap buffer overflow/underflow
- Heap use after free
- Stack use outside scope
- Double free/wild free
ASan runs on both 32-bit and 64-bit ARM, plus x86 and x86-64. ASan's CPU overhead
is roughly 2x, code size overhead is between 50% and 2x, and a large memory overhead
(dependent on your allocation patterns, but on the order of 2x).
Android 10 and the AOSP main branch on AArch64
support
Hardware-assisted AddressSanitizer (HWASan)
,
a similar tool with lower RAM overhead and a larger
range of detected bugs. HWASan detects stack use after return, in addition to the bugs
detected by ASan.
HWASan has similar CPU and code size overhead, but a much smaller RAM overhead (15%).
HWASan is nondeterministic. There are only 256 possible tag values, so there's a flat 0.4%
probability of missing any bug. HWASan doesn't have ASan's limited-size red zones for
detecting overflows and limited-capacity quarantine for detecting use-after-free,
so it doesn't matter to HWASan how large the overflow is or how long ago the memory
was deallocated. This makes HWASan better than ASan. You can read more about the
design of
HWASan
or about the use of
HWASan on Android
.
ASan detects stack/global overflows
in addition to heap overflows, and is fast with minimal memory overhead.
This document describes how to build and run parts/all of Android with
ASan. If you're building an SDK/NDK app with ASan, see
Address Sanitizer
instead.
Sanitize individual executables with ASan
Add
LOCAL_SANITIZE:=address
or
sanitize: { address: true }
to
the build rule for the executable. You can search the code for existing examples or to find
the other available sanitizers.
When a bug is detected, ASan prints a verbose report both to the standard
output and to
logcat
and then crashes the process.
Sanitize shared libraries with ASan
Due to the way ASan works, a library built with ASan can only be used by an
executable that's built with ASan.
To sanitize a shared library that's used in multiple executables, not all of
which are built with ASan, you need two copies of the library. The
recommended way to do this is to add the following to
Android.mk
for the module in question:
LOCAL_SANITIZE:=address
LOCAL_MODULE_RELATIVE_PATH := asan
This puts the library in
/system/lib/asan
instead of
/system/lib
. Then, run your executable with:
LD_LIBRARY_PATH=/system/lib/asan
For system daemons, add the following to the appropriate section of
/init.rc
or
/init.$device$.rc
.
setenv LD_LIBRARY_PATH /system/lib/asan
Verify that the process is using libraries from
/system/lib/asan
when present by reading
/proc/$PID/maps
. If it's not, you may need
to disable SELinux:
adb root
adb shell setenforce 0
# restart the process with adb shell kill $PID
# if it is a system service, or may be adb shell stop; adb shell start.
Better stack traces
ASan uses a fast, frame-pointer-based unwinder to record a stack
trace for every memory allocation and deallocation event in the program. Most
of Android is built without frame pointers. As a result, you often get
only one or two meaningful frames. To fix this, either rebuild the library with
ASan (recommended!), or with:
LOCAL_CFLAGS:=-fno-omit-frame-pointer
LOCAL_ARM_MODE:=arm
Or set
ASAN_OPTIONS=fast_unwind_on_malloc=0
in the process
environment. The latter can be very CPU-intensive, depending on
the load.
Symbolization
Initially, ASan reports contain references to offsets in binaries and shared
libraries. There are two ways to obtain source file and line information:
- Ensure that the
llvm-symbolizer
binary is present in
/system/bin
.
llvm-symbolizer
is built from sources in
third_party/llvm/tools/llvm-symbolizer
.
- Filter the report through the
external/compiler-rt/lib/asan/scripts/symbolize.py
script.
The second approach can provide more data (that is,
file:line
locations) because of
the availability of symbolized libraries on the host.
ASan in apps
ASan can't see into Java code, but it can detect bugs in the JNI
libraries. For that, you need to build the executable with ASan, which in
this case is
/system/bin/app_process(
32|64
)
. This
enables ASan in all apps on the device at the same time, which is a
heavy load, but a device with 2 GB RAM should be able to handle this.
Add
LOCAL_SANITIZE:=address
to
the
app_process
build rule in
frameworks/base/cmds/app_process
. Ignore
the
app_process__asan
target in the same file for now (if it's
still there at the time you read this).
Edit the
service zygote
section of the
appropriate
system/core/rootdir/init.zygote(
32|64
).rc
file to add the
following lines to the block of indented lines containing
class main
, also
indented by the same amount:
setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib
setenv ASAN_OPTIONS allow_user_segv_handler=true
Build, adb sync, fastboot flash boot, and reboot.
Use the wrap property
The approach in the previous section puts ASan into every
app in the system (actually, into every descendant of the Zygote
process). It's possible to run only one (or several) apps with ASan,
trading some memory overhead for slower app startup.
This can be done by starting your app with the
wrap.
property.
The following example runs the Gmail app under ASan:
adb root
adb shell setenforce 0 # disable SELinux
adb shell setprop wrap.com.google.android.gm "asanwrapper"
In this context,
asanwrapper
rewrites
/system/bin/app_process
to
/system/bin/asan/app_process
, which is built with
ASan. It also adds
/system/lib/asan
at the start of
the dynamic library search path. This way ASan-instrumented
libraries from
/system/lib/asan
are preferred to normal libraries
in
/system/lib
when running with
asanwrapper
.
If a bug is found, the app crashes, and the report is printed to
the log.
SANITIZE_TARGET
Android 7.0 and higher includes support for building the entire Android platform with
ASan at once. (If you're building a release higher than Android 9, HWASan is a better choice.)
Run the following commands in the same build tree.
make -j42
SANITIZE_TARGET=address make -j42
In this mode,
userdata.img
contains extra libraries and must be
flashed to the device as well. Use the following command line:
fastboot flash userdata && fastboot flashall
This builds two sets of shared libraries: normal in
/system/lib
(the first make invocation), and ASan-instrumented in
/data/asan/lib
(the second make invocation). Executables from the
second build overwrite those from the first build. ASan-instrumented
executables get a different library search path that includes
/data/asan/lib
before
/system/lib
through the use of
/system/bin/linker_asan
in
PT_INTERP
.
The build system clobbers intermediate object directories when the
$SANITIZE_TARGET
value has changed. This forces a rebuild of all
targets while preserving installed binaries under
/system/lib
.
Some targets can't be built with ASan:
- Statically linked executables
LOCAL_CLANG:=false
targets
LOCAL_SANITIZE:=false
aren't ASan'd for
SANITIZE_TARGET=address
Executables like these are skipped in the
SANITIZE_TARGET
build, and the
version from the first make invocation is left in
/system/bin
.
Libraries like this are built without ASan. They can contain some ASan
code from the static libraries that they depend upon.
Supporting documentation