aboutsummaryrefslogtreecommitdiff
path: root/en/devices/tech/debug/asan.html
blob: 9ea32939d426cf2f38a92af1c6be916254fbbb8a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
<html devsite>
  <head>
    <title>AddressSanitizer</title>
    <meta name="project_path" value="/_project.yaml" />
    <meta name="book_path" value="/_book.yaml" />
  </head>
  <body>
  <!--
      Copyright 2017 The Android Open Source Project

      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      See the License for the specific language governing permissions and
      limitations under the License.
  -->

<p>AddressSanitizer (ASan) is a fast compiler-based tool for detecting memory bugs
in native code. Android supports both regular ASan and hardware-accelerated ASan (HWASan).
HWAsan is based on memory tagging and is only available on AArch64 because it relies on
the Top-Byte-Ignore feature.</p>

<p>These tools detect:</p>
<ul>
<li>Stack and heap buffer overflow/underflow.
<li>Heap use after free.
<li>Stack use outside scope.
<li>Stack use after return (HWAsan only on Android).
<li>Double free/wild free.
</ul>

<p>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).</p>

<p>HWASan has similar CPU and code size overheads, but a much smaller RAM overhead (15%).
HWASan is non-deterministic. There are only 256 possible tag values, so there is a flat 0.4%
probability of missing any bug. HWAsan does not have ASan's limited-size redzones for
detecting overflows and limited-capacity quarantine for detecting use-after-free,
so it does not matter to HWAsan how large the overflow is or how long ago the memory
was deallocated. This makes HWASan better than ASan.</p>

<p>Valgrind's Memcheck tool is similar, but ASan also detects stack/global overflows
in addition to heap overflows, and is much faster with less memory overhead. Conversely,
Valgrind detects uninitialized reads and memory leaks that ASan does not.
Valgrind may be useful for debugging apps but is not practical for the entire OS,
which is why the Android team uses ASan instead.</p>

<p>This document describes how to build and run parts/all of the Android OS itself with
AddressSanitizer. If you are building an SDK/NDK application with AddressSanitizer, see
<a href="https://github.com/google/sanitizers/wiki/AddressSanitizerOnAndroid" class="external">AddressSanitizerOnAndroid</a>
instead.</p>


<h2 id="using-hwasan">Using HWAsan</h2>

<p>As of December 2018 only Pixel 2 and Pixel 2 XL are supported. Supporting another device
requires backporting several kernel patches. The Android team is working on getting those into
the common kernel.
You may also need to remove some optional extras to make room on your system partition for the
larger libraries. See the <code>walleye_hwasan</code> target for an example.</p>

<p>Use the following commands to build the entire platform using HWASan:</p>

<pre class="prettyprint">
<code class="devsite-terminal">lunch walleye_hwasan-userdebug</code>
<code class="devsite-terminal">make SANITIZE_TARGET=hwaddress</code>
</pre>

<p>Unlike ASan, with HWASan there's no need to build twice, incremental builds just work,
there are no special flashing instructions or wiping, static executables are supported,
and it's okay to skip sanitization of any library other than <code>libc</code>.
There's also no requirement that if a library is sanitized, any executable that links
to it must also be sanitized.

<p>To skip sanitization of a module, use <code>LOCAL_NOSANITIZE := hwaddress</code> or
<code>sanitize: { hwaddress: false }</code>.</p>

<aside class="note">
<strong>Note:</strong> Currently there is no support for sanitizing individual modules with HWASan. Use ASan to sanitize individual modules.
</aside>

<h2 id="sanitizing_individual_executables_with_asan">Sanitizing individual executables with ASan</h2>

<p>Add <code>LOCAL_SANITIZE:=address</code> or <code>sanitize: { address: true } }</code> to
the build rule for the executable. You can search the code for existing examples or to find
the other available sanitizers.</p>

<p>When a bug is detected, ASan prints a verbose report both to the standard
output and to <code>logcat</code> and then crashes the process.</p>

