Home | History | Annotate | Download | only in bin
      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 
     13 set -e
     14 
     15 HERE="$(cd "$(dirname "$0")" && pwd)"
     16 
     17 revert=no
     18 extra_options=
     19 device=
     20 lib=
     21 use_su=0
     22 
     23 function 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 
     36 function 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 
     48 function 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 
     62 function adb_shell {
     63   if [ $use_su -eq 0 ]; then
     64     $ADB shell $@
     65   else
     66     $ADB shell su -c "$*"
     67   fi
     68 }
     69 
     70 function adb_root {
     71   if [ $use_su -eq 0 ]; then
     72     $ADB root
     73   fi
     74 }
     75 
     76 function adb_wait_for_device {
     77   $ADB wait-for-device
     78 }
     79 
     80 function 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 
     91 function 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 
    112 while [[ $# > 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
    149 done
    150 
    151 ADB=${ADB:-adb}
    152 if [[ x$device != x ]]; then
    153     ADB="$ADB -s $device"
    154 fi
    155 
    156 if [ $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
    167 fi
    168 
    169 echo '>> Remounting /system rw'
    170 adb_wait_for_device
    171 adb_root
    172 adb_wait_for_device
    173 adb_remount
    174 adb_wait_for_device
    175 
    176 get_device_arch ARCH ARCH64
    177 echo "Target architecture: $ARCH"
    178 ASAN_RT="libclang_rt.asan-$ARCH-android.so"
    179 if [[ -n $ARCH64 ]]; then
    180   echo "Target architecture: $ARCH64"
    181   ASAN_RT64="libclang_rt.asan-$ARCH64-android.so"
    182 fi
    183 
    184 RELEASE=$(adb_shell getprop ro.build.version.release)
    185 PRE_L=0
    186 if echo "$RELEASE" | grep '^4\.' >&/dev/null; then
    187     PRE_L=1
    188 fi
    189 ANDROID_O=0
    190 if echo "$RELEASE" | grep '^8\.0\.' >&/dev/null; then
    191     # 8.0.x is for Android O
    192     ANDROID_O=1
    193 fi
    194 
    195 if [[ 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
    230 fi
    231 
    232 if [[ -d "$lib" ]]; then
    233     ASAN_RT_PATH="$lib"
    234 elif [[ -f "$lib" && "$lib" == *"$ASAN_RT" ]]; then
    235     ASAN_RT_PATH=$(dirname "$lib")
    236 elif [[ -f "$HERE/$ASAN_RT" ]]; then
    237     ASAN_RT_PATH="$HERE"
    238 elif [[ $(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
    250 fi
    251 
    252 if [[ -z "$ASAN_RT_PATH" || ! -f "$ASAN_RT_PATH/$ASAN_RT" ]]; then
    253     echo ">> ASan runtime library not found"
    254     exit 1
    255 fi
    256 
    257 if [[ -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
    262 fi
    263 
    264 TMPDIRBASE=$(mktemp -d)
    265 TMPDIROLD="$TMPDIRBASE/old"
    266 TMPDIR="$TMPDIRBASE/new"
    267 mkdir "$TMPDIROLD"
    268 
    269 if ! 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
    279 fi
    280 
    281 echo '>> Copying files from the device'
    282 if [[ -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
    291 else
    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
    296 fi
    297 cp -r "$TMPDIROLD" "$TMPDIR"
    298 
    299 if [[ -f "$TMPDIR/app_process.wrap" || -f "$TMPDIR/app_process64.real" ]]; then
    300     echo ">> Previous installation detected"
    301 else
    302     echo ">> New installation"
    303 fi
    304 
    305 echo '>> Generating wrappers'
    306 
    307 cp "$ASAN_RT_PATH/$ASAN_RT" "$TMPDIR/"
    308 if [[ -n "$ASAN_RT64" ]]; then
    309   cp "$ASAN_RT_PATH/$ASAN_RT64" "$TMPDIR/"
    310 fi
    311 
    312 ASAN_OPTIONS=start_deactivated=1,malloc_context_size=0
    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.
    317 ASAN_RT_SYMLINK=symlink-to-libclang_rt.asan
    318 
    319 function 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
    331 ASAN_OPTIONS=$ASAN_OPTIONS \\
    332 ASAN_ACTIVATION_OPTIONS=include_if_exists=/data/local/tmp/asan.options.%b \\
    333 LD_PRELOAD=$_ld_preload \\
    334 exec $_to \$@
    335 
    336 EOF
    337 }
    338 
    339 if [[ x$extra_options != x ]] ; then
    340     ASAN_OPTIONS="$ASAN_OPTIONS,$extra_options"
    341 fi
    342 
    343 # Zygote wrapper.
    344 if [[ -f "$TMPDIR/app_process64" ]]; then
    345   # A 64-bit device.
    346   if [[ ! -f "$TMPDIR/app_process64.real" ]]; then
    347     # New installation.
    348     mv "$TMPDIR/app_process32" "$TMPDIR/app_process32.real"
    349     mv "$TMPDIR/app_process64" "$TMPDIR/app_process64.real"
    350   fi
    351   generate_zygote_wrapper "app_process32" "/system/bin/app_process32.real"
    352   generate_zygote_wrapper "app_process64" "/system/bin/app_process64.real"
    353 else
    354   # A 32-bit device.
    355   generate_zygote_wrapper "app_process.wrap" "/system/bin/app_process32"
    356 fi
    357 
    358 # General command-line tool wrapper (use for anything that's not started as
    359 # zygote).
    360 cat <<EOF >"$TMPDIR/asanwrapper"
    361 #!/system/bin/sh
    362 LD_PRELOAD=$ASAN_RT_SYMLINK \\
    363 exec \$@
    364 
    365 EOF
    366 
    367 if [[ -n "$ASAN_RT64" ]]; then
    368   cat <<EOF >"$TMPDIR/asanwrapper64"
    369 #!/system/bin/sh
    370 LD_PRELOAD=$ASAN_RT_SYMLINK \\
    371 exec \$@
    372 
    373 EOF
    374 fi
    375 
    376 function install { # from, to, chmod, chcon
    377   local _from=$1
    378   local _to=$2
    379   local _mode=$3
    380   local _context=$4
    381   local _basename="$(basename "$_from")"
    382   echo "Installing $_to/$_basename $_mode $_context"
    383   adb_push "$_from" "$_to/$_basename"
    384   adb_shell chown root.shell "$_to/$_basename"
    385   if [[ -n "$_mode" ]]; then
    386     adb_shell chmod "$_mode" "$_to/$_basename"
    387   fi
    388   if [[ -n "$_context" ]]; then
    389     adb_shell chcon "$_context" "$_to/$_basename"
    390   fi
    391 }
    392 
    393 if ! ( cd "$TMPDIRBASE" && diff -qr old/ new/ ) ; then
    394     # Make SELinux happy by keeping app_process wrapper and the shell
    395     # it runs on in zygote domain.
    396     ENFORCING=0
    397     if adb_shell getenforce | grep Enforcing >/dev/null; then
    398         # Sometimes shell is not allowed to change file contexts.
    399         # Temporarily switch to permissive.
    400         ENFORCING=1
    401         adb_shell setenforce 0
    402     fi
    403 
    404     if [[ PRE_L -eq 1 ]]; then
    405         CTX=u:object_r:system_file:s0
    406     else
    407         CTX=u:object_r:zygote_exec:s0
    408     fi
    409 
    410     echo '>> Pushing files to the device'
    411 
    412     if [[ -n "$ASAN_RT64" ]]; then
    413       install "$TMPDIR/$ASAN_RT" /system/lib 644
    414       install "$TMPDIR/$ASAN_RT64" /system/lib64 644
    415       install "$TMPDIR/app_process32" /system/bin 755 $CTX
    416       install "$TMPDIR/app_process32.real" /system/bin 755 $CTX
    417       install "$TMPDIR/app_process64" /system/bin 755 $CTX
    418       install "$TMPDIR/app_process64.real" /system/bin 755 $CTX
    419       install "$TMPDIR/asanwrapper" /system/bin 755
    420       install "$TMPDIR/asanwrapper64" /system/bin 755
    421 
    422       adb_shell ln -sf $ASAN_RT /system/lib/$ASAN_RT_SYMLINK
    423       adb_shell ln -sf $ASAN_RT64 /system/lib64/$ASAN_RT_SYMLINK
    424     else
    425       install "$TMPDIR/$ASAN_RT" /system/lib 644
    426       install "$TMPDIR/app_process32" /system/bin 755 $CTX
    427       install "$TMPDIR/app_process.wrap" /system/bin 755 $CTX
    428       install "$TMPDIR/asanwrapper" /system/bin 755 $CTX
    429 
    430       adb_shell ln -sf $ASAN_RT /system/lib/$ASAN_RT_SYMLINK
    431 
    432       adb_shell rm /system/bin/app_process
    433       adb_shell ln -s /system/bin/app_process.wrap /system/bin/app_process
    434     fi
    435 
    436     adb_shell cp /system/bin/sh /system/bin/sh-from-zygote
    437     adb_shell chcon $CTX /system/bin/sh-from-zygote
    438 
    439     if [[ ANDROID_O -eq 1 ]]; then
    440       # For Android O, the linker namespace is temporarily disabled.
    441       adb_shell mv /system/etc/ld.config.txt /system/etc/ld.config.txt.saved
    442     fi
    443 
    444     if [ $ENFORCING == 1 ]; then
    445         adb_shell setenforce 1
    446     fi
    447 
    448     echo '>> Restarting shell (asynchronous)'
    449     adb_shell stop
    450     adb_shell start
    451 
    452     echo '>> Please wait until the device restarts'
    453 else
    454     echo '>> Device is up to date'
    455 fi
    456 
    457 rm -r "$TMPDIRBASE"
    458