asan_device_setup revision 1.1.1.1
1#!/bin/bash
2#===- lib/asan/scripts/asan_device_setup -----------------------------------===#
3#
4#                     The LLVM Compiler Infrastructure
5#
6# This file is distributed under the University of Illinois Open Source
7# License. See LICENSE.TXT for details.
8#
9# Prepare Android device to run ASan applications.
10#
11#===------------------------------------------------------------------------===#
12
13set -e
14
15HERE="$(cd "$(dirname "$0")" && pwd)"
16
17revert=no
18extra_options=
19device=
20lib=
21use_su=0
22
23function usage {
24    echo "usage: $0 [--revert] [--device device-id] [--lib path] [--extra-options options]"
25    echo "  --revert: Uninstall ASan from the device."
26    echo "  --lib: Path to ASan runtime library."
27    echo "  --extra-options: Extra ASAN_OPTIONS."
28    echo "  --device: Install to the given device. Use 'adb devices' to find"
29    echo "            device-id."
30    echo "  --use-su: Use 'su -c' prefix for every adb command instead of using"
31    echo "            'adb root' once."
32    echo
33    exit 1
34}
35
36function adb_push {
37  if [ $use_su -eq 0 ]; then
38    $ADB push "$1" "$2"
39  else
40    local FILENAME=$(basename $1)
41    $ADB push "$1" "/data/local/tmp/$FILENAME"
42    $ADB shell su -c "rm \\\"$2/$FILENAME\\\"" >&/dev/null
43    $ADB shell su -c "cat \\\"/data/local/tmp/$FILENAME\\\" > \\\"$2/$FILENAME\\\""
44    $ADB shell su -c "rm \\\"/data/local/tmp/$FILENAME\\\""
45  fi
46}
47
48function adb_remount {
49  if [ $use_su -eq 0 ]; then
50    $ADB remount
51  else
52    local STORAGE=`$ADB shell mount | grep /system | cut -d ' ' -f1`
53    if [ "$STORAGE" != "" ]; then
54      echo Remounting $STORAGE at /system
55      $ADB shell su -c "mount -o rw,remount $STORAGE /system"
56    else
57      echo Failed to get storage device name for "/system" mount point
58    fi
59  fi
60}
61
62function adb_shell {
63  if [ $use_su -eq 0 ]; then
64    $ADB shell $@
65  else
66    $ADB shell su -c "$*"
67  fi
68}
69
70function adb_root {
71  if [ $use_su -eq 0 ]; then
72    $ADB root
73  fi
74}
75
76function adb_wait_for_device {
77  $ADB wait-for-device
78}
79
80function adb_pull {
81  if [ $use_su -eq 0 ]; then
82    $ADB pull "$1" "$2"
83  else
84    local FILENAME=$(basename $1)
85    $ADB shell rm "/data/local/tmp/$FILENAME" >&/dev/null
86    $ADB shell su -c "[ -f \\\"$1\\\" ] && cat \\\"$1\\\" > \\\"/data/local/tmp/$FILENAME\\\" && chown root.shell \\\"/data/local/tmp/$FILENAME\\\" && chmod 755 \\\"/data/local/tmp/$FILENAME\\\"" &&
87    $ADB pull "/data/local/tmp/$FILENAME" "$2" >&/dev/null && $ADB shell "rm \"/data/local/tmp/$FILENAME\""
88  fi
89}
90
91function get_device_arch { # OUT OUT64
92    local _outvar=$1
93    local _outvar64=$2
94    local _ABI=$(adb_shell getprop ro.product.cpu.abi)
95    local _ARCH=
96    local _ARCH64=
97    if [[ $_ABI == x86* ]]; then
98        _ARCH=i386
99    elif [[ $_ABI == armeabi* ]]; then
100        _ARCH=arm
101    elif [[ $_ABI == arm64-v8a* ]]; then
102        _ARCH=arm
103        _ARCH64=aarch64
104    else
105        echo "Unrecognized device ABI: $_ABI"
106        exit 1
107    fi
108    eval $_outvar=\$_ARCH
109    eval $_outvar64=\$_ARCH64
110}
111
112while [[ $# > 0 ]]; do
113  case $1 in
114    --revert)
115      revert=yes
116      ;;
117    --extra-options)
118      shift
119      if [[ $# == 0 ]]; then
120        echo "--extra-options requires an argument."
121        exit 1
122      fi
123      extra_options="$1"
124      ;;
125    --lib)
126      shift
127      if [[ $# == 0 ]]; then
128        echo "--lib requires an argument."
129        exit 1
130      fi
131      lib="$1"
132      ;;
133    --device)
134      shift
135      if [[ $# == 0 ]]; then
136        echo "--device requires an argument."
137        exit 1
138      fi
139      device="$1"
140      ;;
141    --use-su)
142      use_su=1
143      ;;
144    *)
145      usage
146      ;;
147  esac
148  shift
149done
150
151ADB=${ADB:-adb}
152if [[ x$device != x ]]; then
153    ADB="$ADB -s $device"
154fi
155
156if [ $use_su -eq 1 ]; then
157  # Test if 'su' is present on the device
158  SU_TEST_OUT=`$ADB shell su -c "echo foo" 2>&1 | sed 's/\r$//'`
159  if [ $? != 0 -o "$SU_TEST_OUT" != "foo" ]; then
160    echo "ERROR: Cannot use 'su -c':"
161    echo "$ adb shell su -c \"echo foo\""
162    echo $SU_TEST_OUT
163    echo "Check that 'su' binary is correctly installed on the device or omit"
164    echo "            --use-su flag"
165    exit 1
166  fi
167fi
168
169echo '>> Remounting /system rw'
170adb_wait_for_device
171adb_root
172adb_wait_for_device
173adb_remount
174adb_wait_for_device
175
176get_device_arch ARCH ARCH64
177echo "Target architecture: $ARCH"
178ASAN_RT="libclang_rt.asan-$ARCH-android.so"
179if [[ -n $ARCH64 ]]; then
180  echo "Target architecture: $ARCH64"
181  ASAN_RT64="libclang_rt.asan-$ARCH64-android.so"
182fi
183
184RELEASE=$(adb_shell getprop ro.build.version.release)
185PRE_L=0
186if echo "$RELEASE" | grep '^4\.' >&/dev/null; then
187    PRE_L=1
188fi
189ANDROID_O=0
190if echo "$RELEASE" | grep '^8\.0\.' >&/dev/null; then
191    # 8.0.x is for Android O
192    ANDROID_O=1
193fi
194
195if [[ x$revert == xyes ]]; then
196    echo '>> Uninstalling ASan'
197
198    if ! adb_shell ls -l /system/bin/app_process | grep -o '\->.*app_process' >&/dev/null; then
199      echo '>> Pre-L device detected.'
200      adb_shell mv /system/bin/app_process.real /system/bin/app_process
201      adb_shell rm /system/bin/asanwrapper
202    elif ! adb_shell ls -l /system/bin/app_process64.real | grep -o 'No such file or directory' >&/dev/null; then
203      # 64-bit installation.
204      adb_shell mv /system/bin/app_process32.real /system/bin/app_process32
205      adb_shell mv /system/bin/app_process64.real /system/bin/app_process64
206      adb_shell rm /system/bin/asanwrapper
207      adb_shell rm /system/bin/asanwrapper64
208    else
209      # 32-bit installation.
210      adb_shell rm /system/bin/app_process.wrap
211      adb_shell rm /system/bin/asanwrapper
212      adb_shell rm /system/bin/app_process
213      adb_shell ln -s /system/bin/app_process32 /system/bin/app_process
214    fi
215
216    if [[ ANDROID_O -eq 1 ]]; then
217      adb_shell mv /system/etc/ld.config.txt.saved /system/etc/ld.config.txt
218    fi
219
220    echo '>> Restarting shell'
221    adb_shell stop
222    adb_shell start
223
224    # Remove the library on the last step to give a chance to the 'su' binary to
225    # be executed without problem.
226    adb_shell rm /system/lib/$ASAN_RT
227
228    echo '>> Done'
229    exit 0
230fi
231
232if [[ -d "$lib" ]]; then
233    ASAN_RT_PATH="$lib"
234elif [[ -f "$lib" && "$lib" == *"$ASAN_RT" ]]; then
235    ASAN_RT_PATH=$(dirname "$lib")
236elif [[ -f "$HERE/$ASAN_RT" ]]; then
237    ASAN_RT_PATH="$HERE"
238elif [[ $(basename "$HERE") == "bin" ]]; then
239    # We could be in the toolchain's base directory.
240    # Consider ../lib, ../lib/asan, ../lib/linux,
241    # ../lib/clang/$VERSION/lib/linux, and ../lib64/clang/$VERSION/lib/linux.
242    P=$(ls "$HERE"/../lib/"$ASAN_RT" \
243           "$HERE"/../lib/asan/"$ASAN_RT" \
244           "$HERE"/../lib/linux/"$ASAN_RT" \
245           "$HERE"/../lib/clang/*/lib/linux/"$ASAN_RT" \
246           "$HERE"/../lib64/clang/*/lib/linux/"$ASAN_RT" 2>/dev/null | sort | tail -1)
247    if [[ -n "$P" ]]; then
248        ASAN_RT_PATH="$(dirname "$P")"
249    fi
250fi
251
252if [[ -z "$ASAN_RT_PATH" || ! -f "$ASAN_RT_PATH/$ASAN_RT" ]]; then
253    echo ">> ASan runtime library not found"
254    exit 1
255fi
256
257if [[ -n "$ASAN_RT64" ]]; then
258  if [[ -z "$ASAN_RT_PATH" || ! -f "$ASAN_RT_PATH/$ASAN_RT64" ]]; then
259    echo ">> ASan runtime library not found"
260    exit 1
261  fi
262fi
263
264TMPDIRBASE=$(mktemp -d)
265TMPDIROLD="$TMPDIRBASE/old"
266TMPDIR="$TMPDIRBASE/new"
267mkdir "$TMPDIROLD"
268
269if ! adb_shell ls -l /system/bin/app_process | grep -o '\->.*app_process' >&/dev/null; then
270
271    if adb_pull /system/bin/app_process.real /dev/null >&/dev/null; then
272        echo '>> Old-style ASan installation detected. Reverting.'
273        adb_shell mv /system/bin/app_process.real /system/bin/app_process
274    fi
275
276    echo '>> Pre-L device detected. Setting up app_process symlink.'
277    adb_shell mv /system/bin/app_process /system/bin/app_process32
278    adb_shell ln -s /system/bin/app_process32 /system/bin/app_process
279fi
280
281echo '>> Copying files from the device'
282if [[ -n "$ASAN_RT64" ]]; then
283  adb_pull /system/lib/"$ASAN_RT" "$TMPDIROLD" || true
284  adb_pull /system/lib64/"$ASAN_RT64" "$TMPDIROLD" || true
285  adb_pull /system/bin/app_process32 "$TMPDIROLD" || true
286  adb_pull /system/bin/app_process32.real "$TMPDIROLD" || true
287  adb_pull /system/bin/app_process64 "$TMPDIROLD" || true
288  adb_pull /system/bin/app_process64.real "$TMPDIROLD" || true
289  adb_pull /system/bin/asanwrapper "$TMPDIROLD" || true
290  adb_pull /system/bin/asanwrapper64 "$TMPDIROLD" || true
291else
292  adb_pull /system/lib/"$ASAN_RT" "$TMPDIROLD" || true
293  adb_pull /system/bin/app_process32 "$TMPDIROLD" || true
294  adb_pull /system/bin/app_process.wrap "$TMPDIROLD" || true
295  adb_pull /system/bin/asanwrapper "$TMPDIROLD" || true
296fi
297cp -r "$TMPDIROLD" "$TMPDIR"
298
299if [[ -f "$TMPDIR/app_process.wrap" || -f "$TMPDIR/app_process64.real" ]]; then
300    echo ">> Previous installation detected"
301else
302    echo ">> New installation"
303fi
304
305echo '>> Generating wrappers'
306
307cp "$ASAN_RT_PATH/$ASAN_RT" "$TMPDIR/"
308if [[ -n "$ASAN_RT64" ]]; then
309  cp "$ASAN_RT_PATH/$ASAN_RT64" "$TMPDIR/"
310fi
311
312ASAN_OPTIONS=start_deactivated=1
313
314# The name of a symlink to libclang_rt.asan-$ARCH-android.so used in LD_PRELOAD.
315# The idea is to have the same name in lib and lib64 to keep it from falling
316# apart when a 64-bit process spawns a 32-bit one, inheriting the environment.
317ASAN_RT_SYMLINK=symlink-to-libclang_rt.asan
318
319function generate_zygote_wrapper { # from, to
320  local _from=$1
321  local _to=$2
322  if [[ PRE_L -eq 0 ]]; then
323    # LD_PRELOAD parsing is broken in N if it starts with ":". Luckily, it is
324    # unset in the system environment since L.
325    local _ld_preload=$ASAN_RT_SYMLINK
326  else
327    local _ld_preload=\$LD_PRELOAD:$ASAN_RT_SYMLINK
328  fi
329  cat <<EOF >"$TMPDIR/$_from"
330#!/system/bin/sh-from-zygote
331ASAN_OPTIONS=$ASAN_OPTIONS \\
332ASAN_ACTIVATION_OPTIONS=include_if_exists=/data/local/tmp/asan.options.%b \\
333LD_PRELOAD=$_ld_preload \\
334exec $_to \$@
335
336EOF
337}
338
339# On Android-L not allowing user segv handler breaks some applications.
340# Since ~May 2017 this is the default setting; included for compatibility with
341# older library versions.
342if [[ PRE_L -eq 0 ]]; then
343    ASAN_OPTIONS="$ASAN_OPTIONS,allow_user_segv_handler=1"
344fi
345
346if [[ x$extra_options != x ]] ; then
347    ASAN_OPTIONS="$ASAN_OPTIONS,$extra_options"
348fi
349
350# Zygote wrapper.
351if [[ -f "$TMPDIR/app_process64" ]]; then
352  # A 64-bit device.
353  if [[ ! -f "$TMPDIR/app_process64.real" ]]; then
354    # New installation.
355    mv "$TMPDIR/app_process32" "$TMPDIR/app_process32.real"
356    mv "$TMPDIR/app_process64" "$TMPDIR/app_process64.real"
357  fi
358  generate_zygote_wrapper "app_process32" "/system/bin/app_process32.real"
359  generate_zygote_wrapper "app_process64" "/system/bin/app_process64.real"
360else
361  # A 32-bit device.
362  generate_zygote_wrapper "app_process.wrap" "/system/bin/app_process32"
363fi
364
365# General command-line tool wrapper (use for anything that's not started as
366# zygote).
367cat <<EOF >"$TMPDIR/asanwrapper"
368#!/system/bin/sh
369LD_PRELOAD=$ASAN_RT_SYMLINK \\
370exec \$@
371
372EOF
373
374if [[ -n "$ASAN_RT64" ]]; then
375  cat <<EOF >"$TMPDIR/asanwrapper64"
376#!/system/bin/sh
377LD_PRELOAD=$ASAN_RT_SYMLINK \\
378exec \$@
379
380EOF
381fi
382
383function install { # from, to, chmod, chcon
384  local _from=$1
385  local _to=$2
386  local _mode=$3
387  local _context=$4
388  local _basename="$(basename "$_from")"
389  echo "Installing $_to/$_basename $_mode $_context"
390  adb_push "$_from" "$_to/$_basename"
391  adb_shell chown root.shell "$_to/$_basename"
392  if [[ -n "$_mode" ]]; then
393    adb_shell chmod "$_mode" "$_to/$_basename"
394  fi
395  if [[ -n "$_context" ]]; then
396    adb_shell chcon "$_context" "$_to/$_basename"
397  fi
398}
399
400if ! ( cd "$TMPDIRBASE" && diff -qr old/ new/ ) ; then
401    # Make SELinux happy by keeping app_process wrapper and the shell
402    # it runs on in zygote domain.
403    ENFORCING=0
404    if adb_shell getenforce | grep Enforcing >/dev/null; then
405        # Sometimes shell is not allowed to change file contexts.
406        # Temporarily switch to permissive.
407        ENFORCING=1
408        adb_shell setenforce 0
409    fi
410
411    if [[ PRE_L -eq 1 ]]; then
412        CTX=u:object_r:system_file:s0
413    else
414        CTX=u:object_r:zygote_exec:s0
415    fi
416
417    echo '>> Pushing files to the device'
418
419    if [[ -n "$ASAN_RT64" ]]; then
420      install "$TMPDIR/$ASAN_RT" /system/lib 644
421      install "$TMPDIR/$ASAN_RT64" /system/lib64 644
422      install "$TMPDIR/app_process32" /system/bin 755 $CTX
423      install "$TMPDIR/app_process32.real" /system/bin 755 $CTX
424      install "$TMPDIR/app_process64" /system/bin 755 $CTX
425      install "$TMPDIR/app_process64.real" /system/bin 755 $CTX
426      install "$TMPDIR/asanwrapper" /system/bin 755
427      install "$TMPDIR/asanwrapper64" /system/bin 755
428
429      adb_shell rm -f /system/lib/$ASAN_RT_SYMLINK
430      adb_shell ln -s $ASAN_RT /system/lib/$ASAN_RT_SYMLINK
431      adb_shell rm -f /system/lib64/$ASAN_RT_SYMLINK
432      adb_shell ln -s $ASAN_RT64 /system/lib64/$ASAN_RT_SYMLINK
433    else
434      install "$TMPDIR/$ASAN_RT" /system/lib 644
435      install "$TMPDIR/app_process32" /system/bin 755 $CTX
436      install "$TMPDIR/app_process.wrap" /system/bin 755 $CTX
437      install "$TMPDIR/asanwrapper" /system/bin 755 $CTX
438
439      adb_shell rm -f /system/lib/$ASAN_RT_SYMLINK
440      adb_shell ln -s $ASAN_RT /system/lib/$ASAN_RT_SYMLINK
441
442      adb_shell rm /system/bin/app_process
443      adb_shell ln -s /system/bin/app_process.wrap /system/bin/app_process
444    fi
445
446    adb_shell cp /system/bin/sh /system/bin/sh-from-zygote
447    adb_shell chcon $CTX /system/bin/sh-from-zygote
448
449    if [[ ANDROID_O -eq 1 ]]; then
450      # For Android O, the linker namespace is temporarily disabled.
451      adb_shell mv /system/etc/ld.config.txt /system/etc/ld.config.txt.saved
452    fi
453
454    if [ $ENFORCING == 1 ]; then
455        adb_shell setenforce 1
456    fi
457
458    echo '>> Restarting shell (asynchronous)'
459    adb_shell stop
460    adb_shell start
461
462    echo '>> Please wait until the device restarts'
463else
464    echo '>> Device is up to date'
465fi
466
467rm -r "$TMPDIRBASE"
468