<h2 id="sanitizing_shared_libraries_with_asan">Sanitizing shared libraries with ASan</h2>

<p>Due to the way ASan works, a library built with ASan cannot be used by an
executable that's built without ASan.</p>

<p class="note"><strong>Note</strong>: In runtime situations where an ASan library is
loaded into an incorrect process, you will see unresolved symbol messages
starting with <code>_asan</code> or <code>_sanitizer</code>.</p>

<p>To sanitize a shared library that is used in multiple executables, not all of
which are built with ASan, you'll need two copies of the library. The
recommended way to do this is to add the following to <code>Android.mk</code>
for the module in question:</p>

<pre class="devsite-click-to-copy">
LOCAL_SANITIZE:=address
LOCAL_MODULE_RELATIVE_PATH := asan
</pre>

<p>This puts the library in <code>/system/lib/asan</code> instead of
<code>/system/lib</code>. Then, run your executable with:
<code>LD_LIBRARY_PATH=/system/lib/asan</code></p>

<p>For system daemons, add the following to the appropriate section of
<code>/init.rc</code> or <code>/init.$device$.rc</code>.</p>

<pre class="devsite-click-to-copy">
setenv LD_LIBRARY_PATH /system/lib/asan
</pre>

<p class="warning"><strong>Warning</strong>: The <code>LOCAL_MODULE_RELATIVE_PATH</code>
setting <strong>moves</strong> your library to <code>/system/lib/asan</code>,
meaning that clobbering and rebuilding from scratch will result in the
library missing from <code>/system/lib</code>, and probably an unbootable
image. That's an unfortunate limitation of the
current build system. Don't clobber; do <code>make -j $N</code> and <code>adb
sync</code>.</p>

<p>Verify the process is using libraries from <code>/system/lib/asan</code>
when present by reading <code>/proc/$PID/maps</code>. If it's not, you may need
to disable SELinux, like so:</p>

<pre class="devsite-click-to-copy">
<code class="devsite-terminal">adb root</code>
<code class="devsite-terminal">adb shell setenforce 0</code>
# restart the process with adb shell kill $PID
# if it is a system service, or may be adb shell stop; adb shell start.
</pre>

<h2 id=better_stack_traces>Better stack traces</h2>

<p>AddressSanitizer 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 will often get
only one or two meaningful frames. To fix this, either rebuild the library with
ASan (recommended!), or with:</p>

<pre class="devsite-click-to-copy">
LOCAL_CFLAGS:=-fno-omit-frame-pointer
LOCAL_ARM_MODE:=arm
</pre>

<p>Or set <code>ASAN_OPTIONS=fast_unwind_on_malloc=0</code> in the process
environment. The latter can be very CPU-intensive, depending on
the load.</p>

<h2 id=symbolization>Symbolization</h2>

<p>Initially, ASan reports contain references to offsets in binaries and shared
libraries. There are two ways to obtain source file and line information:</p>

<ul>
  <li>Ensure llvm-symbolizer binary is present in <code>/system/bin</code>.
Llvm-symbolizer is built from sources in:
<code>third_party/llvm/tools/llvm-symbolizer</code> <li>Filter the report
through the <code>external/compiler-rt/lib/asan/scripts/symbolize.py</code>
script.
</ul>

<p>The second approach can provide more data (i.e. file:line locations) because of
the availability of symbolized libraries on the host.</p>

<h2 id=addresssanitizer_in_the_apps>AddressSanitizer in apps</h2>

<p>AddressSanitizer cannot see into Java code, but it can detect bugs in the JNI
libraries. For that, you'll need to build the executable with ASan, which in
this case is <code>/system/bin/app_process(<em>32|64</em>)</code>. This will
enable ASan in all apps on the device at the same time, which is a
bit stressful, but nothing that a 2GB RAM device cannot handle.</p>

<p>Add the usual <code>LOCAL_SANITIZE:=address</code> to
the app_process build rule in <code>frameworks/base/cmds/app_process</code>. Ignore
the <code>app_process__asan</code> target in the same file for now (if it is
still there at the time you read
this). Edit the Zygote record in
<code>system/core/rootdir/init.zygote(<em>32|64</em>).rc</code> to add the
following lines:</p>

