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