[asan] Support 'su' rooted devices in ASan setup script.

Android devices may not support 'adb root', but be rooted with 'su'
binary. This patch makes it possible to install ASAN to such
devices. When --use-su flag is specified, most 'adb ...' commangs are
changed to 'adb su -c "..."'.

Some other notes:
 * 'readlink' changed to 'ls -l', since not all devices have readlink
   in their firmware.
 * removing ASan library step moved to very end, because 'su' may not
   run properly without this library until shell will be restarted.

Patch by Dmitry <ripp at yandex-team dot ru>.

llvm-svn: 229368
This commit is contained in:
Evgeniy Stepanov 2015-02-16 10:22:12 +00:00
parent e63bbd97a7
commit 84d30ba43a

@ -18,6 +18,7 @@ revert=no
extra_options= extra_options=
device= device=
lib= lib=
use_su=0
function usage { function usage {
echo "usage: $0 [--revert] [--device device-id] [--lib path] [--extra-options options]" echo "usage: $0 [--revert] [--device device-id] [--lib path] [--extra-options options]"
@ -26,13 +27,70 @@ function usage {
echo " --extra-options: Extra ASAN_OPTIONS." echo " --extra-options: Extra ASAN_OPTIONS."
echo " --device: Install to the given device. Use 'adb devices' to find" echo " --device: Install to the given device. Use 'adb devices' to find"
echo " device-id." echo " device-id."
echo " --use-su: Use 'su -c' prefix for every adb command instead of using"
echo " 'adb root' once."
echo echo
exit 1 exit 1
} }
function adb_push {
if [ $use_su -eq 0 ]; then
$ADB push "$1" "$2"
else
local FILENAME=$(basename $1)
$ADB push "$1" "/data/local/tmp/$FILENAME"
$ADB shell su -c "rm \\\"$2/$FILENAME\\\"" >&/dev/null
$ADB shell su -c "cat \\\"/data/local/tmp/$FILENAME\\\" > \\\"$2/$FILENAME\\\""
$ADB shell su -c "rm \\\"/data/local/tmp/$FILENAME\\\""
fi
}
function adb_remount {
if [ $use_su -eq 0 ]; then
$ADB remount
else
local STORAGE=`$ADB shell mount | grep /system | cut -d ' ' -f1`
if [ "$STORAGE" != "" ]; then
echo Remounting $STORAGE at /system
$ADB shell su -c "mount -o remount,rw $STORAGE /system"
else
echo Failed to get storage device name for "/system" mount point
fi
fi
}
function adb_shell {
if [ $use_su -eq 0 ]; then
$ADB shell $@
else
$ADB shell su -c "$*"
fi
}
function adb_root {
if [ $use_su -eq 0 ]; then
$ADB root
fi
}
function adb_wait_for_device {
$ADB wait-for-device
}
function adb_pull {
if [ $use_su -eq 0 ]; then
$ADB pull "$1" "$2"
else
local FILENAME=$(basename $1)
$ADB shell rm "/data/local/tmp/$FILENAME" >&/dev/null
$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\\\"" &&
$ADB pull "/data/local/tmp/$FILENAME" "$2" >&/dev/null && $ADB shell "rm \"/data/local/tmp/$FILENAME\""
fi
}
function get_device_arch { # OUTVAR function get_device_arch { # OUTVAR
local _outvar=$1 local _outvar=$1
local _ABI=$($ADB shell getprop ro.product.cpu.abi) local _ABI=$(adb_shell getprop ro.product.cpu.abi)
local _ARCH= local _ARCH=
if [[ $_ABI == x86* ]]; then if [[ $_ABI == x86* ]]; then
_ARCH=i686 _ARCH=i686
@ -74,6 +132,9 @@ while [[ $# > 0 ]]; do
fi fi
device="$1" device="$1"
;; ;;
--use-su)
use_su=1
;;
*) *)
usage usage
;; ;;
@ -86,12 +147,25 @@ if [[ x$device != x ]]; then
ADB="$ADB -s $device" ADB="$ADB -s $device"
fi fi
if [ $use_su -eq 1 ]; then
# Test if 'su' is present on the device
SU_TEST_OUT=`$ADB shell su -c "echo foo" 2>&1 | sed 's/\r$//'`
if [ $? != 0 -o "$SU_TEST_OUT" != "foo" ]; then
echo "ERROR: Cannot use 'su -c':"
echo "$ adb shell su -c \"echo foo\""
echo $SU_TEST_OUT
echo "Check that 'su' binary is correctly installed on the device or omit"
echo " --use-su flag"
exit 1
fi
fi
echo '>> Remounting /system rw' echo '>> Remounting /system rw'
$ADB wait-for-device adb_wait_for_device
$ADB root adb_root
$ADB wait-for-device adb_wait_for_device
$ADB remount adb_remount
$ADB wait-for-device adb_wait_for_device
get_device_arch ARCH get_device_arch ARCH
echo "Target architecture: $ARCH" echo "Target architecture: $ARCH"
@ -100,22 +174,24 @@ ASAN_RT="libclang_rt.asan-$ARCH-android.so"
if [[ x$revert == xyes ]]; then if [[ x$revert == xyes ]]; then
echo '>> Uninstalling ASan' echo '>> Uninstalling ASan'
if ! $ADB shell readlink /system/bin/app_process | grep 'app_process' >&/dev/null; then if ! adb_shell ls -l /system/bin/app_process | grep -o '\->.*app_process' >&/dev/null; then
echo '>> Pre-L device detected.' echo '>> Pre-L device detected.'
$ADB shell mv /system/bin/app_process.real /system/bin/app_process adb_shell mv /system/bin/app_process.real /system/bin/app_process
$ADB shell rm /system/bin/asanwrapper adb_shell rm /system/bin/asanwrapper
$ADB shell rm /system/lib/$ASAN_RT
else else
$ADB shell rm /system/bin/app_process.wrap adb_shell rm /system/bin/app_process.wrap
$ADB shell rm /system/bin/asanwrapper adb_shell rm /system/bin/asanwrapper
$ADB shell rm /system/lib/$ASAN_RT adb_shell rm /system/bin/app_process
$ADB shell rm /system/bin/app_process adb_shell ln -s /system/bin/app_process32 /system/bin/app_process
$ADB shell ln -s /system/bin/app_process32 /system/bin/app_process
fi fi
echo '>> Restarting shell' echo '>> Restarting shell'
$ADB shell stop adb_shell stop
$ADB shell start adb_shell start
# Remove the library on the last step to give a chance to the 'su' binary to
# be executed without problem.
adb_shell rm /system/lib/$ASAN_RT
echo '>> Done' echo '>> Done'
exit 0 exit 0
@ -146,28 +222,28 @@ TMPDIROLD="$TMPDIRBASE/old"
TMPDIR="$TMPDIRBASE/new" TMPDIR="$TMPDIRBASE/new"
mkdir "$TMPDIROLD" mkdir "$TMPDIROLD"
RELEASE=$($ADB shell getprop ro.build.version.release) RELEASE=$(adb_shell getprop ro.build.version.release)
PRE_L=0 PRE_L=0
if echo "$RELEASE" | grep '^4\.' >&/dev/null; then if echo "$RELEASE" | grep '^4\.' >&/dev/null; then
PRE_L=1 PRE_L=1
fi fi
if ! $ADB shell readlink /system/bin/app_process | grep 'app_process' >&/dev/null; then if ! adb_shell ls -l /system/bin/app_process | grep -o '\->.*app_process' >&/dev/null; then
if $ADB pull /system/bin/app_process.real /dev/null >&/dev/null; then if adb_pull /system/bin/app_process.real /dev/null >&/dev/null; then
echo '>> Old-style ASan installation detected. Reverting.' echo '>> Old-style ASan installation detected. Reverting.'
$ADB shell mv /system/bin/app_process.real /system/bin/app_process adb_shell mv /system/bin/app_process.real /system/bin/app_process
fi fi
echo '>> Pre-L device detected. Setting up app_process symlink.' echo '>> Pre-L device detected. Setting up app_process symlink.'
$ADB shell mv /system/bin/app_process /system/bin/app_process32 adb_shell mv /system/bin/app_process /system/bin/app_process32
$ADB shell ln -s /system/bin/app_process32 /system/bin/app_process adb_shell ln -s /system/bin/app_process32 /system/bin/app_process
fi fi
echo '>> Copying files from the device' echo '>> Copying files from the device'
$ADB pull /system/bin/app_process.wrap "$TMPDIROLD" || true adb_pull /system/bin/app_process.wrap "$TMPDIROLD" || true
$ADB pull /system/bin/asanwrapper "$TMPDIROLD" || true adb_pull /system/bin/asanwrapper "$TMPDIROLD" || true
$ADB pull /system/lib/"$ASAN_RT" "$TMPDIROLD" || true adb_pull /system/lib/"$ASAN_RT" "$TMPDIROLD" || true
cp -r "$TMPDIROLD" "$TMPDIR" cp -r "$TMPDIROLD" "$TMPDIR"
if [[ -f "$TMPDIR/app_process.wrap" ]]; then if [[ -f "$TMPDIR/app_process.wrap" ]]; then
@ -213,52 +289,52 @@ EOF
if ! ( cd "$TMPDIRBASE" && diff -qr old/ new/ ) ; then if ! ( cd "$TMPDIRBASE" && diff -qr old/ new/ ) ; then
echo '>> Pushing files to the device' echo '>> Pushing files to the device'
$ADB push "$TMPDIR/$ASAN_RT" /system/lib/ adb_push "$TMPDIR/$ASAN_RT" /system/lib/
$ADB push "$TMPDIR/app_process.wrap" /system/bin/app_process.wrap adb_push "$TMPDIR/app_process.wrap" /system/bin
$ADB push "$TMPDIR/asanwrapper" /system/bin/asanwrapper adb_push "$TMPDIR/asanwrapper" /system/bin
$ADB shell rm /system/bin/app_process adb_shell rm /system/bin/app_process
$ADB shell ln -s /system/bin/app_process.wrap /system/bin/app_process adb_shell ln -s /system/bin/app_process.wrap /system/bin/app_process
$ADB shell chown root.shell \ adb_shell chown root.shell \
/system/lib/"$ASAN_RT" \ /system/lib/"$ASAN_RT" \
/system/bin/app_process.wrap \ /system/bin/app_process.wrap \
/system/bin/asanwrapper /system/bin/asanwrapper
$ADB shell chmod 644 \ adb_shell chmod 644 \
/system/lib/"$ASAN_RT" /system/lib/"$ASAN_RT"
$ADB shell chmod 755 \ adb_shell chmod 755 \
/system/bin/app_process.wrap \ /system/bin/app_process.wrap \
/system/bin/asanwrapper /system/bin/asanwrapper
# Make SELinux happy by keeping app_process wrapper and the shell # Make SELinux happy by keeping app_process wrapper and the shell
# it runs on in zygote domain. # it runs on in zygote domain.
ENFORCING=0 ENFORCING=0
if $ADB shell getenforce | grep Enforcing >/dev/null; then if adb_shell getenforce | grep Enforcing >/dev/null; then
# Sometimes shell is not allowed to change file contexts. # Sometimes shell is not allowed to change file contexts.
# Temporarily switch to permissive. # Temporarily switch to permissive.
ENFORCING=1 ENFORCING=1
$ADB shell setenforce 0 adb_shell setenforce 0
fi fi
$ADB shell cp /system/bin/sh /system/bin/sh-from-zygote adb_shell cp /system/bin/sh /system/bin/sh-from-zygote
if [[ PRE_L -eq 1 ]]; then if [[ PRE_L -eq 1 ]]; then
CTX=u:object_r:system_file:s0 CTX=u:object_r:system_file:s0
else else
CTX=u:object_r:zygote_exec:s0 CTX=u:object_r:zygote_exec:s0
fi fi
$ADB shell chcon $CTX \ adb_shell chcon $CTX \
/system/bin/sh-from-zygote \ /system/bin/sh-from-zygote \
/system/bin/app_process.wrap \ /system/bin/app_process.wrap \
/system/bin/app_process32 /system/bin/app_process32
if [ $ENFORCING == 1 ]; then if [ $ENFORCING == 1 ]; then
$ADB shell setenforce 1 adb_shell setenforce 1
fi fi
echo '>> Restarting shell (asynchronous)' echo '>> Restarting shell (asynchronous)'
$ADB shell stop adb_shell stop
$ADB shell start adb_shell start
echo '>> Please wait until the device restarts' echo '>> Please wait until the device restarts'
else else