<pre class="devsite-click-to-copy">
setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib
setenv ASAN_OPTIONS
allow_user_segv_handler=true
</pre>

<p>Build, adb sync, fastboot flash boot, reboot.</p>

<h2 id=using_the_wrap_property>Using the wrap property</h2>

<p>The approach in the previous section puts AddressSanitizer into every
application in the system (actually, into every descendant of the Zygote
process). It is possible to run only one (or several) applications with ASan,
trading some memory overhead for slower application startup.</p>

<p>This can be done by starting your app with the “wrap.” property, the same one
that’s used to run apps under Valgrind. The following example runs the Gmail app
under ASan:</p>

<pre class="devsite-click-to-copy">
<code class="devsite-terminal">adb root</code>
<code class="devsite-terminal">adb shell setenforce 0  # disable SELinux</code>
<code class="devsite-terminal">adb shell setprop wrap.com.google.android.gm "asanwrapper"</code>
</pre>

<p>In this context, asanwrapper rewrites <code>/system/bin/app_process</code>
to <code>/system/bin/asan/app_process</code>, which is built with
AddressSanitizer. It also adds <code>/system/lib/asan</code> at the start of
the dynamic library search path. This way ASan-instrumented
libraries from <code>/system/lib/asan</code> are preferred to normal libraries
in <code>/system/lib</code> when running with asanwrapper.</p>

<p>Again, if a bug is found, the app will crash, and the report will be printed to
the log.</p>

<h2 id=sanitize_target>SANITIZE_TARGET</h2>

<p>Since Android 7.0 Nougat, there is support for building the entire Android platform with
ASan at once. (If you're building a release newer than Android 9.0 Pie, HWASan is a better choice.)</p>

<p>Run the following commands in the same build tree.</p>

<pre class="devsite-click-to-copy">
<code class="devsite-terminal">make -j42</code>
<code class="devsite-terminal">SANITIZE_TARGET=address make -j42</code>
</pre>

<p>In this mode, <code>userdata.img</code> contains extra libraries and must be
flashed to the device as well. Use the following command line:</p>

<pre class="devsite-terminal devsite-click-to-copy">
fastboot flash userdata && fastboot flashall
</pre>

<p>This works by building two sets of shared libraries: normal in
<code>/system/lib</code> (the first make invocation), ASan-instrumented in
<code>/data/asan/lib</code> (the second make invocation). Executables from the
second build overwrite the ones from the first build. ASan-instrumented
executables get a different library search path that includes
<code>/data/asan/lib</code> before <code>/system/lib</code> through the use of
"/system/bin/linker_asan" in PT_INTERP.</p>

<p>The build system clobbers intermediate object directories when the
<code>$SANITIZE_TARGET</code> value has changed. This forces a rebuild of all
targets while preserving installed binaries under <code>/system/lib</code>.</p>

<p>Some targets cannot be built with ASan:</p>

<ul>
  <li>Statically linked executables.
  <li><code>LOCAL_CLANG:=false</code> targets
  <li><code>LOCAL_SANITIZE:=false</code> will not be ASan'd for <code>SANITIZE_TARGET=address</code>
</ul>

<p>Executables like these are skipped in the SANITIZE_TARGET build, and the
version from the first make invocation is left in <code>/system/bin</code>.</p>

<p>Libraries like this are simply built without ASan. They can contain some ASan
code anyway from the static libraries they depend upon.</p>

<h2 id=supporting_documentation>Supporting documentation</h2>

<p><a href="https://github.com/google/sanitizers/wiki/AddressSanitizerOnAndroid">AddressSanitizerOnAndroid</a> public project site</p>
<p><a href="https://www.chromium.org/developers/testing/addresssanitizer">AddressSanitizer and Chromium</a></p>
<p><a href="https://github.com/google/sanitizers">Other Google Sanitizers</a></p>

  </body>
</html>