467 lines · plain
1#!/usr/bin/env bash2#===- lib/asan/scripts/asan_device_setup -----------------------------------===#3#4# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.5# See https://llvm.org/LICENSE.txt for license information.6# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception7#8# Prepare Android device to run ASan applications.9#10#===------------------------------------------------------------------------===#11 12set -e13 14HERE="$(cd "$(dirname "$0")" && pwd)"15 16revert=no17extra_options=18device=19lib=20use_su=021 22function usage {23 echo "usage: $0 [--revert] [--device device-id] [--lib path] [--extra-options options]"24 echo " --revert: Uninstall ASan from the device."25 echo " --lib: Path to ASan runtime library."26 echo " --extra-options: Extra ASAN_OPTIONS."27 echo " --device: Install to the given device. Use 'adb devices' to find"28 echo " device-id."29 echo " --use-su: Use 'su -c' prefix for every adb command instead of using"30 echo " 'adb root' once."31 echo32 exit 133}34 35function adb_push {36 if [ $use_su -eq 0 ]; then37 $ADB push "$1" "$2"38 else39 local FILENAME=$(basename $1)40 $ADB push "$1" "/data/local/tmp/$FILENAME"41 $ADB shell su -c "rm \\\"$2/$FILENAME\\\"" >&/dev/null42 $ADB shell su -c "cat \\\"/data/local/tmp/$FILENAME\\\" > \\\"$2/$FILENAME\\\""43 $ADB shell su -c "rm \\\"/data/local/tmp/$FILENAME\\\""44 fi45}46 47function adb_remount {48 if [ $use_su -eq 0 ]; then49 $ADB remount50 else51 local STORAGE=`$ADB shell mount | grep /system | cut -d ' ' -f1`52 if [ "$STORAGE" != "" ]; then53 echo Remounting $STORAGE at /system54 $ADB shell su -c "mount -o rw,remount $STORAGE /system"55 else56 echo Failed to get storage device name for "/system" mount point57 fi58 fi59}60 61function adb_shell {62 if [ $use_su -eq 0 ]; then63 $ADB shell $@64 else65 $ADB shell su -c "$*"66 fi67}68 69function adb_root {70 if [ $use_su -eq 0 ]; then71 $ADB root72 fi73}74 75function adb_wait_for_device {76 $ADB wait-for-device77}78 79function adb_pull {80 if [ $use_su -eq 0 ]; then81 $ADB pull "$1" "$2"82 else83 local FILENAME=$(basename $1)84 $ADB shell rm "/data/local/tmp/$FILENAME" >&/dev/null85 $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\\\"" &&86 $ADB pull "/data/local/tmp/$FILENAME" "$2" >&/dev/null && $ADB shell "rm \"/data/local/tmp/$FILENAME\""87 fi88}89 90function get_device_arch { # OUT OUT6491 local _outvar=$192 local _outvar64=$293 local _ABI=$(adb_shell getprop ro.product.cpu.abi)94 local _ARCH=95 local _ARCH64=96 if [[ $_ABI == x86* ]]; then97 _ARCH=i68698 elif [[ $_ABI == armeabi* ]]; then99 _ARCH=arm100 elif [[ $_ABI == arm64-v8a* ]]; then101 _ARCH=arm102 _ARCH64=aarch64103 else104 echo "Unrecognized device ABI: $_ABI"105 exit 1106 fi107 eval $_outvar=\$_ARCH108 eval $_outvar64=\$_ARCH64109}110 111while [[ $# > 0 ]]; do112 case $1 in113 --revert)114 revert=yes115 ;;116 --extra-options)117 shift118 if [[ $# == 0 ]]; then119 echo "--extra-options requires an argument."120 exit 1121 fi122 extra_options="$1"123 ;;124 --lib)125 shift126 if [[ $# == 0 ]]; then127 echo "--lib requires an argument."128 exit 1129 fi130 lib="$1"131 ;;132 --device)133 shift134 if [[ $# == 0 ]]; then135 echo "--device requires an argument."136 exit 1137 fi138 device="$1"139 ;;140 --use-su)141 use_su=1142 ;;143 *)144 usage145 ;;146 esac147 shift148done149 150ADB=${ADB:-adb}151if [[ x$device != x ]]; then152 ADB="$ADB -s $device"153fi154 155if [ $use_su -eq 1 ]; then156 # Test if 'su' is present on the device157 SU_TEST_OUT=`$ADB shell su -c "echo foo" 2>&1 | sed 's/\r$//'`158 if [ $? != 0 -o "$SU_TEST_OUT" != "foo" ]; then159 echo "ERROR: Cannot use 'su -c':"160 echo "$ adb shell su -c \"echo foo\""161 echo $SU_TEST_OUT162 echo "Check that 'su' binary is correctly installed on the device or omit"163 echo " --use-su flag"164 exit 1165 fi166fi167 168echo '>> Remounting /system rw'169adb_wait_for_device170adb_root171adb_wait_for_device172adb_remount173adb_wait_for_device174 175get_device_arch ARCH ARCH64176echo "Target architecture: $ARCH"177ASAN_RT="libclang_rt.asan-$ARCH-android.so"178if [[ -n $ARCH64 ]]; then179 echo "Target architecture: $ARCH64"180 ASAN_RT64="libclang_rt.asan-$ARCH64-android.so"181fi182 183RELEASE=$(adb_shell getprop ro.build.version.release)184PRE_L=0185if echo "$RELEASE" | grep '^4\.' >&/dev/null; then186 PRE_L=1187fi188ANDROID_O=0189if echo "$RELEASE" | grep '^8\.0\.' >&/dev/null; then190 # 8.0.x is for Android O191 ANDROID_O=1192fi193 194if [[ x$revert == xyes ]]; then195 echo '>> Uninstalling ASan'196 197 if ! adb_shell ls -l /system/bin/app_process | grep -o '\->.*app_process' >&/dev/null; then198 echo '>> Pre-L device detected.'199 adb_shell mv /system/bin/app_process.real /system/bin/app_process200 adb_shell rm /system/bin/asanwrapper201 elif ! adb_shell ls -l /system/bin/app_process64.real | grep -o 'No such file or directory' >&/dev/null; then202 # 64-bit installation.203 adb_shell mv /system/bin/app_process32.real /system/bin/app_process32204 adb_shell mv /system/bin/app_process64.real /system/bin/app_process64205 adb_shell rm /system/bin/asanwrapper206 adb_shell rm /system/bin/asanwrapper64207 else208 # 32-bit installation.209 adb_shell rm /system/bin/app_process.wrap210 adb_shell rm /system/bin/asanwrapper211 adb_shell rm /system/bin/app_process212 adb_shell ln -s /system/bin/app_process32 /system/bin/app_process213 fi214 215 if [[ ANDROID_O -eq 1 ]]; then216 adb_shell mv /system/etc/ld.config.txt.saved /system/etc/ld.config.txt217 fi218 219 echo '>> Restarting shell'220 adb_shell stop221 adb_shell start222 223 # Remove the library on the last step to give a chance to the 'su' binary to224 # be executed without problem.225 adb_shell rm /system/lib/$ASAN_RT226 227 echo '>> Done'228 exit 0229fi230 231if [[ -d "$lib" ]]; then232 ASAN_RT_PATH="$lib"233elif [[ -f "$lib" && "$lib" == *"$ASAN_RT" ]]; then234 ASAN_RT_PATH=$(dirname "$lib")235elif [[ -f "$HERE/$ASAN_RT" ]]; then236 ASAN_RT_PATH="$HERE"237elif [[ $(basename "$HERE") == "bin" ]]; then238 # We could be in the toolchain's base directory.239 # Consider ../lib, ../lib/asan, ../lib/linux,240 # ../lib/clang/$VERSION/lib/linux, and ../lib64/clang/$VERSION/lib/linux.241 P=$(ls "$HERE"/../lib/"$ASAN_RT" \242 "$HERE"/../lib/asan/"$ASAN_RT" \243 "$HERE"/../lib/linux/"$ASAN_RT" \244 "$HERE"/../lib/clang/*/lib/linux/"$ASAN_RT" \245 "$HERE"/../lib64/clang/*/lib/linux/"$ASAN_RT" 2>/dev/null | sort | tail -1)246 if [[ -n "$P" ]]; then247 ASAN_RT_PATH="$(dirname "$P")"248 fi249fi250 251if [[ -z "$ASAN_RT_PATH" || ! -f "$ASAN_RT_PATH/$ASAN_RT" ]]; then252 echo ">> ASan runtime library not found"253 exit 1254fi255 256if [[ -n "$ASAN_RT64" ]]; then257 if [[ -z "$ASAN_RT_PATH" || ! -f "$ASAN_RT_PATH/$ASAN_RT64" ]]; then258 echo ">> ASan runtime library not found"259 exit 1260 fi261fi262 263TMPDIRBASE=$(mktemp -d)264TMPDIROLD="$TMPDIRBASE/old"265TMPDIR="$TMPDIRBASE/new"266mkdir "$TMPDIROLD"267 268if ! adb_shell ls -l /system/bin/app_process | grep -o '\->.*app_process' >&/dev/null; then269 270 if adb_pull /system/bin/app_process.real /dev/null >&/dev/null; then271 echo '>> Old-style ASan installation detected. Reverting.'272 adb_shell mv /system/bin/app_process.real /system/bin/app_process273 fi274 275 echo '>> Pre-L device detected. Setting up app_process symlink.'276 adb_shell mv /system/bin/app_process /system/bin/app_process32277 adb_shell ln -s /system/bin/app_process32 /system/bin/app_process278fi279 280echo '>> Copying files from the device'281if [[ -n "$ASAN_RT64" ]]; then282 adb_pull /system/lib/"$ASAN_RT" "$TMPDIROLD" || true283 adb_pull /system/lib64/"$ASAN_RT64" "$TMPDIROLD" || true284 adb_pull /system/bin/app_process32 "$TMPDIROLD" || true285 adb_pull /system/bin/app_process32.real "$TMPDIROLD" || true286 adb_pull /system/bin/app_process64 "$TMPDIROLD" || true287 adb_pull /system/bin/app_process64.real "$TMPDIROLD" || true288 adb_pull /system/bin/asanwrapper "$TMPDIROLD" || true289 adb_pull /system/bin/asanwrapper64 "$TMPDIROLD" || true290else291 adb_pull /system/lib/"$ASAN_RT" "$TMPDIROLD" || true292 adb_pull /system/bin/app_process32 "$TMPDIROLD" || true293 adb_pull /system/bin/app_process.wrap "$TMPDIROLD" || true294 adb_pull /system/bin/asanwrapper "$TMPDIROLD" || true295fi296cp -r "$TMPDIROLD" "$TMPDIR"297 298if [[ -f "$TMPDIR/app_process.wrap" || -f "$TMPDIR/app_process64.real" ]]; then299 echo ">> Previous installation detected"300else301 echo ">> New installation"302fi303 304echo '>> Generating wrappers'305 306cp "$ASAN_RT_PATH/$ASAN_RT" "$TMPDIR/"307if [[ -n "$ASAN_RT64" ]]; then308 cp "$ASAN_RT_PATH/$ASAN_RT64" "$TMPDIR/"309fi310 311ASAN_OPTIONS=start_deactivated=1312 313# The name of a symlink to libclang_rt.asan-$ARCH-android.so used in LD_PRELOAD.314# The idea is to have the same name in lib and lib64 to keep it from falling315# apart when a 64-bit process spawns a 32-bit one, inheriting the environment.316ASAN_RT_SYMLINK=symlink-to-libclang_rt.asan317 318function generate_zygote_wrapper { # from, to319 local _from=$1320 local _to=$2321 if [[ PRE_L -eq 0 ]]; then322 # LD_PRELOAD parsing is broken in N if it starts with ":". Luckily, it is323 # unset in the system environment since L.324 local _ld_preload=$ASAN_RT_SYMLINK325 else326 local _ld_preload=\$LD_PRELOAD:$ASAN_RT_SYMLINK327 fi328 cat <<EOF >"$TMPDIR/$_from"329#!/system/bin/sh-from-zygote330ASAN_OPTIONS=$ASAN_OPTIONS \\331ASAN_ACTIVATION_OPTIONS=include_if_exists=/data/local/tmp/asan.options.%b \\332LD_PRELOAD=$_ld_preload \\333exec $_to "\$@"334 335EOF336}337 338# On Android-L not allowing user segv handler breaks some applications.339# Since ~May 2017 this is the default setting; included for compatibility with340# older library versions.341if [[ PRE_L -eq 0 ]]; then342 ASAN_OPTIONS="$ASAN_OPTIONS,allow_user_segv_handler=1"343fi344 345if [[ x$extra_options != x ]] ; then346 ASAN_OPTIONS="$ASAN_OPTIONS,$extra_options"347fi348 349# Zygote wrapper.350if [[ -f "$TMPDIR/app_process64" ]]; then351 # A 64-bit device.352 if [[ ! -f "$TMPDIR/app_process64.real" ]]; then353 # New installation.354 mv "$TMPDIR/app_process32" "$TMPDIR/app_process32.real"355 mv "$TMPDIR/app_process64" "$TMPDIR/app_process64.real"356 fi357 generate_zygote_wrapper "app_process32" "/system/bin/app_process32.real"358 generate_zygote_wrapper "app_process64" "/system/bin/app_process64.real"359else360 # A 32-bit device.361 generate_zygote_wrapper "app_process.wrap" "/system/bin/app_process32"362fi363 364# General command-line tool wrapper (use for anything that's not started as365# zygote).366cat <<EOF >"$TMPDIR/asanwrapper"367#!/system/bin/sh368LD_PRELOAD=$ASAN_RT_SYMLINK \\369exec \$@370 371EOF372 373if [[ -n "$ASAN_RT64" ]]; then374 cat <<EOF >"$TMPDIR/asanwrapper64"375#!/system/bin/sh376LD_PRELOAD=$ASAN_RT_SYMLINK \\377exec \$@378 379EOF380fi381 382function install { # from, to, chmod, chcon383 local _from=$1384 local _to=$2385 local _mode=$3386 local _context=$4387 local _basename="$(basename "$_from")"388 echo "Installing $_to/$_basename $_mode $_context"389 adb_push "$_from" "$_to/$_basename"390 adb_shell chown root.shell "$_to/$_basename"391 if [[ -n "$_mode" ]]; then392 adb_shell chmod "$_mode" "$_to/$_basename"393 fi394 if [[ -n "$_context" ]]; then395 adb_shell chcon "$_context" "$_to/$_basename"396 fi397}398 399if ! ( cd "$TMPDIRBASE" && diff -qr old/ new/ ) ; then400 # Make SELinux happy by keeping app_process wrapper and the shell401 # it runs on in zygote domain.402 ENFORCING=0403 if adb_shell getenforce | grep Enforcing >/dev/null; then404 # Sometimes shell is not allowed to change file contexts.405 # Temporarily switch to permissive.406 ENFORCING=1407 adb_shell setenforce 0408 fi409 410 if [[ PRE_L -eq 1 ]]; then411 CTX=u:object_r:system_file:s0412 else413 CTX=u:object_r:zygote_exec:s0414 fi415 416 echo '>> Pushing files to the device'417 418 if [[ -n "$ASAN_RT64" ]]; then419 install "$TMPDIR/$ASAN_RT" /system/lib 644420 install "$TMPDIR/$ASAN_RT64" /system/lib64 644421 install "$TMPDIR/app_process32" /system/bin 755 $CTX422 install "$TMPDIR/app_process32.real" /system/bin 755 $CTX423 install "$TMPDIR/app_process64" /system/bin 755 $CTX424 install "$TMPDIR/app_process64.real" /system/bin 755 $CTX425 install "$TMPDIR/asanwrapper" /system/bin 755426 install "$TMPDIR/asanwrapper64" /system/bin 755427 428 adb_shell rm -f /system/lib/$ASAN_RT_SYMLINK429 adb_shell ln -s $ASAN_RT /system/lib/$ASAN_RT_SYMLINK430 adb_shell rm -f /system/lib64/$ASAN_RT_SYMLINK431 adb_shell ln -s $ASAN_RT64 /system/lib64/$ASAN_RT_SYMLINK432 else433 install "$TMPDIR/$ASAN_RT" /system/lib 644434 install "$TMPDIR/app_process32" /system/bin 755 $CTX435 install "$TMPDIR/app_process.wrap" /system/bin 755 $CTX436 install "$TMPDIR/asanwrapper" /system/bin 755 $CTX437 438 adb_shell rm -f /system/lib/$ASAN_RT_SYMLINK439 adb_shell ln -s $ASAN_RT /system/lib/$ASAN_RT_SYMLINK440 441 adb_shell rm /system/bin/app_process442 adb_shell ln -s /system/bin/app_process.wrap /system/bin/app_process443 fi444 445 adb_shell cp /system/bin/sh /system/bin/sh-from-zygote446 adb_shell chcon $CTX /system/bin/sh-from-zygote447 448 if [[ ANDROID_O -eq 1 ]]; then449 # For Android O, the linker namespace is temporarily disabled.450 adb_shell mv /system/etc/ld.config.txt /system/etc/ld.config.txt.saved451 fi452 453 if [ $ENFORCING == 1 ]; then454 adb_shell setenforce 1455 fi456 457 echo '>> Restarting shell (asynchronous)'458 adb_shell stop459 adb_shell start460 461 echo '>> Please wait until the device restarts'462else463 echo '>> Device is up to date'464fi465 466rm -r "$TMPDIRBASE"467