Merge "Add a way to turn off unique_fd's operator int."
diff --git a/adb/Android.bp b/adb/Android.bp index b87f4cb..f2f4018 100644 --- a/adb/Android.bp +++ b/adb/Android.bp
@@ -60,6 +60,9 @@ // MinGW hides some things behind _POSIX_SOURCE. "-D_POSIX_SOURCE", + // libusb uses __stdcall on a variadic function, which gets ignored. + "-Wno-ignored-attributes", + // Not supported yet. "-Wno-thread-safety", ],
diff --git a/adb/adb.cpp b/adb/adb.cpp index e417f05..2dd22b3 100644 --- a/adb/adb.cpp +++ b/adb/adb.cpp
@@ -280,6 +280,9 @@ } else if (type == "sideload") { D("setting connection_state to kCsSideload"); t->SetConnectionState(kCsSideload); + } else if (type == "rescue") { + D("setting connection_state to kCsRescue"); + t->SetConnectionState(kCsRescue); } else { D("setting connection_state to kCsHost"); t->SetConnectionState(kCsHost);
diff --git a/adb/adb.h b/adb/adb.h index c60dcbc..3a6f059 100644 --- a/adb/adb.h +++ b/adb/adb.h
@@ -107,6 +107,7 @@ kCsHost, kCsRecovery, kCsSideload, + kCsRescue, }; inline bool ConnectionStateIsOnline(ConnectionState state) { @@ -116,6 +117,7 @@ case kCsHost: case kCsRecovery: case kCsSideload: + case kCsRescue: return true; default: return false;
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp index afb3ed0..d1b798b 100644 --- a/adb/client/commandline.cpp +++ b/adb/client/commandline.cpp
@@ -190,7 +190,7 @@ "scripting:\n" " wait-for[-TRANSPORT]-STATE\n" " wait for device to be in the given state\n" - " STATE: device, recovery, sideload, bootloader, or disconnect\n" + " STATE: device, recovery, rescue, sideload, bootloader, or disconnect\n" " TRANSPORT: usb, local, or any [default=any]\n" " get-state print offline | bootloader | device\n" " get-serialno print <serial-number>\n" @@ -838,26 +838,25 @@ #define SIDELOAD_HOST_BLOCK_SIZE (CHUNK_SIZE) -/* - * The sideload-host protocol serves the data in a file (given on the - * command line) to the client, using a simple protocol: - * - * - The connect message includes the total number of bytes in the - * file and a block size chosen by us. - * - * - The other side sends the desired block number as eight decimal - * digits (eg "00000023" for block 23). Blocks are numbered from - * zero. - * - * - We send back the data of the requested block. The last block is - * likely to be partial; when the last block is requested we only - * send the part of the block that exists, it's not padded up to the - * block size. - * - * - When the other side sends "DONEDONE" instead of a block number, - * we hang up. - */ -static int adb_sideload_host(const char* filename) { +// Connects to the sideload / rescue service on the device (served by minadbd) and sends over the +// data in an OTA package. +// +// It uses a simple protocol as follows. +// +// - The connect message includes the total number of bytes in the file and a block size chosen by +// us. +// +// - The other side sends the desired block number as eight decimal digits (e.g. "00000023" for +// block 23). Blocks are numbered from zero. +// +// - We send back the data of the requested block. The last block is likely to be partial; when the +// last block is requested we only send the part of the block that exists, it's not padded up to +// the block size. +// +// - When the other side sends "DONEDONE" or "FAILFAIL" instead of a block number, we have done all +// the data transfer. +// +static int adb_sideload_install(const char* filename, bool rescue_mode) { // TODO: use a LinePrinter instead... struct stat sb; if (stat(filename, &sb) == -1) { @@ -870,14 +869,18 @@ return -1; } - std::string service = - android::base::StringPrintf("sideload-host:%" PRId64 ":%d", - static_cast<int64_t>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE); + std::string service = android::base::StringPrintf( + "%s:%" PRId64 ":%d", rescue_mode ? "rescue-install" : "sideload-host", + static_cast<int64_t>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE); std::string error; unique_fd device_fd(adb_connect(service, &error)); if (device_fd < 0) { fprintf(stderr, "adb: sideload connection failed: %s\n", error.c_str()); + if (rescue_mode) { + return -1; + } + // If this is a small enough package, maybe this is an older device that doesn't // support sideload-host. Try falling back to the older (<= K) sideload method. if (sb.st_size > INT_MAX) { @@ -901,10 +904,14 @@ } buf[8] = '\0'; - if (strcmp("DONEDONE", buf) == 0) { + if (strcmp(kSideloadServiceExitSuccess, buf) == 0 || + strcmp(kSideloadServiceExitFailure, buf) == 0) { printf("\rTotal xfer: %.2fx%*s\n", static_cast<double>(xfer) / (sb.st_size ? sb.st_size : 1), static_cast<int>(strlen(filename) + 10), ""); + if (strcmp(kSideloadServiceExitFailure, buf) == 0) { + return 1; + } return 0; } @@ -1037,11 +1044,12 @@ } if (components[3] != "any" && components[3] != "bootloader" && components[3] != "device" && - components[3] != "recovery" && components[3] != "sideload" && + components[3] != "recovery" && components[3] != "rescue" && components[3] != "sideload" && components[3] != "disconnect") { fprintf(stderr, "adb: unknown state %s; " - "expected 'any', 'bootloader', 'device', 'recovery', 'sideload', or 'disconnect'\n", + "expected 'any', 'bootloader', 'device', 'recovery', 'rescue', 'sideload', or " + "'disconnect'\n", components[3].c_str()); return false; } @@ -1627,11 +1635,25 @@ return adb_kill_server() ? 0 : 1; } else if (!strcmp(argv[0], "sideload")) { if (argc != 2) error_exit("sideload requires an argument"); - if (adb_sideload_host(argv[1])) { + if (adb_sideload_install(argv[1], false /* rescue_mode */)) { return 1; } else { return 0; } + } else if (!strcmp(argv[0], "rescue")) { + // adb rescue getprop <prop> + // adb rescue install <filename> + if (argc != 3) error_exit("rescue requires two arguments"); + if (!strcmp(argv[1], "getprop")) { + return adb_connect_command(android::base::StringPrintf("rescue-getprop:%s", argv[2])); + } else if (!strcmp(argv[1], "install")) { + if (adb_sideload_install(argv[2], true /* rescue_mode */) != 0) { + return 1; + } + } else { + error_exit("invalid rescue argument"); + } + return 0; } else if (!strcmp(argv[0], "tcpip")) { if (argc != 2) error_exit("tcpip requires an argument"); int port;
diff --git a/adb/services.cpp b/adb/services.cpp index 5730c4e..46cab6e 100644 --- a/adb/services.cpp +++ b/adb/services.cpp
@@ -226,6 +226,8 @@ sinfo->state = kCsDevice; } else if (name == "-recovery") { sinfo->state = kCsRecovery; + } else if (name == "-rescue") { + sinfo->state = kCsRescue; } else if (name == "-sideload") { sinfo->state = kCsSideload; } else if (name == "-bootloader") {
diff --git a/adb/services.h b/adb/services.h index 0ce25ba..8f3919b 100644 --- a/adb/services.h +++ b/adb/services.h
@@ -23,5 +23,9 @@ constexpr char kShellServiceArgPty[] = "pty"; constexpr char kShellServiceArgShellProtocol[] = "v2"; +// Special flags sent by minadbd that indicate the end of sideload transfer and install result. +constexpr char kSideloadServiceExitSuccess[] = "DONEDONE"; +constexpr char kSideloadServiceExitFailure[] = "FAILFAIL"; + unique_fd create_service_thread(const char* service_name, std::function<void(unique_fd)> func); #endif // SERVICES_H_
diff --git a/adb/transport.cpp b/adb/transport.cpp index 15c3a9a..841865a 100644 --- a/adb/transport.cpp +++ b/adb/transport.cpp
@@ -1012,6 +1012,8 @@ return "host"; case kCsRecovery: return "recovery"; + case kCsRescue: + return "rescue"; case kCsNoPerm: return UsbNoPermissionsShortHelpText(); case kCsSideload:
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh index 71d3ecb..8979b0c 100755 --- a/bootstat/boot_reason_test.sh +++ b/bootstat/boot_reason_test.sh
@@ -25,6 +25,8 @@ # Best guess to an average device's reboot time, refined as tests return DURATION_DEFAULT=45 STOP_ON_FAILURE=false +progname="${0##*/}" +progpath="${0%${progname}}" # Helper functions @@ -42,11 +44,40 @@ adb devices | grep -v 'List of devices attached' | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null } +[ "USAGE: adb_sh <commands> </dev/stdin >/dev/stdout 2>/dev/stderr + +Returns: true if the command succeeded" ] +adb_sh() { + local args= + for i in "${@}"; do + [ -z "${args}" ] || args="${args} " + if [ X"${i}" != X"${i#\'}" ]; then + args="${args}${i}" + elif [ X"${i}" != X"${i#*\\}" ]; then + args="${args}`echo ${i} | sed 's/\\\\/\\\\\\\\/g'`" + elif [ X"${i}" != X"${i#* }" ]; then + args="${args}'${i}'" + elif [ X"${i}" != X"${i#*${TAB}}" ]; then + args="${args}'${i}'" + else + args="${args}${i}" + fi + done + adb shell "${args}" +} + +[ "USAGE: adb_su <commands> </dev/stdin >/dev/stdout 2>/dev/stderr + +Returns: true if the command running as root succeeded" ] +adb_su() { + adb_sh su root "${@}" +} + [ "USAGE: hasPstore Returns: true if device (likely) has pstore data" ] hasPstore() { - if inAdb && [ 0 -eq `adb shell su root ls /sys/fs/pstore | wc -l` ]; then + if inAdb && [ 0 -eq `adb_su ls /sys/fs/pstore </dev/null | wc -l` ]; then false fi } @@ -55,7 +86,7 @@ Returns the property value" ] get_property() { - adb shell getprop ${1} 2>&1 </dev/null + adb_sh getprop ${1} 2>&1 </dev/null } [ "USAGE: isDebuggable @@ -89,18 +120,18 @@ Returns: true if device supports and set boot reason injection" ] setBootloaderBootReason() { inAdb || ( echo "ERROR: device not in adb mode." >&2 ; false ) || return 1 - if [ -z "`adb shell ls /etc/init/bootstat-debug.rc 2>/dev/null`" ]; then + if [ -z "`adb_sh ls /etc/init/bootstat-debug.rc 2>/dev/null </dev/null`" ]; then echo "ERROR: '${TEST}' test requires /etc/init/bootstat-debug.rc" >&2 return 1 fi checkDebugBuild || return 1 - if adb shell su root "cat /proc/cmdline | tr '\\0 ' '\\n\\n'" | + if adb_su "cat /proc/cmdline | tr '\\0 ' '\\n\\n'" </dev/null | grep '^androidboot[.]bootreason=[^ ]' >/dev/null; then echo "ERROR: '${TEST}' test requires a device with a bootloader that" >&2 echo " does not set androidboot.bootreason kernel parameter." >&2 return 1 fi - adb shell su root setprop persist.test.boot.reason "'${1}'" 2>/dev/null + adb_su setprop persist.test.boot.reason "'${1}'" 2>/dev/null </dev/null test_reason="`get_property persist.test.boot.reason`" if [ X"${test_reason}" != X"${1}" ]; then echo "ERROR: can not set persist.test.boot.reason to '${1}'." >&2 @@ -299,7 +330,14 @@ return ${save_ret} } -[ "USAGE: report_bootstat_logs <expected> ... +[ "USAGE: adb_date >/dev/stdout + +Returns: report device epoch time (suitable for logcat -t)" ] +adb_date() { + adb_sh date +%s.%N </dev/null +} + +[ "USAGE: report_bootstat_logs [-t<timestamp>] <expected> ... if not prefixed with a minus (-), <expected> will become a series of expected matches: @@ -314,8 +352,11 @@ report_bootstat_logs() { save_ret=${?} match= + timestamp=-d for i in "${@}"; do - if [ X"${i}" != X"${i#-}" ] ; then + if [ X"${i}" != X"${i#-t}" ]; then + timestamp="${i}" + elif [ X"${i}" != X"${i#-}" ]; then match="${match} ${i#-}" else @@ -323,12 +364,13 @@ bootstat: Canonical boot reason: ${i}" fi done - adb logcat -b all -d | + adb logcat -b all ${timestamp} | grep bootstat[^e] | grep -v -F "bootstat: Service started: /system/bin/bootstat --record_boot_complete${match} bootstat: Failed to read /data/misc/bootstat/post_decrypt_time_elapsed: No such file or directory bootstat: Failed to parse boot time record: /data/misc/bootstat/post_decrypt_time_elapsed bootstat: Service started: /system/bin/bootstat --record_boot_reason +bootstat: Service started: /system/bin/bootstat --set_system_boot_reason bootstat: Service started: /system/bin/bootstat --record_time_since_factory_reset bootstat: Service started: /system/bin/bootstat -l bootstat: Service started: /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l @@ -341,6 +383,8 @@ init : processing action (post-fs-data) from (/system/etc/init/bootstat.rc init : processing action (boot) from (/system/etc/init/bootstat.rc init : processing action (ro.boot.bootreason=*) from (/system/etc/init/bootstat.rc +init : processing action (ro.boot.bootreason=* && post-fs) from (/system/etc/init/bootstat.rc +init : processing action (zygote-start) from (/system/etc/init/bootstat.rc init : processing action (sys.boot_completed=1 && sys.logbootcomplete=1) from (/system/etc/init/bootstat.rc (/system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)' (/system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)' @@ -355,6 +399,8 @@ (/system/bin/bootstat --record_boot_reason)' (pid${SPACE} (/system/bin/bootstat --record_time_since_factory_reset)'... (/system/bin/bootstat --record_time_since_factory_reset)' (pid${SPACE} + (/system/bin/bootstat --set_system_boot_reason)'... + (/system/bin/bootstat --set_system_boot_reason)' (pid${SPACE} (/system/bin/bootstat -l)'... (/system/bin/bootstat -l)' (pid " | grep -v 'bootstat: Unknown boot reason: $' # Hikey Special @@ -613,7 +659,7 @@ test_optional_ota() { checkDebugBuild || return duration_test - adb shell su root touch /data/misc/bootstat/build_date >&2 + adb_su touch /data/misc/bootstat/build_date >&2 </dev/null adb reboot ota wait_for_screen EXPECT_PROPERTY sys.boot.reason reboot,ota @@ -679,7 +725,7 @@ test_factory_reset() { checkDebugBuild || return duration_test - adb shell su root rm /data/misc/bootstat/build_date >&2 + adb_su rm /data/misc/bootstat/build_date >&2 </dev/null adb reboot >&2 wait_for_screen EXPECT_PROPERTY sys.boot.reason reboot,factory_reset @@ -715,7 +761,7 @@ wait_for_screen ( exit ${save_ret} ) # because one can not just do ?=${save_ret} EXPECT_PROPERTY sys.boot.reason reboot,factory_reset - EXPECT_PROPERTY sys.boot.reason.last "" + EXPECT_PROPERTY sys.boot.reason.last "\(\|bootloader\)" check_boilerplate_properties report_bootstat_logs reboot,factory_reset bootloader \ "-bootstat: Failed to read /data/misc/bootstat/last_boot_time_utc: No such file or directory" \ @@ -766,12 +812,12 @@ enterPstore # Send it _many_ times to combat devices with flakey pstore for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do - echo 'healthd: battery l=2 ' | adb shell su root tee /dev/kmsg >/dev/null + echo 'healthd: battery l=2 ' | adb_su tee /dev/kmsg >/dev/null done adb reboot cold >&2 adb wait-for-device wait_for_screen - adb shell su root \ + adb_su </dev/null \ cat /proc/fs/pstore/console-ramoops \ /proc/fs/pstore/console-ramoops-0 2>/dev/null | grep 'healthd: battery l=' | @@ -780,7 +826,7 @@ if ! EXPECT_PROPERTY sys.boot.reason reboot,battery >/dev/null 2>/dev/null; then # retry for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do - echo 'healthd: battery l=2 ' | adb shell su root tee /dev/kmsg >/dev/null + echo 'healthd: battery l=2 ' | adb_su tee /dev/kmsg >/dev/null done adb reboot cold >&2 adb wait-for-device @@ -806,7 +852,7 @@ test_optional_battery() { duration_test ">60" echo " power on request" >&2 - adb shell setprop sys.powerctl shutdown,battery + adb_sh setprop sys.powerctl shutdown,battery </dev/null sleep 5 echo -n "WARNING: Please power device back up, waiting ... " >&2 wait_for_screen -n >&2 @@ -827,7 +873,7 @@ test_optional_battery_thermal() { duration_test ">60" echo " power on request" >&2 - adb shell setprop sys.powerctl shutdown,thermal,battery + adb_sh setprop sys.powerctl shutdown,thermal,battery </dev/null sleep 5 echo -n "WARNING: Please power device back up, waiting ... " >&2 wait_for_screen -n >&2 @@ -866,7 +912,7 @@ panic_msg="\(kernel_panic,sysrq\|kernel_panic\)" pstore_ok=true fi - echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null + echo c | adb_su tee /proc/sysrq-trigger >/dev/null wait_for_screen EXPECT_PROPERTY sys.boot.reason ${panic_msg} EXPECT_PROPERTY sys.boot.reason.last ${panic_msg} @@ -893,8 +939,8 @@ panic_msg="\(kernel_panic,sysrq,test\|kernel_panic\)" pstore_ok=true fi - echo "SysRq : Trigger a crash : 'test'" | adb shell su root tee /dev/kmsg - echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null + echo "SysRq : Trigger a crash : 'test'" | adb_su tee /dev/kmsg + echo c | adb_su tee /proc/sysrq-trigger >/dev/null wait_for_screen EXPECT_PROPERTY sys.boot.reason ${panic_msg} EXPECT_PROPERTY sys.boot.reason.last ${panic_msg} @@ -924,7 +970,7 @@ pstore_ok=true fi echo "Kernel panic - not syncing: hung_task: blocked tasks" | - adb shell su root tee /dev/kmsg + adb_su tee /dev/kmsg adb reboot warm wait_for_screen EXPECT_PROPERTY sys.boot.reason ${panic_msg} @@ -956,7 +1002,7 @@ test_thermal_shutdown() { duration_test ">60" echo " power on request" >&2 - adb shell setprop sys.powerctl shutdown,thermal + adb_sh setprop sys.powerctl shutdown,thermal </dev/null sleep 5 echo -n "WARNING: Please power device back up, waiting ... " >&2 wait_for_screen -n >&2 @@ -977,7 +1023,7 @@ test_userrequested_shutdown() { duration_test ">60" echo " power on request" >&2 - adb shell setprop sys.powerctl shutdown,userrequested + adb_sh setprop sys.powerctl shutdown,userrequested </dev/null sleep 5 echo -n "WARNING: Please power device back up, waiting ... " >&2 wait_for_screen -n >&2 @@ -996,7 +1042,7 @@ - NB: should report reboot,shell" ] test_shell_reboot() { duration_test - adb shell reboot + adb_sh reboot </dev/null wait_for_screen EXPECT_PROPERTY sys.boot.reason reboot,shell EXPECT_PROPERTY sys.boot.reason.last reboot,shell @@ -1032,7 +1078,7 @@ test_optional_rescueparty() { blind_reboot_test echo "WARNING: legacy devices are allowed to fail following ro.boot.bootreason result" >&2 - EXPECT_PROPERTY ro.boot.bootreason reboot,rescueparty + EXPECT_PROPERTY ro.boot.bootreason '\(reboot\|reboot,rescueparty\)' } [ "USAGE: test_Its_Just_So_Hard_reboot @@ -1049,7 +1095,7 @@ else duration_test `expr ${DURATION_DEFAULT} + ${DURATION_DEFAULT}` fi - adb shell 'reboot "Its Just So Hard"' + adb_sh 'reboot "Its Just So Hard"' </dev/null wait_for_screen EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard EXPECT_PROPERTY sys.boot.reason.last reboot,its_just_so_hard @@ -1146,7 +1192,121 @@ run_bootloader } -[ "USAGE: ${0##*/} [-s SERIAL] [tests] +[ "USAGE: run_kBootReasonMap [--boot_reason_enum] value expected + +bootloader boot reason injection tests: +- if --boot_reason_enum run bootstat executable for result instead. +- inject boot reason into sys.boot.reason +- run bootstat --set_system_boot_reason +- check for expected enum +- " ] +run_kBootReasonMap() { + if [ X"--boot_reason_enum" = X"${1}" ]; then + shift + local sys_expected="${1}" + shift + local enum_expected="${1}" + adb_su bootstat --boot_reason_enum="${sys_expected}" | + ( + local retval=-1 + while read -r id match; do + if [ ${retval} = -1 -a ${enum_expected} = ${id} ]; then + retval=0 + fi + if [ ${enum_expected} != ${id} ]; then + echo "ERROR: ${enum_expected} ${sys_expected} got ${id} ${match}" >&2 + retval=1 + fi + done + exit ${retval} + ) + return + fi + local sys_expected="${1}" + shift + local enum_expected="${1}" + adb_su setprop sys.boot.reason "${sys_expected}" </dev/null + adb_su bootstat --record_boot_reason </dev/null + # Check values + EXPECT_PROPERTY sys.boot.reason "${sys_expected}" + local retval=${?} + local result=`adb_su stat -c %Y /data/misc/bootstat/system_boot_reason </dev/null 2>/dev/null` + [ "${enum_expected}" = "${result}" ] || + ( + [ -n "${result}" ] || result="<nothing>" + echo "ERROR: ${enum_expected} ${sys_expected} got ${result}" >&2 + false + ) || + retval=${?} + return ${retval} +} + +[ "USAGE: filter_kBootReasonMap </dev/stdin >/dev/stdout + +convert any regex expressions into a series of non-regex test strings" ] +filter_kBootReasonMap() { + while read -r id match; do + case ${match} in + 'reboot,[empty]') + echo ${id} # matches b/c of special case + echo ${id} reboot,y # matches b/c of regex + echo 1 reboot,empty # negative test (ID for unknown is 1) + ;; + reboot) + echo 1 reboog # negative test (ID for unknown is 1) + ;; + 'reboot,pmic_off_fault,.*') + echo ${id} reboot,pmic_off_fault,hello,world + echo ${id} reboot,pmic_off_fault, + echo 1 reboot,pmic_off_fault + ;; + esac + echo ${id} "${match}" # matches b/c of exact + done +} + +[ "USAGE: test_kBootReasonMap + +kBootReasonMap test +- (wait until screen is up, boot has completed) +- read bootstat for kBootReasonMap entries and test them all" ] +test_kBootReasonMap() { + checkDebugBuild || return + duration_test 15 + local tempfile="`mktemp`" + local arg=--boot_reason_enum + adb_su bootstat ${arg} </dev/null 2>/dev/null | + filter_kBootReasonMap >${tempfile} + if [ ! -s "${tempfile}" ]; then + wait_for_screen + arg= + sed -n <${progpath}bootstat.cpp \ + '/kBootReasonMap = {/,/^};/s/.*{"\([^"]*\)", *\([0-9][0-9]*\)},.*/\2 \1/p' | + sed 's/\\\\/\\/g' | + filter_kBootReasonMap >${tempfile} + fi + T=`adb_date` + retval=0 + while read -r enum string; do + if [ X"${string}" != X"${string#*[[].[]]}" -o X"${string}" != X"${string#*\\.}" ]; then + if [ 'reboot\.empty' != "${string}" ]; then + echo "WARNING: regex snuck through filter_kBootReasonMap ${enum} ${string}" >&2 + enum=1 + fi + fi + run_kBootReasonMap ${arg} "${string}" "${enum}" </dev/null || retval=${?} + done <${tempfile} + rm ${tempfile} + ( exit ${retval} ) + # See filter_kBootReasonMap() for negative tests and add them here too + report_bootstat_logs -t${T} \ + '-bootstat: Service started: bootstat --boot_reason_enum=' \ + '-bootstat: Unknown boot reason: reboot,empty' \ + '-bootstat: Unknown boot reason: reboog' \ + '-bootstat: Unknown boot reason: reboot,pmic_off_fault' +} + +[ "USAGE: ${progname} [-s SERIAL] [tests]... Mainline executive to run the above tests" ] @@ -1161,7 +1321,7 @@ if [ X"--macros" != X"${1}" ]; then if [ X"--help" = X"${1}" -o X"-h" = X"${1}" -o X"-?" = X"${1}" ]; then - echo "USAGE: ${0##*/} [-s SERIAL] [tests]" + echo "USAGE: ${progname} [-s SERIAL] [tests]..." echo tests - `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null` exit 0 fi @@ -1210,7 +1370,7 @@ Its_Just_So_Hard_reboot bootloader_normal bootloader_watchdog \ bootloader_kernel_panic bootloader_oem_powerkey \ bootloader_wdog_reset bootloader_cold bootloader_warm \ - bootloader_hard bootloader_recovery + bootloader_hard bootloader_recovery kBootReasonMap fi if [ X"nothing" = X"${1}" ]; then shift 1
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp index 1ce0ec4..558e6c4 100644 --- a/bootstat/bootstat.cpp +++ b/bootstat/bootstat.cpp
@@ -89,7 +89,7 @@ } void ShowHelp(const char* cmd) { - fprintf(stderr, "Usage: %s [options]\n", cmd); + fprintf(stderr, "Usage: %s [options]...\n", cmd); fprintf(stderr, "options include:\n" " -h, --help Show this help\n" @@ -99,7 +99,8 @@ " --value Optional value to associate with the boot event\n" " --record_boot_complete Record metrics related to the time for the device boot\n" " --record_boot_reason Record the reason why the device booted\n" - " --record_time_since_factory_reset Record the time since the device was reset\n"); + " --record_time_since_factory_reset Record the time since the device was reset\n" + " --boot_reason_enum=<reason> Report the match to the kBootReasonMap table\n"); } // Constructs a readable, printable string from the givencommand line @@ -120,9 +121,10 @@ // A mapping from boot reason string, as read from the ro.boot.bootreason // system property, to a unique integer ID. Viewers of log data dashboards for // the boot_reason metric may refer to this mapping to discern the histogram -// values. +// values. Regex matching, to manage the scale, as a minimum require either +// [, \ or * to be present in the string to switch to checking. const std::map<std::string, int32_t> kBootReasonMap = { - {"empty", kEmptyBootReason}, + {"reboot,[empty]", kEmptyBootReason}, {"__BOOTSTAT_UNKNOWN__", kUnknownBootReason}, {"normal", 2}, {"recovery", 3}, @@ -299,6 +301,9 @@ {"reboot,dm-verity_device_corrupted", 172}, {"reboot,dm-verity_enforcing", 173}, {"reboot,keys_clear", 174}, + {"reboot,pmic_off_fault,.*", 175}, + {"reboot,pmic_off_s3rst,.*", 176}, + {"reboot,pmic_off_other,.*", 177}, }; // Converts a string value representing the reason the system booted to an @@ -314,6 +319,16 @@ return kEmptyBootReason; } + for (const auto& [match, id] : kBootReasonMap) { + // Regex matches as a minimum require either [, \ or * to be present. + if (match.find_first_of("[\\*") == match.npos) continue; + // enforce match from beginning to end + auto exact = match; + if (exact[0] != '^') exact = "^" + exact; + if (exact[exact.size() - 1] != '$') exact = exact + "$"; + if (std::regex_search(boot_reason, std::regex(exact))) return id; + } + LOG(INFO) << "Unknown boot reason: " << boot_reason; return kUnknownBootReason; } @@ -1266,6 +1281,19 @@ boot_event_store.AddBootEventWithValue("time_since_factory_reset", time_since_factory_reset); } +// List the associated boot reason(s), if arg is nullptr then all. +void PrintBootReasonEnum(const char* arg) { + int value = -1; + if (arg != nullptr) { + value = BootReasonStrToEnum(arg); + } + for (const auto& [match, id] : kBootReasonMap) { + if ((value < 0) || (value == id)) { + printf("%u\t%s\n", id, match.c_str()); + } + } +} + } // namespace int main(int argc, char** argv) { @@ -1280,6 +1308,7 @@ static const char boot_complete_str[] = "record_boot_complete"; static const char boot_reason_str[] = "record_boot_reason"; static const char factory_reset_str[] = "record_time_since_factory_reset"; + static const char boot_reason_enum_str[] = "boot_reason_enum"; static const struct option long_options[] = { // clang-format off { "help", no_argument, NULL, 'h' }, @@ -1291,6 +1320,7 @@ { boot_complete_str, no_argument, NULL, 0 }, { boot_reason_str, no_argument, NULL, 0 }, { factory_reset_str, no_argument, NULL, 0 }, + { boot_reason_enum_str, optional_argument, NULL, 0 }, { NULL, 0, NULL, 0 } // clang-format on }; @@ -1315,6 +1345,8 @@ RecordBootReason(); } else if (option_name == factory_reset_str) { RecordFactoryReset(); + } else if (option_name == boot_reason_enum_str) { + PrintBootReasonEnum(optarg); } else { LOG(ERROR) << "Invalid option: " << option_name; }
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp index 0cf3378..2e226da 100644 --- a/debuggerd/Android.bp +++ b/debuggerd/Android.bp
@@ -183,6 +183,12 @@ ], }, }, + + product_variables: { + debuggable: { + cflags: ["-DROOT_POSSIBLE"], + }, + }, } cc_test {
diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp index 94fcfb2..c606970 100644 --- a/debuggerd/libdebuggerd/backtrace.cpp +++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -74,10 +74,7 @@ return; } - unwinder->SetDisplayBuildID(true); - for (size_t i = 0; i < unwinder->NumFrames(); i++) { - _LOG(&log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(i).c_str()); - } + log_backtrace(&log, unwinder, " "); } void dump_backtrace(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h index 238c00c..f189c45 100644 --- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h +++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -73,9 +73,12 @@ void _LOG(log_t* log, logtype ltype, const char* fmt, ...) __attribute__((format(printf, 3, 4))); namespace unwindstack { +class Unwinder; class Memory; } +void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix); + void dump_memory(log_t* log, unwindstack::Memory* backtrace, uint64_t addr, const std::string&); void read_with_default(const char* path, char* buf, size_t len, const char* default_value);
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp index cc337ed..d1726cd 100644 --- a/debuggerd/libdebuggerd/tombstone.cpp +++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -371,13 +371,6 @@ } } -void dump_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix) { - unwinder->SetDisplayBuildID(true); - for (size_t i = 0; i < unwinder->NumFrames(); i++) { - _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(i).c_str()); - } -} - static void print_register_row(log_t* log, const std::vector<std::pair<std::string, uint64_t>>& registers) { std::string output; @@ -470,7 +463,7 @@ _LOG(log, logtype::THREAD, "Failed to unwind"); } else { _LOG(log, logtype::BACKTRACE, "\nbacktrace:\n"); - dump_backtrace(log, unwinder, " "); + log_backtrace(log, unwinder, " "); _LOG(log, logtype::STACK, "\nstack:\n"); dump_stack(log, unwinder->frames(), unwinder->GetMaps(), unwinder->GetProcessMemory().get());
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp index 7aebea8..9b2779a 100644 --- a/debuggerd/libdebuggerd/utility.cpp +++ b/debuggerd/libdebuggerd/utility.cpp
@@ -38,6 +38,7 @@ #include <debuggerd/handler.h> #include <log/log.h> #include <unwindstack/Memory.h> +#include <unwindstack/Unwinder.h> using android::base::unique_fd; @@ -422,3 +423,22 @@ // Then give up... return "?"; } + +void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix) { + if (unwinder->elf_from_memory_not_file()) { + _LOG(log, logtype::BACKTRACE, + "%sNOTE: Function names and BuildId information is missing for some frames due\n", prefix); + _LOG(log, logtype::BACKTRACE, + "%sNOTE: to unreadable libraries. For unwinds of apps, only shared libraries\n", prefix); + _LOG(log, logtype::BACKTRACE, "%sNOTE: found under the lib/ directory are readable.\n", prefix); +#if defined(ROOT_POSSIBLE) + _LOG(log, logtype::BACKTRACE, + "%sNOTE: On this device, run setenforce 0 to make the libraries readable.\n", prefix); +#endif + } + + unwinder->SetDisplayBuildID(true); + for (size_t i = 0; i < unwinder->NumFrames(); i++) { + _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(i).c_str()); + } +}
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp index 5114f55..c1aafda 100644 --- a/fs_mgr/fs_mgr.cpp +++ b/fs_mgr/fs_mgr.cpp
@@ -1695,11 +1695,12 @@ std::string fs_mgr_get_super_partition_name(int slot) { // Devices upgrading to dynamic partitions are allowed to specify a super - // partition name, assumed to be A/B (non-A/B retrofit is not supported). - // For devices launching with dynamic partition support, the partition - // name must be "super". + // partition name. This includes cuttlefish, which is a non-A/B device. std::string super_partition; if (fs_mgr_get_boot_config_from_kernel_cmdline("super_partition", &super_partition)) { + if (fs_mgr_get_slot_suffix().empty()) { + return super_partition; + } std::string suffix; if (slot == 0) { suffix = "_a";
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp index 4043fc6..da049ef 100644 --- a/fs_mgr/fs_mgr_fstab.cpp +++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -38,6 +38,7 @@ using android::base::ParseByteCount; using android::base::ParseInt; +using android::base::ReadFileToString; using android::base::Split; using android::base::StartsWith; @@ -660,6 +661,8 @@ TransformFstabForGsi(fstab); } + SkipMountingPartitions(fstab); + return true; } @@ -687,6 +690,36 @@ return false; } + SkipMountingPartitions(fstab); + + return true; +} + +// For GSI to skip mounting /product and /product_services, until there are +// well-defined interfaces between them and /system. Otherwise, the GSI flashed +// on /system might not be able to work with /product and /product_services. +// When they're skipped here, /system/product and /system/product_services in +// GSI will be used. +bool SkipMountingPartitions(Fstab* fstab) { + constexpr const char kSkipMountConfig[] = "/system/etc/init/config/skip_mount.cfg"; + + std::string skip_config; + if (!ReadFileToString(kSkipMountConfig, &skip_config)) { + return true; + } + + for (const auto& skip_mount_point : Split(skip_config, "\n")) { + if (skip_mount_point.empty()) { + continue; + } + auto it = std::remove_if(fstab->begin(), fstab->end(), + [&skip_mount_point](const auto& entry) { + return entry.mount_point == skip_mount_point; + }); + fstab->erase(it, fstab->end()); + LOG(INFO) << "Skip mounting partition: " << skip_mount_point; + } + return true; }
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp index 8984752..e46e497 100644 --- a/fs_mgr/fs_mgr_overlayfs.cpp +++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -636,7 +636,7 @@ LERROR << mnt_type << " has no mkfs cookbook"; return false; } - command += " " + scratch_device; + command += " " + scratch_device + " >/dev/null 2>/dev/null </dev/null"; fs_mgr_set_blk_ro(scratch_device, false); auto ret = system(command.c_str()); if (ret) {
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h index 88da41d..d7afed6 100644 --- a/fs_mgr/include_fstab/fstab/fstab.h +++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -99,6 +99,7 @@ bool ReadFstabFromFile(const std::string& path, Fstab* fstab); bool ReadFstabFromDt(Fstab* fstab, bool log = true); bool ReadDefaultFstab(Fstab* fstab); +bool SkipMountingPartitions(Fstab* fstab); FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path);
diff --git a/init/Android.bp b/init/Android.bp index 69ee34f..69498ac 100644 --- a/init/Android.bp +++ b/init/Android.bp
@@ -61,6 +61,7 @@ static_libs: [ "libseccomp_policy", "libavb", + "libc++fs", "libcgrouprc_format", "libprotobuf-cpp-lite", "libpropertyinfoserializer",
diff --git a/init/Android.mk b/init/Android.mk index 39af0e6..b02c926 100644 --- a/init/Android.mk +++ b/init/Android.mk
@@ -65,15 +65,22 @@ LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT) LOCAL_UNSTRIPPED_PATH := $(TARGET_RAMDISK_OUT_UNSTRIPPED) +# Install adb_debug.prop into debug ramdisk. +# This allows adb root on a user build, when debug ramdisk is used. +LOCAL_REQUIRED_MODULES := \ + adb_debug.prop \ + # Set up the same mount points on the ramdisk that system-as-root contains. LOCAL_POST_INSTALL_CMD := mkdir -p \ $(TARGET_RAMDISK_OUT)/apex \ + $(TARGET_RAMDISK_OUT)/debug_ramdisk \ $(TARGET_RAMDISK_OUT)/dev \ $(TARGET_RAMDISK_OUT)/mnt \ $(TARGET_RAMDISK_OUT)/proc \ $(TARGET_RAMDISK_OUT)/sys \ LOCAL_STATIC_LIBRARIES := \ + libc++fs \ libfs_avb \ libfs_mgr \ libfec \
diff --git a/init/debug_ramdisk.h b/init/debug_ramdisk.h new file mode 100644 index 0000000..4e3a395 --- /dev/null +++ b/init/debug_ramdisk.h
@@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +namespace android { +namespace init { + +constexpr const char kDebugRamdiskProp[] = "/debug_ramdisk/adb_debug.prop"; +constexpr const char kDebugRamdiskSEPolicy[] = "/debug_ramdisk/userdebug_plat_sepolicy.cil"; + +} // namespace init +} // namespace android
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp index c566676..8b95e38 100644 --- a/init/first_stage_init.cpp +++ b/init/first_stage_init.cpp
@@ -26,6 +26,7 @@ #include <sys/types.h> #include <unistd.h> +#include <filesystem> #include <string> #include <vector> @@ -35,6 +36,7 @@ #include <cutils/android_reboot.h> #include <private/android_filesystem_config.h> +#include "debug_ramdisk.h" #include "first_stage_mount.h" #include "reboot_utils.h" #include "switch_root.h" @@ -44,6 +46,8 @@ using namespace std::literals; +namespace fs = std::filesystem; + namespace android { namespace init { @@ -159,6 +163,9 @@ CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV, "mode=0755,uid=0,gid=0")); + // /debug_ramdisk is used to preserve additional files from the debug ramdisk + CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV, + "mode=0755,uid=0,gid=0")); #undef CHECKCALL // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually @@ -202,7 +209,14 @@ // If this file is present, the second-stage init will use a userdebug sepolicy // and load adb_debug.prop to allow adb root, if the device is unlocked. if (access("/force_debuggable", F_OK) == 0) { - setenv("INIT_FORCE_DEBUGGABLE", "true", 1); + std::error_code ec; // to invoke the overloaded copy_file() that won't throw. + if (!fs::copy_file("/adb_debug.prop", kDebugRamdiskProp, ec) || + !fs::copy_file("/userdebug_plat_sepolicy.cil", kDebugRamdiskSEPolicy, ec)) { + LOG(ERROR) << "Failed to setup debug ramdisk"; + } else { + // setenv for second-stage init to read above kDebugRamdisk* files. + setenv("INIT_FORCE_DEBUGGABLE", "true", 1); + } } if (!DoFirstStageMount()) {
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp index 3900f72..3e76556 100644 --- a/init/first_stage_mount.cpp +++ b/init/first_stage_mount.cpp
@@ -43,7 +43,6 @@ #include "uevent_listener.h" #include "util.h" -using android::base::ReadFileToString; using android::base::Split; using android::base::Timer; using android::fs_mgr::AvbHandle; @@ -55,6 +54,7 @@ using android::fs_mgr::FstabEntry; using android::fs_mgr::ReadDefaultFstab; using android::fs_mgr::ReadFstabFromDt; +using android::fs_mgr::SkipMountingPartitions; using namespace std::literals; @@ -524,38 +524,10 @@ return true; } -// For GSI to skip mounting /product and /product_services, until there are -// well-defined interfaces between them and /system. Otherwise, the GSI flashed -// on /system might not be able to work with /product and /product_services. -// When they're skipped here, /system/product and /system/product_services in -// GSI will be used. -bool FirstStageMount::TrySkipMountingPartitions() { - constexpr const char kSkipMountConfig[] = "/system/etc/init/config/skip_mount.cfg"; - - std::string skip_config; - if (!ReadFileToString(kSkipMountConfig, &skip_config)) { - return true; - } - - for (const auto& skip_mount_point : Split(skip_config, "\n")) { - if (skip_mount_point.empty()) { - continue; - } - auto it = std::remove_if(fstab_.begin(), fstab_.end(), - [&skip_mount_point](const auto& entry) { - return entry.mount_point == skip_mount_point; - }); - fstab_.erase(it, fstab_.end()); - LOG(INFO) << "Skip mounting partition: " << skip_mount_point; - } - - return true; -} - bool FirstStageMount::MountPartitions() { if (!TrySwitchSystemAsRoot()) return false; - if (!TrySkipMountingPartitions()) return false; + if (!SkipMountingPartitions(&fstab_)) return false; for (auto current = fstab_.begin(); current != fstab_.end();) { // We've already mounted /system above.
diff --git a/init/init.cpp b/init/init.cpp index 0f44efd..c79e459 100644 --- a/init/init.cpp +++ b/init/init.cpp
@@ -621,6 +621,12 @@ }); } +static void UmountDebugRamdisk() { + if (umount("/debug_ramdisk") != 0) { + LOG(ERROR) << "Failed to umount /debug_ramdisk"; + } +} + int SecondStageMain(int argc, char** argv) { if (REBOOT_BOOTLOADER_ON_PANIC) { InstallRebootSignalHandlers(); @@ -630,6 +636,11 @@ InitKernelLogging(argv, InitAborter); LOG(INFO) << "init second stage started!"; + // Set init and its forked children's oom_adj. + if (auto result = WriteFile("/proc/1/oom_score_adj", "-1000"); !result) { + LOG(ERROR) << "Unable to write -1000 to /proc/1/oom_score_adj: " << result.error(); + } + // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote). GlobalSeccomp(); @@ -685,6 +696,7 @@ InstallSignalFdHandler(&epoll); property_load_boot_defaults(load_debug_prop); + UmountDebugRamdisk(); fs_mgr_vendor_overlay_mount_all(); export_oem_lock_status(); StartPropertyService(&epoll);
diff --git a/init/property_service.cpp b/init/property_service.cpp index 467568c..a1e9551 100644 --- a/init/property_service.cpp +++ b/init/property_service.cpp
@@ -41,7 +41,9 @@ #include <map> #include <memory> +#include <mutex> #include <queue> +#include <thread> #include <vector> #include <android-base/chrono_utils.h> @@ -56,6 +58,7 @@ #include <selinux/label.h> #include <selinux/selinux.h> +#include "debug_ramdisk.h" #include "epoll.h" #include "init.h" #include "persistent_properties.h" @@ -82,6 +85,8 @@ namespace android { namespace init { +static constexpr const char kRestoreconProperty[] = "selinux.restorecon_recursive"; + static bool persistent_properties_loaded = false; static int property_set_fd = -1; @@ -99,7 +104,24 @@ const char* name; }; +static int PropertyAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) { + auto* d = reinterpret_cast<PropertyAuditData*>(data); + + if (!d || !d->name || !d->cr) { + LOG(ERROR) << "AuditCallback invoked with null data arguments!"; + return 0; + } + + snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name, d->cr->pid, d->cr->uid, + d->cr->gid); + return 0; +} + void property_init() { + selinux_callback cb; + cb.func_audit = PropertyAuditCallback; + selinux_set_callback(SELINUX_CB_AUDIT, cb); + mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH); CreateSerializedPropertyInfo(); if (__system_property_area_init()) { @@ -186,88 +208,51 @@ return PROP_SUCCESS; } -typedef int (*PropertyAsyncFunc)(const std::string&, const std::string&); +class AsyncRestorecon { + public: + void TriggerRestorecon(const std::string& path) { + auto guard = std::lock_guard{mutex_}; + paths_.emplace(path); -struct PropertyChildInfo { - pid_t pid; - PropertyAsyncFunc func; - std::string name; - std::string value; + if (!thread_started_) { + thread_started_ = true; + std::thread{&AsyncRestorecon::ThreadFunction, this}.detach(); + } + } + + private: + void ThreadFunction() { + auto lock = std::unique_lock{mutex_}; + + while (!paths_.empty()) { + auto path = paths_.front(); + paths_.pop(); + + lock.unlock(); + if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) { + LOG(ERROR) << "Asynchronous restorecon of '" << path << "' failed'"; + } + android::base::SetProperty(kRestoreconProperty, path); + lock.lock(); + } + + thread_started_ = false; + } + + std::mutex mutex_; + std::queue<std::string> paths_; + bool thread_started_ = false; }; -static std::queue<PropertyChildInfo> property_children; - -static void PropertyChildLaunch() { - auto& info = property_children.front(); - pid_t pid = fork(); - if (pid < 0) { - LOG(ERROR) << "Failed to fork for property_set_async"; - while (!property_children.empty()) { - property_children.pop(); - } - return; - } - if (pid != 0) { - info.pid = pid; - } else { - if (info.func(info.name, info.value) != 0) { - LOG(ERROR) << "property_set_async(\"" << info.name << "\", \"" << info.value - << "\") failed"; - } - _exit(0); - } -} - -bool PropertyChildReap(pid_t pid) { - if (property_children.empty()) { - return false; - } - auto& info = property_children.front(); - if (info.pid != pid) { - return false; - } - std::string error; - if (PropertySet(info.name, info.value, &error) != PROP_SUCCESS) { - LOG(ERROR) << "Failed to set async property " << info.name << " to " << info.value << ": " - << error; - } - property_children.pop(); - if (!property_children.empty()) { - PropertyChildLaunch(); - } - return true; -} - -static uint32_t PropertySetAsync(const std::string& name, const std::string& value, - PropertyAsyncFunc func, std::string* error) { - if (value.empty()) { - return PropertySet(name, value, error); - } - - PropertyChildInfo info; - info.func = func; - info.name = name; - info.value = value; - property_children.push(info); - if (property_children.size() == 1) { - PropertyChildLaunch(); - } - return PROP_SUCCESS; -} - -static int RestoreconRecursiveAsync(const std::string& name, const std::string& value) { - return selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE); -} - uint32_t InitPropertySet(const std::string& name, const std::string& value) { if (StartsWith(name, "ctl.")) { LOG(ERROR) << "InitPropertySet: Do not set ctl. properties from init; call the Service " "functions directly"; return PROP_ERROR_INVALID_NAME; } - if (name == "selinux.restorecon_recursive") { - LOG(ERROR) << "InitPropertySet: Do not set selinux.restorecon_recursive from init; use the " - "restorecon builtin directly"; + if (name == kRestoreconProperty) { + LOG(ERROR) << "InitPropertySet: Do not set '" << kRestoreconProperty + << "' from init; use the restorecon builtin directly"; return PROP_ERROR_INVALID_NAME; } @@ -328,18 +313,20 @@ return result == sizeof(value); } + bool GetSourceContext(std::string* source_context) const { + char* c_source_context = nullptr; + if (getpeercon(socket_, &c_source_context) != 0) { + return false; + } + *source_context = c_source_context; + freecon(c_source_context); + return true; + } + int socket() { return socket_; } const ucred& cred() { return cred_; } - std::string source_context() const { - char* source_context = nullptr; - getpeercon(socket_, &source_context); - std::string result = source_context; - freecon(source_context); - return result; - } - private: bool PollIn(uint32_t* timeout_ms) { struct pollfd ufds[1]; @@ -505,8 +492,14 @@ << process_log_string; } - if (name == "selinux.restorecon_recursive") { - return PropertySetAsync(name, value, RestoreconRecursiveAsync, error); + // If a process other than init is writing a non-empty value, it means that process is + // requesting that init performs a restorecon operation on the path specified by 'value'. + // We use a thread to do this restorecon operation to prevent holding up init, as it may take + // a long time to complete. + if (name == kRestoreconProperty && cr.pid != 1 && !value.empty()) { + static AsyncRestorecon async_restorecon; + async_restorecon.TriggerRestorecon(value); + return PROP_SUCCESS; } return PropertySet(name, value, error); @@ -552,10 +545,15 @@ prop_name[PROP_NAME_MAX-1] = 0; prop_value[PROP_VALUE_MAX-1] = 0; + std::string source_context; + if (!socket.GetSourceContext(&source_context)) { + PLOG(ERROR) << "Unable to set property '" << prop_name << "': getpeercon() failed"; + return; + } + const auto& cr = socket.cred(); std::string error; - uint32_t result = - HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error); + uint32_t result = HandlePropertySet(prop_name, prop_value, source_context, cr, &error); if (result != PROP_SUCCESS) { LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error; @@ -574,9 +572,16 @@ return; } + std::string source_context; + if (!socket.GetSourceContext(&source_context)) { + PLOG(ERROR) << "Unable to set property '" << name << "': getpeercon() failed"; + socket.SendUint32(PROP_ERROR_PERMISSION_DENIED); + return; + } + const auto& cr = socket.cred(); std::string error; - uint32_t result = HandlePropertySet(name, value, socket.source_context(), cr, &error); + uint32_t result = HandlePropertySet(name, value, source_context, cr, &error); if (result != PROP_SUCCESS) { LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error; @@ -660,7 +665,7 @@ } if (StartsWith(key, "ctl.") || key == "sys.powerctl"s || - key == "selinux.restorecon_recursive"s) { + std::string{key} == kRestoreconProperty) { LOG(ERROR) << "Ignoring disallowed property '" << key << "' with special meaning in prop file '" << filename << "'"; continue; @@ -887,9 +892,8 @@ load_properties_from_file("/factory/factory.prop", "ro.*", &properties); if (load_debug_prop) { - constexpr static const char kAdbDebugProp[] = "/system/etc/adb_debug.prop"; - LOG(INFO) << "Loading " << kAdbDebugProp; - load_properties_from_file(kAdbDebugProp, nullptr, &properties); + LOG(INFO) << "Loading " << kDebugRamdiskProp; + load_properties_from_file(kDebugRamdiskProp, nullptr, &properties); } for (const auto& [name, value] : properties) { @@ -906,19 +910,6 @@ update_sys_usb_config(); } -static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) { - auto* d = reinterpret_cast<PropertyAuditData*>(data); - - if (!d || !d->name || !d->cr) { - LOG(ERROR) << "AuditCallback invoked with null data arguments!"; - return 0; - } - - snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name, d->cr->pid, d->cr->uid, - d->cr->gid); - return 0; -} - bool LoadPropertyInfoFromFile(const std::string& filename, std::vector<PropertyInfoEntry>* property_infos) { auto file_contents = std::string(); @@ -989,10 +980,6 @@ } void StartPropertyService(Epoll* epoll) { - selinux_callback cb; - cb.func_audit = SelinuxAuditCallback; - selinux_set_callback(SELINUX_CB_AUDIT, cb); - property_set("ro.property_service.version", "2"); property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
diff --git a/init/property_service.h b/init/property_service.h index 85e7bc0..7f9f844 100644 --- a/init/property_service.h +++ b/init/property_service.h
@@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef _INIT_PROPERTY_H -#define _INIT_PROPERTY_H +#pragma once #include <sys/socket.h> @@ -33,15 +32,10 @@ uint32_t HandlePropertySet(const std::string& name, const std::string& value, const std::string& source_context, const ucred& cr, std::string* error); -extern bool PropertyChildReap(pid_t pid); - -void property_init(void); -void property_load_boot_defaults(bool); -void load_persist_props(void); -void load_system_props(void); +void property_init(); +void property_load_boot_defaults(bool load_debug_prop); +void load_persist_props(); void StartPropertyService(Epoll* epoll); } // namespace init } // namespace android - -#endif /* _INIT_PROPERTY_H */
diff --git a/init/selinux.cpp b/init/selinux.cpp index aa66baa..132fc13 100644 --- a/init/selinux.cpp +++ b/init/selinux.cpp
@@ -64,6 +64,7 @@ #include <fs_avb/fs_avb.h> #include <selinux/android.h> +#include "debug_ramdisk.h" #include "reboot_utils.h" #include "util.h" @@ -271,8 +272,6 @@ } constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil"; -constexpr const char userdebug_plat_policy_cil_file[] = - "/system/etc/selinux/userdebug_plat_sepolicy.cil"; bool IsSplitPolicyDevice() { return access(plat_policy_cil_file, R_OK) != -1; @@ -292,7 +291,7 @@ const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE"); bool use_userdebug_policy = ((force_debuggable_env && "true"s == force_debuggable_env) && - AvbHandle::IsDeviceUnlocked() && access(userdebug_plat_policy_cil_file, F_OK) == 0); + AvbHandle::IsDeviceUnlocked() && access(kDebugRamdiskSEPolicy, F_OK) == 0); if (use_userdebug_policy) { LOG(WARNING) << "Using userdebug system sepolicy"; } @@ -367,7 +366,7 @@ // clang-format off std::vector<const char*> compile_args { "/system/bin/secilc", - use_userdebug_policy ? userdebug_plat_policy_cil_file : plat_policy_cil_file, + use_userdebug_policy ? kDebugRamdiskSEPolicy: plat_policy_cil_file, "-m", "-M", "true", "-G", "-N", "-c", version_as_string.c_str(), plat_mapping_file.c_str(),
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp index 0b03324..987b2f9 100644 --- a/init/sigchld_handler.cpp +++ b/init/sigchld_handler.cpp
@@ -29,7 +29,6 @@ #include <android-base/stringprintf.h> #include "init.h" -#include "property_service.h" #include "service.h" using android::base::StringPrintf; @@ -61,9 +60,7 @@ std::string wait_string; Service* service = nullptr; - if (PropertyChildReap(pid)) { - name = "Async property child"; - } else if (SubcontextChildReap(pid)) { + if (SubcontextChildReap(pid)) { name = "Subcontext"; } else { service = ServiceList::GetInstance().FindService(pid, &Service::pid);
diff --git a/libion/ion_4.12.h b/libion/ion_4.12.h index 6ae79d4..614510c 100644 --- a/libion/ion_4.12.h +++ b/libion/ion_4.12.h
@@ -1,125 +1,50 @@ -/* - * Adapted from drivers/staging/android/uapi/ion.h - * - * Copyright (C) 2011 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - +/**************************************************************************** + **************************************************************************** + *** + *** This header was automatically generated from a Linux kernel header + *** of the same name, to make information necessary for userspace to + *** call into the kernel available to libc. It contains only constants, + *** structures, and macros generated from the original header, and thus, + *** contains no copyrightable information. + *** + *** To edit the content of this header, modify the corresponding + *** source file (e.g. under external/kernel-headers/original/) then + *** run bionic/libc/kernel/tools/update_all.py + *** + *** Any manual change here will be lost the next time this script will + *** be run. You've been warned! + *** + **************************************************************************** + ****************************************************************************/ #ifndef _UAPI_LINUX_ION_NEW_H #define _UAPI_LINUX_ION_NEW_H - #include <linux/ioctl.h> #include <linux/types.h> - #define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8) - -/** - * DOC: Ion Userspace API - * - * create a client by opening /dev/ion - * most operations handled via following ioctls - * - */ - -/** - * struct ion_new_allocation_data - metadata passed from userspace for allocations - * @len: size of the allocation - * @heap_id_mask: mask of heap ids to allocate from - * @flags: flags passed to heap - * @handle: pointer that will be populated with a cookie to use to - * refer to this allocation - * - * Provided by userspace as an argument to the ioctl - added _new to denote - * this belongs to the new ION interface. - */ struct ion_new_allocation_data { - __u64 len; - __u32 heap_id_mask; - __u32 flags; - __u32 fd; - __u32 unused; + __u64 len; + __u32 heap_id_mask; + __u32 flags; + __u32 fd; + __u32 unused; }; - #define MAX_HEAP_NAME 32 - -/** - * struct ion_heap_data - data about a heap - * @name - first 32 characters of the heap name - * @type - heap type - * @heap_id - heap id for the heap - */ struct ion_heap_data { - char name[MAX_HEAP_NAME]; - __u32 type; - __u32 heap_id; - __u32 reserved0; - __u32 reserved1; - __u32 reserved2; + char name[MAX_HEAP_NAME]; + __u32 type; + __u32 heap_id; + __u32 reserved0; + __u32 reserved1; + __u32 reserved2; }; - -/** - * struct ion_heap_query - collection of data about all heaps - * @cnt - total number of heaps to be copied - * @heaps - buffer to copy heap data - */ struct ion_heap_query { - __u32 cnt; /* Total number of heaps to be copied */ - __u32 reserved0; /* align to 64bits */ - __u64 heaps; /* buffer to be populated */ - __u32 reserved1; - __u32 reserved2; + __u32 cnt; + __u32 reserved0; + __u64 heaps; + __u32 reserved1; + __u32 reserved2; }; - #define ION_IOC_MAGIC 'I' - -/** - * DOC: ION_IOC_NEW_ALLOC - allocate memory - * - * Takes an ion_allocation_data struct and returns it with the handle field - * populated with the opaque handle for the allocation. - * TODO: This IOCTL will clash by design; however, only one of - * ION_IOC_ALLOC or ION_IOC_NEW_ALLOC paths will be exercised, - * so this should not conflict. - */ #define ION_IOC_NEW_ALLOC _IOWR(ION_IOC_MAGIC, 0, struct ion_new_allocation_data) - -/** - * DOC: ION_IOC_FREE - free memory - * - * Takes an ion_handle_data struct and frees the handle. - * - * #define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data) - * This will come from the older kernels, so don't redefine here - */ - -/** - * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation - * - * Takes an ion_fd_data struct with the handle field populated with a valid - * opaque handle. Returns the struct with the fd field set to a file - * descriptor open in the current address space. This file descriptor - * can then be passed to another process. The corresponding opaque handle can - * be retrieved via ION_IOC_IMPORT. - * - * #define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data) - * This will come from the older kernels, so don't redefine here - */ - -/** - * DOC: ION_IOC_HEAP_QUERY - information about available heaps - * - * Takes an ion_heap_query structure and populates information about - * available Ion heaps. - */ #define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, struct ion_heap_query) - -#endif /* _UAPI_LINUX_ION_NEW_H */ +#endif
diff --git a/libion/original-kernel-headers/linux/ion_4.12.h b/libion/original-kernel-headers/linux/ion_4.12.h new file mode 100644 index 0000000..6ae79d4 --- /dev/null +++ b/libion/original-kernel-headers/linux/ion_4.12.h
@@ -0,0 +1,125 @@ +/* + * Adapted from drivers/staging/android/uapi/ion.h + * + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _UAPI_LINUX_ION_NEW_H +#define _UAPI_LINUX_ION_NEW_H + +#include <linux/ioctl.h> +#include <linux/types.h> + +#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8) + +/** + * DOC: Ion Userspace API + * + * create a client by opening /dev/ion + * most operations handled via following ioctls + * + */ + +/** + * struct ion_new_allocation_data - metadata passed from userspace for allocations + * @len: size of the allocation + * @heap_id_mask: mask of heap ids to allocate from + * @flags: flags passed to heap + * @handle: pointer that will be populated with a cookie to use to + * refer to this allocation + * + * Provided by userspace as an argument to the ioctl - added _new to denote + * this belongs to the new ION interface. + */ +struct ion_new_allocation_data { + __u64 len; + __u32 heap_id_mask; + __u32 flags; + __u32 fd; + __u32 unused; +}; + +#define MAX_HEAP_NAME 32 + +/** + * struct ion_heap_data - data about a heap + * @name - first 32 characters of the heap name + * @type - heap type + * @heap_id - heap id for the heap + */ +struct ion_heap_data { + char name[MAX_HEAP_NAME]; + __u32 type; + __u32 heap_id; + __u32 reserved0; + __u32 reserved1; + __u32 reserved2; +}; + +/** + * struct ion_heap_query - collection of data about all heaps + * @cnt - total number of heaps to be copied + * @heaps - buffer to copy heap data + */ +struct ion_heap_query { + __u32 cnt; /* Total number of heaps to be copied */ + __u32 reserved0; /* align to 64bits */ + __u64 heaps; /* buffer to be populated */ + __u32 reserved1; + __u32 reserved2; +}; + +#define ION_IOC_MAGIC 'I' + +/** + * DOC: ION_IOC_NEW_ALLOC - allocate memory + * + * Takes an ion_allocation_data struct and returns it with the handle field + * populated with the opaque handle for the allocation. + * TODO: This IOCTL will clash by design; however, only one of + * ION_IOC_ALLOC or ION_IOC_NEW_ALLOC paths will be exercised, + * so this should not conflict. + */ +#define ION_IOC_NEW_ALLOC _IOWR(ION_IOC_MAGIC, 0, struct ion_new_allocation_data) + +/** + * DOC: ION_IOC_FREE - free memory + * + * Takes an ion_handle_data struct and frees the handle. + * + * #define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data) + * This will come from the older kernels, so don't redefine here + */ + +/** + * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation + * + * Takes an ion_fd_data struct with the handle field populated with a valid + * opaque handle. Returns the struct with the fd field set to a file + * descriptor open in the current address space. This file descriptor + * can then be passed to another process. The corresponding opaque handle can + * be retrieved via ION_IOC_IMPORT. + * + * #define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data) + * This will come from the older kernels, so don't redefine here + */ + +/** + * DOC: ION_IOC_HEAP_QUERY - information about available heaps + * + * Takes an ion_heap_query structure and populates information about + * available Ion heaps. + */ +#define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, struct ion_heap_query) + +#endif /* _UAPI_LINUX_ION_NEW_H */
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h index 86e6035..7e6bf45 100644 --- a/libprocessgroup/include/processgroup/processgroup.h +++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -24,16 +24,20 @@ __BEGIN_DECLS static constexpr const char* CGROUPV2_CONTROLLER_NAME = "cgroup2"; -static constexpr const char* CGROUPS_RC_PATH = "/dev/cgroup_info/cgroup.rc"; bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path); bool CgroupGetAttributePath(const std::string& attr_name, std::string* path); bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path); -bool UsePerAppMemcg(); +bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache = false); +bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles, + bool use_fd_cache = false); -bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles); -bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles); +#ifndef __ANDROID_VNDK__ + +static constexpr const char* CGROUPS_RC_PATH = "/dev/cgroup_info/cgroup.rc"; + +bool UsePerAppMemcg(); // Return 0 and removes the cgroup if there are no longer any processes in it. // Returns -1 in the case of an error occurring or if there are processes still running @@ -54,4 +58,6 @@ void removeAllProcessGroups(void); +#endif // __ANDROID_VNDK__ + __END_DECLS
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp index abe63dd..1485ae9 100644 --- a/libprocessgroup/processgroup.cpp +++ b/libprocessgroup/processgroup.cpp
@@ -112,12 +112,16 @@ return memcg_supported; } -bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) { +bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles, + bool use_fd_cache) { const TaskProfiles& tp = TaskProfiles::GetInstance(); for (const auto& name : profiles) { - const TaskProfile* profile = tp.GetProfile(name); + TaskProfile* profile = tp.GetProfile(name); if (profile != nullptr) { + if (use_fd_cache) { + profile->EnableResourceCaching(); + } if (!profile->ExecuteForProcess(uid, pid)) { PLOG(WARNING) << "Failed to apply " << name << " process profile"; } @@ -129,12 +133,15 @@ return true; } -bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles) { +bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache) { const TaskProfiles& tp = TaskProfiles::GetInstance(); for (const auto& name : profiles) { - const TaskProfile* profile = tp.GetProfile(name); + TaskProfile* profile = tp.GetProfile(name); if (profile != nullptr) { + if (use_fd_cache) { + profile->EnableResourceCaching(); + } if (!profile->ExecuteForTask(tid)) { PLOG(WARNING) << "Failed to apply " << name << " task profile"; }
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp index ab0f1ca..fe4f93b 100644 --- a/libprocessgroup/sched_policy.cpp +++ b/libprocessgroup/sched_policy.cpp
@@ -46,26 +46,34 @@ switch (policy) { case SP_BACKGROUND: - return SetTaskProfiles(tid, {"HighEnergySaving", "ProcessCapacityLow", "LowIoPriority", - "TimerSlackHigh"}) + return SetTaskProfiles(tid, + {"HighEnergySaving", "ProcessCapacityLow", "LowIoPriority", + "TimerSlackHigh"}, + true) ? 0 : -1; case SP_FOREGROUND: case SP_AUDIO_APP: case SP_AUDIO_SYS: - return SetTaskProfiles(tid, {"HighPerformance", "ProcessCapacityHigh", "HighIoPriority", - "TimerSlackNormal"}) + return SetTaskProfiles(tid, + {"HighPerformance", "ProcessCapacityHigh", "HighIoPriority", + "TimerSlackNormal"}, + true) ? 0 : -1; case SP_TOP_APP: - return SetTaskProfiles(tid, {"MaxPerformance", "ProcessCapacityMax", "MaxIoPriority", - "TimerSlackNormal"}) + return SetTaskProfiles(tid, + {"MaxPerformance", "ProcessCapacityMax", "MaxIoPriority", + "TimerSlackNormal"}, + true) ? 0 : -1; case SP_SYSTEM: - return SetTaskProfiles(tid, {"ServiceCapacityLow", "TimerSlackNormal"}) ? 0 : -1; + return SetTaskProfiles(tid, {"ServiceCapacityLow", "TimerSlackNormal"}, true) ? 0 : -1; case SP_RESTRICTED: - return SetTaskProfiles(tid, {"ServiceCapacityRestricted", "TimerSlackNormal"}) ? 0 : -1; + return SetTaskProfiles(tid, {"ServiceCapacityRestricted", "TimerSlackNormal"}, true) + ? 0 + : -1; default: break; } @@ -126,17 +134,17 @@ switch (policy) { case SP_BACKGROUND: - return SetTaskProfiles(tid, {"HighEnergySaving", "TimerSlackHigh"}) ? 0 : -1; + return SetTaskProfiles(tid, {"HighEnergySaving", "TimerSlackHigh"}, true) ? 0 : -1; case SP_FOREGROUND: case SP_AUDIO_APP: case SP_AUDIO_SYS: - return SetTaskProfiles(tid, {"HighPerformance", "TimerSlackNormal"}) ? 0 : -1; + return SetTaskProfiles(tid, {"HighPerformance", "TimerSlackNormal"}, true) ? 0 : -1; case SP_TOP_APP: - return SetTaskProfiles(tid, {"MaxPerformance", "TimerSlackNormal"}) ? 0 : -1; + return SetTaskProfiles(tid, {"MaxPerformance", "TimerSlackNormal"}, true) ? 0 : -1; case SP_RT_APP: - return SetTaskProfiles(tid, {"RealtimePerformance", "TimerSlackNormal"}) ? 0 : -1; + return SetTaskProfiles(tid, {"RealtimePerformance", "TimerSlackNormal"}, true) ? 0 : -1; default: - return SetTaskProfiles(tid, {"TimerSlackNormal"}) ? 0 : -1; + return SetTaskProfiles(tid, {"TimerSlackNormal"}, true) ? 0 : -1; } return 0;
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp index 4b45c87..40d8d90 100644 --- a/libprocessgroup/task_profiles.cpp +++ b/libprocessgroup/task_profiles.cpp
@@ -138,31 +138,38 @@ SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p) : controller_(c), path_(p) { -#ifdef CACHE_FILE_DESCRIPTORS - // cache file descriptor only if path is app independent + // file descriptors for app-dependent paths can't be cached if (IsAppDependentPath(path_)) { // file descriptor is not cached - fd_.reset(-2); + fd_.reset(FDS_APP_DEPENDENT); return; } - std::string tasks_path = c.GetTasksFilePath(p); + // file descriptor can be cached later on request + fd_.reset(FDS_NOT_CACHED); +} + +void SetCgroupAction::EnableResourceCaching() { + if (fd_ != FDS_NOT_CACHED) { + return; + } + + std::string tasks_path = controller_.GetTasksFilePath(path_); if (access(tasks_path.c_str(), W_OK) != 0) { // file is not accessible - fd_.reset(-1); + fd_.reset(FDS_INACCESSIBLE); return; } unique_fd fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC))); if (fd < 0) { PLOG(ERROR) << "Failed to cache fd '" << tasks_path << "'"; - fd_.reset(-1); + fd_.reset(FDS_INACCESSIBLE); return; } fd_ = std::move(fd); -#endif } bool SetCgroupAction::AddTidToCgroup(int tid, int fd) { @@ -184,8 +191,7 @@ } bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const { -#ifdef CACHE_FILE_DESCRIPTORS - if (fd_ >= 0) { + if (IsFdValid()) { // fd is cached, reuse it if (!AddTidToCgroup(pid, fd_)) { LOG(ERROR) << "Failed to add task into cgroup"; @@ -194,12 +200,12 @@ return true; } - if (fd_ == -1) { + if (fd_ == FDS_INACCESSIBLE) { // no permissions to access the file, ignore return true; } - // this is app-dependent path, file descriptor is not cached + // this is app-dependent path and fd is not cached or cached fd can't be used std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid); unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC))); if (tmp_fd < 0) { @@ -212,25 +218,10 @@ } return true; -#else - std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid); - unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC))); - if (tmp_fd < 0) { - // no permissions to access the file, ignore - return true; - } - if (!AddTidToCgroup(pid, tmp_fd)) { - LOG(ERROR) << "Failed to add task into cgroup"; - return false; - } - - return true; -#endif } bool SetCgroupAction::ExecuteForTask(int tid) const { -#ifdef CACHE_FILE_DESCRIPTORS - if (fd_ >= 0) { + if (IsFdValid()) { // fd is cached, reuse it if (!AddTidToCgroup(tid, fd_)) { LOG(ERROR) << "Failed to add task into cgroup"; @@ -239,20 +230,23 @@ return true; } - if (fd_ == -1) { + if (fd_ == FDS_INACCESSIBLE) { // no permissions to access the file, ignore return true; } - // application-dependent path can't be used with tid - LOG(ERROR) << "Application profile can't be applied to a thread"; - return false; -#else + if (fd_ == FDS_APP_DEPENDENT) { + // application-dependent path can't be used with tid + PLOG(ERROR) << "Application profile can't be applied to a thread"; + return false; + } + + // fd was not cached because cached fd can't be used std::string tasks_path = controller()->GetTasksFilePath(path_); unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC))); if (tmp_fd < 0) { - // no permissions to access the file, ignore - return true; + PLOG(WARNING) << "Failed to open " << tasks_path << ": " << strerror(errno); + return false; } if (!AddTidToCgroup(tid, tmp_fd)) { LOG(ERROR) << "Failed to add task into cgroup"; @@ -260,7 +254,6 @@ } return true; -#endif } bool TaskProfile::ExecuteForProcess(uid_t uid, pid_t pid) const { @@ -284,6 +277,18 @@ return true; } +void TaskProfile::EnableResourceCaching() { + if (res_cached_) { + return; + } + + for (auto& element : elements_) { + element->EnableResourceCaching(); + } + + res_cached_ = true; +} + TaskProfiles& TaskProfiles::GetInstance() { // Deliberately leak this object to avoid a race between destruction on // process exit and concurrent access from another thread. @@ -411,7 +416,7 @@ return true; } -const TaskProfile* TaskProfiles::GetProfile(const std::string& name) const { +TaskProfile* TaskProfiles::GetProfile(const std::string& name) const { auto iter = profiles_.find(name); if (iter != profiles_.end()) {
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h index 37cc305..445647d 100644 --- a/libprocessgroup/task_profiles.h +++ b/libprocessgroup/task_profiles.h
@@ -48,6 +48,8 @@ // Default implementations will fail virtual bool ExecuteForProcess(uid_t, pid_t) const { return false; }; virtual bool ExecuteForTask(int) const { return false; }; + + virtual void EnableResourceCaching() {} }; // Profile actions @@ -110,31 +112,40 @@ virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const; virtual bool ExecuteForTask(int tid) const; + virtual void EnableResourceCaching(); const CgroupController* controller() const { return &controller_; } std::string path() const { return path_; } private: + enum FdState { + FDS_INACCESSIBLE = -1, + FDS_APP_DEPENDENT = -2, + FDS_NOT_CACHED = -3, + }; + CgroupController controller_; std::string path_; -#ifdef CACHE_FILE_DESCRIPTORS android::base::unique_fd fd_; -#endif static bool IsAppDependentPath(const std::string& path); static bool AddTidToCgroup(int tid, int fd); + + bool IsFdValid() const { return fd_ > FDS_INACCESSIBLE; } }; class TaskProfile { public: - TaskProfile() {} + TaskProfile() : res_cached_(false) {} void Add(std::unique_ptr<ProfileAction> e) { elements_.push_back(std::move(e)); } bool ExecuteForProcess(uid_t uid, pid_t pid) const; bool ExecuteForTask(int tid) const; + void EnableResourceCaching(); private: + bool res_cached_; std::vector<std::unique_ptr<ProfileAction>> elements_; }; @@ -143,7 +154,7 @@ // Should be used by all users static TaskProfiles& GetInstance(); - const TaskProfile* GetProfile(const std::string& name) const; + TaskProfile* GetProfile(const std::string& name) const; const ProfileAttribute* GetAttribute(const std::string& name) const; private:
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp index 28373b2..03658b4 100644 --- a/libunwindstack/MapInfo.cpp +++ b/libunwindstack/MapInfo.cpp
@@ -161,6 +161,7 @@ // option is used. std::unique_ptr<MemoryRange> memory(new MemoryRange(process_memory, start, end - start, 0)); if (Elf::IsValidElf(memory.get())) { + memory_backed_elf = true; return memory.release(); } @@ -184,6 +185,7 @@ new MemoryRange(process_memory, prev_map->start, prev_map->end - prev_map->start, 0)); ranges->Insert(new MemoryRange(process_memory, start, end - start, elf_offset)); + memory_backed_elf = true; return ranges; } @@ -237,6 +239,7 @@ std::lock_guard<std::mutex> guard(prev_map->mutex_); if (prev_map->elf.get() == nullptr) { prev_map->elf = elf; + prev_map->memory_backed_elf = memory_backed_elf; } } return elf.get();
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp index f3d2b5e..26626b5 100644 --- a/libunwindstack/Unwinder.cpp +++ b/libunwindstack/Unwinder.cpp
@@ -141,6 +141,7 @@ frames_.clear(); last_error_.code = ERROR_NONE; last_error_.address = 0; + elf_from_memory_not_file_ = false; ArchEnum arch = regs_->Arch(); @@ -164,6 +165,12 @@ break; } elf = map_info->GetElf(process_memory_, arch); + // If this elf is memory backed, and there is a valid file, then set + // an indicator that we couldn't open the file. + if (!elf_from_memory_not_file_ && map_info->memory_backed_elf && !map_info->name.empty() && + map_info->name[0] != '[') { + elf_from_memory_not_file_ = true; + } step_pc = regs_->pc(); rel_pc = elf->GetRelPc(step_pc, map_info); // Everyone except elf data in gdb jit debug maps uses the relative pc.
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h index e938986..025fd98 100644 --- a/libunwindstack/include/unwindstack/MapInfo.h +++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -75,6 +75,9 @@ // make it easier to move to a fine grained lock in the future. std::atomic_uintptr_t build_id; + // Set to true if the elf file data is coming from memory. + bool memory_backed_elf = false; + // This function guarantees it will never return nullptr. Elf* GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch);
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h index 75be209..52b3578 100644 --- a/libunwindstack/include/unwindstack/Unwinder.h +++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -111,6 +111,8 @@ void SetDexFiles(DexFiles* dex_files, ArchEnum arch); #endif + bool elf_from_memory_not_file() { return elf_from_memory_not_file_; } + ErrorCode LastErrorCode() { return last_error_.code; } uint64_t LastErrorAddress() { return last_error_.address; } @@ -132,6 +134,9 @@ bool resolve_names_ = true; bool embedded_soname_ = true; bool display_build_id_ = false; + // True if at least one elf file is coming from memory and not the related + // file. This is only true if there is an actual file backing up the elf. + bool elf_from_memory_not_file_ = false; ErrorData last_error_; };
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp index 2ddadef..6be8bdc 100644 --- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp +++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -108,6 +108,7 @@ info.end = 0x101; memory.reset(info.CreateMemory(process_memory_)); ASSERT_TRUE(memory.get() != nullptr); + EXPECT_FALSE(info.memory_backed_elf); } // Verify that if the offset is non-zero but there is no elf at the offset, @@ -117,6 +118,7 @@ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_)); ASSERT_TRUE(memory.get() != nullptr); + EXPECT_FALSE(info.memory_backed_elf); ASSERT_EQ(0x100U, info.elf_offset); EXPECT_EQ(0x100U, info.elf_start_offset); @@ -140,32 +142,40 @@ // offset to zero. info.elf_offset = 0; info.elf_start_offset = 0; + info.memory_backed_elf = false; memory.reset(info.CreateMemory(process_memory_)); ASSERT_TRUE(memory.get() != nullptr); + EXPECT_FALSE(info.memory_backed_elf); ASSERT_EQ(0x100U, info.elf_offset); EXPECT_EQ(0x100U, info.elf_start_offset); prev_info.offset = 0; info.elf_offset = 0; info.elf_start_offset = 0; + info.memory_backed_elf = false; memory.reset(info.CreateMemory(process_memory_)); ASSERT_TRUE(memory.get() != nullptr); + EXPECT_FALSE(info.memory_backed_elf); ASSERT_EQ(0x100U, info.elf_offset); EXPECT_EQ(0x100U, info.elf_start_offset); prev_info.flags = PROT_READ; info.elf_offset = 0; info.elf_start_offset = 0; + info.memory_backed_elf = false; memory.reset(info.CreateMemory(process_memory_)); ASSERT_TRUE(memory.get() != nullptr); + EXPECT_FALSE(info.memory_backed_elf); ASSERT_EQ(0x100U, info.elf_offset); EXPECT_EQ(0x100U, info.elf_start_offset); prev_info.name = info.name; info.elf_offset = 0; info.elf_start_offset = 0; + info.memory_backed_elf = false; memory.reset(info.CreateMemory(process_memory_)); ASSERT_TRUE(memory.get() != nullptr); + EXPECT_FALSE(info.memory_backed_elf); ASSERT_EQ(0x100U, info.elf_offset); EXPECT_EQ(0U, info.elf_start_offset); } @@ -177,6 +187,7 @@ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_)); ASSERT_TRUE(memory.get() != nullptr); + EXPECT_FALSE(info.memory_backed_elf); ASSERT_EQ(0U, info.elf_offset); EXPECT_EQ(0x1000U, info.elf_start_offset); @@ -201,6 +212,7 @@ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_)); ASSERT_TRUE(memory.get() != nullptr); + EXPECT_FALSE(info.memory_backed_elf); ASSERT_EQ(0U, info.elf_offset); EXPECT_EQ(0x1000U, info.elf_start_offset); @@ -218,6 +230,7 @@ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_)); ASSERT_TRUE(memory.get() != nullptr); + EXPECT_FALSE(info.memory_backed_elf); ASSERT_EQ(0U, info.elf_offset); EXPECT_EQ(0x2000U, info.elf_start_offset); @@ -259,6 +272,7 @@ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_)); ASSERT_TRUE(memory.get() != nullptr); + EXPECT_TRUE(info.memory_backed_elf); memset(buffer.data(), 0, buffer.size()); ASSERT_TRUE(memory->ReadFully(0, buffer.data(), buffer.size())); @@ -290,6 +304,7 @@ std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_)); ASSERT_TRUE(mem.get() != nullptr); + EXPECT_TRUE(map_info->memory_backed_elf); EXPECT_EQ(0x4000UL, map_info->elf_offset); EXPECT_EQ(0x4000UL, map_info->offset); EXPECT_EQ(0U, map_info->elf_start_offset); @@ -336,6 +351,7 @@ std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_)); ASSERT_TRUE(mem.get() != nullptr); + EXPECT_TRUE(map_info->memory_backed_elf); EXPECT_EQ(0x1000UL, map_info->elf_offset); EXPECT_EQ(0xb000UL, map_info->offset); EXPECT_EQ(0xa000UL, map_info->elf_start_offset); @@ -374,6 +390,7 @@ // extend over the executable segment. std::unique_ptr<Memory> memory(map_info->CreateMemory(process_memory_)); ASSERT_TRUE(memory.get() != nullptr); + EXPECT_FALSE(map_info->memory_backed_elf); std::vector<uint8_t> buffer(0x100); EXPECT_EQ(0x2000U, map_info->offset); EXPECT_EQ(0U, map_info->elf_offset); @@ -388,7 +405,9 @@ ASSERT_EQ(0x1000, lseek(elf_at_1000_.fd, 0x1000, SEEK_SET)); ASSERT_TRUE(android::base::WriteFully(elf_at_1000_.fd, &ehdr, sizeof(ehdr))); + map_info->memory_backed_elf = false; memory.reset(map_info->CreateMemory(process_memory_)); + EXPECT_FALSE(map_info->memory_backed_elf); EXPECT_EQ(0x2000U, map_info->offset); EXPECT_EQ(0x1000U, map_info->elf_offset); EXPECT_EQ(0x1000U, map_info->elf_start_offset);
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp index 48e038e..f635021 100644 --- a/libunwindstack/tests/UnwinderTest.cpp +++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -108,6 +108,24 @@ const auto& info2 = *--maps_->end(); info2->elf_offset = 0x8000; + elf = new ElfFake(new MemoryFake); + elf->FakeSetInterface(new ElfInterfaceFake(nullptr)); + AddMapInfo(0xc0000, 0xc1000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/unreadable.so", elf); + const auto& info3 = *--maps_->end(); + info3->memory_backed_elf = true; + + elf = new ElfFake(new MemoryFake); + elf->FakeSetInterface(new ElfInterfaceFake(nullptr)); + AddMapInfo(0xc1000, 0xc2000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "[vdso]", elf); + const auto& info4 = *--maps_->end(); + info4->memory_backed_elf = true; + + elf = new ElfFake(new MemoryFake); + elf->FakeSetInterface(new ElfInterfaceFake(nullptr)); + AddMapInfo(0xc2000, 0xc3000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "", elf); + const auto& info5 = *--maps_->end(); + info5->memory_backed_elf = true; + process_memory_.reset(new MemoryFake); } @@ -140,6 +158,7 @@ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_); unwinder.Unwind(); EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); ASSERT_EQ(3U, unwinder.NumFrames()); @@ -204,6 +223,7 @@ unwinder.SetResolveNames(false); unwinder.Unwind(); EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); ASSERT_EQ(3U, unwinder.NumFrames()); @@ -263,6 +283,7 @@ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_); unwinder.Unwind(); EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); ASSERT_EQ(1U, unwinder.NumFrames()); @@ -292,6 +313,7 @@ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_); unwinder.Unwind(); EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); ASSERT_EQ(1U, unwinder.NumFrames()); @@ -321,6 +343,7 @@ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_); unwinder.Unwind(); EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); ASSERT_EQ(1U, unwinder.NumFrames()); @@ -351,6 +374,7 @@ unwinder.SetEmbeddedSoname(false); unwinder.Unwind(); EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); ASSERT_EQ(1U, unwinder.NumFrames()); @@ -387,6 +411,7 @@ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_); unwinder.Unwind(); EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); ASSERT_EQ(1U, unwinder.NumFrames()); @@ -419,6 +444,7 @@ Unwinder unwinder(20, maps_.get(), ®s_, process_memory_); unwinder.Unwind(); EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); ASSERT_EQ(20U, unwinder.NumFrames()); @@ -461,6 +487,7 @@ std::vector<std::string> skip_libs{"libunwind.so", "libanother.so"}; unwinder.Unwind(&skip_libs); EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); ASSERT_EQ(3U, unwinder.NumFrames()); @@ -522,6 +549,7 @@ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_); unwinder.Unwind(); EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); ASSERT_EQ(2U, unwinder.NumFrames()); @@ -569,6 +597,7 @@ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_); unwinder.Unwind(); EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); ASSERT_EQ(1U, unwinder.NumFrames()); } @@ -588,6 +617,7 @@ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_); unwinder.Unwind(); EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); ASSERT_EQ(1U, unwinder.NumFrames()); } @@ -602,6 +632,7 @@ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_); unwinder.Unwind(); EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); ASSERT_EQ(1U, unwinder.NumFrames()); @@ -638,6 +669,7 @@ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_); unwinder.Unwind(); EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); ASSERT_EQ(3U, unwinder.NumFrames()); @@ -703,6 +735,7 @@ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_); unwinder.Unwind(); EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); ASSERT_EQ(2U, unwinder.NumFrames()); @@ -752,6 +785,7 @@ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_); unwinder.Unwind(); EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); ASSERT_EQ(2U, unwinder.NumFrames()); @@ -799,6 +833,7 @@ std::vector<std::string> skip_names{"libanother.so"}; unwinder.Unwind(&skip_names); EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); ASSERT_EQ(0U, unwinder.NumFrames()); } @@ -821,6 +856,7 @@ std::vector<std::string> suffixes{"oat"}; unwinder.Unwind(nullptr, &suffixes); EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); ASSERT_EQ(2U, unwinder.NumFrames()); // Make sure the elf was not initialized. @@ -879,6 +915,7 @@ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_); unwinder.Unwind(); EXPECT_EQ(ERROR_REPEATED_FRAME, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); ASSERT_EQ(3U, unwinder.NumFrames()); @@ -937,6 +974,7 @@ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_); unwinder.Unwind(); EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); ASSERT_EQ(2U, unwinder.NumFrames()); @@ -980,6 +1018,7 @@ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_); unwinder.Unwind(); EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); ASSERT_EQ(2U, unwinder.NumFrames()); @@ -1026,6 +1065,7 @@ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_); unwinder.Unwind(); EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); ASSERT_EQ(3U, unwinder.NumFrames()); @@ -1084,6 +1124,7 @@ Unwinder unwinder(1, maps_.get(), ®s_, process_memory_); unwinder.Unwind(); EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); ASSERT_EQ(1U, unwinder.NumFrames()); @@ -1103,6 +1144,96 @@ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags); } +TEST_F(UnwinderTest, elf_from_memory_not_file) { + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0)); + + regs_.set_pc(0xc0050); + regs_.set_sp(0x10000); + ElfInterfaceFake::FakePushStepData(StepData(0, 0, true)); + + Unwinder unwinder(64, maps_.get(), ®s_, process_memory_); + unwinder.Unwind(); + EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode()); + EXPECT_TRUE(unwinder.elf_from_memory_not_file()); + + ASSERT_EQ(1U, unwinder.NumFrames()); + + auto* frame = &unwinder.frames()[0]; + EXPECT_EQ(0U, frame->num); + EXPECT_EQ(0x50U, frame->rel_pc); + EXPECT_EQ(0xc0050U, frame->pc); + EXPECT_EQ(0x10000U, frame->sp); + EXPECT_EQ("Frame0", frame->function_name); + EXPECT_EQ(0U, frame->function_offset); + EXPECT_EQ("/fake/unreadable.so", frame->map_name); + EXPECT_EQ(0U, frame->map_elf_start_offset); + EXPECT_EQ(0U, frame->map_exact_offset); + EXPECT_EQ(0xc0000U, frame->map_start); + EXPECT_EQ(0xc1000U, frame->map_end); + EXPECT_EQ(0U, frame->map_load_bias); + EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags); +} + +TEST_F(UnwinderTest, elf_from_memory_but_no_valid_file_with_bracket) { + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0)); + + regs_.set_pc(0xc1050); + regs_.set_sp(0x10000); + ElfInterfaceFake::FakePushStepData(StepData(0, 0, true)); + + Unwinder unwinder(64, maps_.get(), ®s_, process_memory_); + unwinder.Unwind(); + EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); + + ASSERT_EQ(1U, unwinder.NumFrames()); + + auto* frame = &unwinder.frames()[0]; + EXPECT_EQ(0U, frame->num); + EXPECT_EQ(0x50U, frame->rel_pc); + EXPECT_EQ(0xc1050U, frame->pc); + EXPECT_EQ(0x10000U, frame->sp); + EXPECT_EQ("Frame0", frame->function_name); + EXPECT_EQ(0U, frame->function_offset); + EXPECT_EQ("[vdso]", frame->map_name); + EXPECT_EQ(0U, frame->map_elf_start_offset); + EXPECT_EQ(0U, frame->map_exact_offset); + EXPECT_EQ(0xc1000U, frame->map_start); + EXPECT_EQ(0xc2000U, frame->map_end); + EXPECT_EQ(0U, frame->map_load_bias); + EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags); +} + +TEST_F(UnwinderTest, elf_from_memory_but_empty_filename) { + ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0)); + + regs_.set_pc(0xc2050); + regs_.set_sp(0x10000); + ElfInterfaceFake::FakePushStepData(StepData(0, 0, true)); + + Unwinder unwinder(64, maps_.get(), ®s_, process_memory_); + unwinder.Unwind(); + EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode()); + EXPECT_FALSE(unwinder.elf_from_memory_not_file()); + + ASSERT_EQ(1U, unwinder.NumFrames()); + + auto* frame = &unwinder.frames()[0]; + EXPECT_EQ(0U, frame->num); + EXPECT_EQ(0x50U, frame->rel_pc); + EXPECT_EQ(0xc2050U, frame->pc); + EXPECT_EQ(0x10000U, frame->sp); + EXPECT_EQ("Frame0", frame->function_name); + EXPECT_EQ(0U, frame->function_offset); + EXPECT_EQ("", frame->map_name); + EXPECT_EQ(0U, frame->map_elf_start_offset); + EXPECT_EQ(0U, frame->map_exact_offset); + EXPECT_EQ(0xc2000U, frame->map_start); + EXPECT_EQ(0xc3000U, frame->map_end); + EXPECT_EQ(0U, frame->map_load_bias); + EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags); +} + // Verify format frame code. TEST_F(UnwinderTest, format_frame) { RegsFake regs_arm(10);
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp index bc1543b..858c0bb 100644 --- a/libziparchive/Android.bp +++ b/libziparchive/Android.bp
@@ -26,6 +26,8 @@ // Incorrectly warns when C++11 empty brace {} initializer is used. // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61489 "-Wno-missing-field-initializers", + "-Wconversion", + "-Wno-sign-conversion", ], // Enable -Wold-style-cast only for non-Windows targets. _islower_l,
diff --git a/libziparchive/entry_name_utils-inl.h b/libziparchive/entry_name_utils-inl.h index 1714586..10311b5 100644 --- a/libziparchive/entry_name_utils-inl.h +++ b/libziparchive/entry_name_utils-inl.h
@@ -20,9 +20,15 @@ #include <stddef.h> #include <stdint.h> +#include <limits> + // Check if |length| bytes at |entry_name| constitute a valid entry name. -// Entry names must be valid UTF-8 and must not contain '0'. +// Entry names must be valid UTF-8 and must not contain '0'. They also must +// fit into the central directory record. inline bool IsValidEntryName(const uint8_t* entry_name, const size_t length) { + if (length > std::numeric_limits<uint16_t>::max()) { + return false; + } for (size_t i = 0; i < length; ++i) { const uint8_t byte = entry_name[i]; if (byte == 0) { @@ -35,7 +41,8 @@ return false; } else { // 2-5 byte sequences. - for (uint8_t first = (byte & 0x7f) << 1; first & 0x80; first = (first & 0x7f) << 1) { + for (uint8_t first = static_cast<uint8_t>((byte & 0x7f) << 1); first & 0x80; + first = static_cast<uint8_t>((first & 0x7f) << 1)) { ++i; // Missing continuation byte..
diff --git a/libziparchive/include/ziparchive/zip_writer.h b/libziparchive/include/ziparchive/zip_writer.h index f6c8427..bd44fdb 100644 --- a/libziparchive/include/ziparchive/zip_writer.h +++ b/libziparchive/include/ziparchive/zip_writer.h
@@ -76,7 +76,7 @@ uint32_t uncompressed_size; uint16_t last_mod_time; uint16_t last_mod_date; - uint32_t padding_length; + uint16_t padding_length; off64_t local_file_header_offset; }; @@ -161,8 +161,8 @@ int32_t HandleError(int32_t error_code); int32_t PrepareDeflate(); - int32_t StoreBytes(FileEntry* file, const void* data, size_t len); - int32_t CompressBytes(FileEntry* file, const void* data, size_t len); + int32_t StoreBytes(FileEntry* file, const void* data, uint32_t len); + int32_t CompressBytes(FileEntry* file, const void* data, uint32_t len); int32_t FlushCompressedBytes(FileEntry* file); enum class State {
diff --git a/libziparchive/unzip.cpp b/libziparchive/unzip.cpp index cc059d8..c6def73 100644 --- a/libziparchive/unzip.cpp +++ b/libziparchive/unzip.cpp
@@ -85,7 +85,7 @@ static int CompressionRatio(int64_t uncompressed, int64_t compressed) { if (uncompressed == 0) return 0; - return (100LL * (uncompressed - compressed)) / uncompressed; + return static_cast<int>((100LL * (uncompressed - compressed)) / uncompressed); } static void MaybeShowHeader() {
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc index e1ec47a..596786a 100644 --- a/libziparchive/zip_archive.cc +++ b/libziparchive/zip_archive.cc
@@ -146,7 +146,7 @@ /* * Add a new entry to the hash table. */ -static int32_t AddToHash(ZipStringOffset* hash_table, const uint64_t hash_table_size, +static int32_t AddToHash(ZipStringOffset* hash_table, const uint32_t hash_table_size, const ZipString& name, const uint8_t* start) { const uint64_t hash = ComputeHash(name); uint32_t ent = hash & (hash_table_size - 1); @@ -214,7 +214,7 @@ } static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive, - off64_t file_length, off64_t read_amount, + off64_t file_length, uint32_t read_amount, uint8_t* scan_buffer) { const off64_t search_start = file_length - read_amount; @@ -230,7 +230,8 @@ * doing an initial minimal read; if we don't find it, retry with a * second read as above.) */ - int i = read_amount - sizeof(EocdRecord); + CHECK_LE(read_amount, std::numeric_limits<int32_t>::max()); + int32_t i = read_amount - sizeof(EocdRecord); for (; i >= 0; i--) { if (scan_buffer[i] == 0x50) { uint32_t* sig_addr = reinterpret_cast<uint32_t*>(&scan_buffer[i]); @@ -333,9 +334,9 @@ * * We start by pulling in the last part of the file. */ - off64_t read_amount = kMaxEOCDSearch; + uint32_t read_amount = kMaxEOCDSearch; if (file_length < read_amount) { - read_amount = file_length; + read_amount = static_cast<uint32_t>(file_length); } std::vector<uint8_t> scan_buffer(read_amount); @@ -532,7 +533,7 @@ return 0; } -static int32_t FindEntry(const ZipArchive* archive, const int ent, ZipEntry* data) { +static int32_t FindEntry(const ZipArchive* archive, const int32_t ent, ZipEntry* data) { const uint16_t nameLen = archive->hash_table[ent].name_length; // Recover the start of the central directory entry from the filename @@ -751,9 +752,10 @@ archive->central_directory.GetBasePtr()); if (ent < 0) { ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name); - return ent; + return static_cast<int32_t>(ent); // kEntryNotFound is safe to truncate. } - return FindEntry(archive, ent, data); + // We know there are at most hast_table_size entries, safe to truncate. + return FindEntry(archive, static_cast<uint32_t>(ent), data); } int32_t Next(void* cookie, ZipEntry* data, ZipString* name) { @@ -836,7 +838,6 @@ return FileWriter{}; } - int result = 0; #if defined(__linux__) if (declared_length > 0) { // Make sure we have enough space on the volume to extract the compressed @@ -848,7 +849,7 @@ // EOPNOTSUPP error when issued in other filesystems. // Hence, check for the return error code before concluding that the // disk does not have enough space. - result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length)); + long result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length)); if (result == -1 && errno == ENOSPC) { ALOGW("Zip: unable to allocate %" PRId64 " bytes at offset %" PRId64 ": %s", static_cast<int64_t>(declared_length), static_cast<int64_t>(current_offset), @@ -866,7 +867,7 @@ // Block device doesn't support ftruncate(2). if (!S_ISBLK(sb.st_mode)) { - result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset)); + long result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset)); if (result == -1) { ALOGW("Zip: unable to truncate file to %" PRId64 ": %s", static_cast<int64_t>(declared_length + current_offset), strerror(errno)); @@ -985,16 +986,16 @@ std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter); const bool compute_crc = (crc_out != nullptr); - uint64_t crc = 0; + uLong crc = 0; uint32_t remaining_bytes = compressed_length; do { /* read as much as we can */ if (zstream.avail_in == 0) { - const size_t read_size = (remaining_bytes > kBufSize) ? kBufSize : remaining_bytes; + const uint32_t read_size = (remaining_bytes > kBufSize) ? kBufSize : remaining_bytes; const uint32_t offset = (compressed_length - remaining_bytes); // Make sure to read at offset to ensure concurrent access to the fd. if (!reader.ReadAtOffset(read_buf.data(), read_size, offset)) { - ALOGW("Zip: inflate read failed, getSize = %zu: %s", read_size, strerror(errno)); + ALOGW("Zip: inflate read failed, getSize = %u: %s", read_size, strerror(errno)); return kIoError; } @@ -1018,7 +1019,8 @@ if (!writer->Append(&write_buf[0], write_size)) { return kIoError; } else if (compute_crc) { - crc = crc32(crc, &write_buf[0], write_size); + DCHECK_LE(write_size, kBufSize); + crc = crc32(crc, &write_buf[0], static_cast<uint32_t>(write_size)); } zstream.next_out = &write_buf[0]; @@ -1063,17 +1065,17 @@ const uint32_t length = entry->uncompressed_length; uint32_t count = 0; - uint64_t crc = 0; + uLong crc = 0; while (count < length) { uint32_t remaining = length - count; off64_t offset = entry->offset + count; // Safe conversion because kBufSize is narrow enough for a 32 bit signed value. - const size_t block_size = (remaining > kBufSize) ? kBufSize : remaining; + const uint32_t block_size = (remaining > kBufSize) ? kBufSize : remaining; // Make sure to read at offset to ensure concurrent access to the fd. if (!mapped_zip.ReadAtOffset(buf.data(), block_size, offset)) { - ALOGW("CopyFileToFile: copy read failed, block_size = %zu, offset = %" PRId64 ": %s", + ALOGW("CopyFileToFile: copy read failed, block_size = %u, offset = %" PRId64 ": %s", block_size, static_cast<int64_t>(offset), strerror(errno)); return kIoError; }
diff --git a/libziparchive/zip_archive_stream_entry.cc b/libziparchive/zip_archive_stream_entry.cc index 9ec89b1..1ec95b6 100644 --- a/libziparchive/zip_archive_stream_entry.cc +++ b/libziparchive/zip_archive_stream_entry.cc
@@ -27,6 +27,7 @@ #include <vector> #include <android-base/file.h> +#include <android-base/logging.h> #include <log/log.h> #include <ziparchive/zip_archive.h> @@ -77,6 +78,12 @@ } const std::vector<uint8_t>* ZipArchiveStreamEntryUncompressed::Read() { + // Simple sanity check. The vector should *only* be handled by this code. A caller + // should not const-cast and modify the capacity. This may invalidate next_out. + // + // Note: it would be better to store the results of data() across Read calls. + CHECK_EQ(data_.capacity(), kBufSize); + if (length_ == 0) { return nullptr; } @@ -97,7 +104,8 @@ if (bytes < data_.size()) { data_.resize(bytes); } - computed_crc32_ = crc32(computed_crc32_, data_.data(), data_.size()); + computed_crc32_ = static_cast<uint32_t>( + crc32(computed_crc32_, data_.data(), static_cast<uint32_t>(data_.size()))); length_ -= bytes; offset_ += bytes; return &data_; @@ -192,9 +200,15 @@ } const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() { + // Simple sanity check. The vector should *only* be handled by this code. A caller + // should not const-cast and modify the capacity. This may invalidate next_out. + // + // Note: it would be better to store the results of data() across Read calls. + CHECK_EQ(out_.capacity(), kBufSize); + if (z_stream_.avail_out == 0) { z_stream_.next_out = out_.data(); - z_stream_.avail_out = out_.size(); + z_stream_.avail_out = static_cast<uint32_t>(out_.size()); ; } @@ -203,7 +217,9 @@ if (compressed_length_ == 0) { return nullptr; } - size_t bytes = (compressed_length_ > in_.size()) ? in_.size() : compressed_length_; + DCHECK_LE(in_.size(), std::numeric_limits<uint32_t>::max()); // Should be buf size = 64k. + uint32_t bytes = (compressed_length_ > in_.size()) ? static_cast<uint32_t>(in_.size()) + : compressed_length_; ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_); errno = 0; if (!archive->mapped_zip.ReadAtOffset(in_.data(), bytes, offset_)) { @@ -230,14 +246,16 @@ if (z_stream_.avail_out == 0) { uncompressed_length_ -= out_.size(); - computed_crc32_ = crc32(computed_crc32_, out_.data(), out_.size()); + computed_crc32_ = static_cast<uint32_t>( + crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size()))); return &out_; } if (zerr == Z_STREAM_END) { if (z_stream_.avail_out != 0) { // Resize the vector down to the actual size of the data. out_.resize(out_.size() - z_stream_.avail_out); - computed_crc32_ = crc32(computed_crc32_, out_.data(), out_.size()); + computed_crc32_ = static_cast<uint32_t>( + crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size()))); uncompressed_length_ -= out_.size(); return &out_; }
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc index cea42d4..e471d5e 100644 --- a/libziparchive/zip_archive_test.cc +++ b/libziparchive/zip_archive_test.cc
@@ -27,6 +27,7 @@ #include <vector> #include <android-base/file.h> +#include <android-base/logging.h> #include <android-base/mapped_file.h> #include <android-base/unique_fd.h> #include <gtest/gtest.h> @@ -65,7 +66,8 @@ static void SetZipString(ZipString* zip_str, const std::string& str) { zip_str->name = reinterpret_cast<const uint8_t*>(str.c_str()); - zip_str->name_length = str.size(); + CHECK_LE(str.size(), std::numeric_limits<uint16_t>::max()); + zip_str->name_length = static_cast<uint16_t>(str.size()); } TEST(ziparchive, Open) { @@ -332,7 +334,7 @@ // Extract the entry to memory. std::vector<uint8_t> buffer(kAbUncompressedSize); - ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], buffer.size())); + ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], static_cast<uint32_t>(buffer.size()))); // Extract the entry to a file. TemporaryFile tmp_output_file; @@ -415,7 +417,8 @@ ASSERT_EQ(0, fstat(fd, &sb)); // Memory map the file first and open the archive from the memory region. - auto file_map{android::base::MappedFile::FromFd(fd, 0, sb.st_size, PROT_READ)}; + auto file_map{ + android::base::MappedFile::FromFd(fd, 0, static_cast<size_t>(sb.st_size), PROT_READ)}; ZipArchiveHandle handle; ASSERT_EQ(0, OpenArchiveFromMemory(file_map->data(), file_map->size(), zip_path.c_str(), &handle)); @@ -488,7 +491,8 @@ std::vector<uint8_t> cmp_data(entry.uncompressed_length); ASSERT_EQ(entry.uncompressed_length, read_data.size()); - ASSERT_EQ(0, ExtractToMemory(handle, &entry, cmp_data.data(), cmp_data.size())); + ASSERT_EQ( + 0, ExtractToMemory(handle, &entry, cmp_data.data(), static_cast<uint32_t>(cmp_data.size()))); ASSERT_TRUE(memcmp(read_data.data(), cmp_data.data(), read_data.size()) == 0); CloseArchive(handle); @@ -737,8 +741,8 @@ }; TEST(ziparchive, Inflate) { - const uint32_t compressed_length = kATxtContentsCompressed.size(); - const uint32_t uncompressed_length = kATxtContents.size(); + const uint32_t compressed_length = static_cast<uint32_t>(kATxtContentsCompressed.size()); + const uint32_t uncompressed_length = static_cast<uint32_t>(kATxtContents.size()); const VectorReader reader(kATxtContentsCompressed); {
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc index 0df0fa5..ae9d145 100644 --- a/libziparchive/zip_writer.cc +++ b/libziparchive/zip_writer.cc
@@ -169,8 +169,8 @@ year = 80; } - *out_date = (year - 80) << 9 | (ptm->tm_mon + 1) << 5 | ptm->tm_mday; - *out_time = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1; + *out_date = static_cast<uint16_t>((year - 80) << 9 | (ptm->tm_mon + 1) << 5 | ptm->tm_mday); + *out_time = static_cast<uint16_t>(ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1); } static void CopyFromFileEntry(const ZipWriter::FileEntry& src, bool use_data_descriptor, @@ -193,7 +193,8 @@ dst->compression_method = src.compression_method; dst->last_mod_time = src.last_mod_time; dst->last_mod_date = src.last_mod_date; - dst->file_name_length = src.path.size(); + DCHECK_LE(src.path.size(), std::numeric_limits<uint16_t>::max()); + dst->file_name_length = static_cast<uint16_t>(src.path.size()); dst->extra_field_length = src.padding_length; } @@ -203,6 +204,11 @@ return kInvalidState; } + // Can only have 16535 entries because of zip records. + if (files_.size() == std::numeric_limits<uint16_t>::max()) { + return HandleError(kIoError); + } + if (flags & kAlign32) { return kInvalidAlign32Flag; } @@ -210,10 +216,17 @@ if (powerof2(alignment) == 0) { return kInvalidAlignment; } + if (alignment > std::numeric_limits<uint16_t>::max()) { + return kInvalidAlignment; + } FileEntry file_entry = {}; file_entry.local_file_header_offset = current_offset_; file_entry.path = path; + // No support for larger than 4GB files. + if (file_entry.local_file_header_offset > std::numeric_limits<uint32_t>::max()) { + return HandleError(kIoError); + } if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(file_entry.path.data()), file_entry.path.size())) { @@ -237,7 +250,7 @@ std::vector<char> zero_padding; if (alignment != 0 && (offset & (alignment - 1))) { // Pad the extra field so the data will be aligned. - uint16_t padding = alignment - (offset % alignment); + uint16_t padding = static_cast<uint16_t>(alignment - (offset % alignment)); file_entry.padding_length = padding; offset += padding; zero_padding.resize(padding, 0); @@ -314,7 +327,8 @@ } z_stream_->next_out = buffer_.data(); - z_stream_->avail_out = buffer_.size(); + DCHECK_EQ(buffer_.size(), kBufSize); + z_stream_->avail_out = static_cast<uint32_t>(buffer_.size()); return kNoError; } @@ -322,25 +336,31 @@ if (state_ != State::kWritingEntry) { return HandleError(kInvalidState); } + // Need to be able to mark down data correctly. + if (len + static_cast<uint64_t>(current_file_entry_.uncompressed_size) > + std::numeric_limits<uint32_t>::max()) { + return HandleError(kIoError); + } + uint32_t len32 = static_cast<uint32_t>(len); int32_t result = kNoError; if (current_file_entry_.compression_method & kCompressDeflated) { - result = CompressBytes(¤t_file_entry_, data, len); + result = CompressBytes(¤t_file_entry_, data, len32); } else { - result = StoreBytes(¤t_file_entry_, data, len); + result = StoreBytes(¤t_file_entry_, data, len32); } if (result != kNoError) { return result; } - current_file_entry_.crc32 = - crc32(current_file_entry_.crc32, reinterpret_cast<const Bytef*>(data), len); - current_file_entry_.uncompressed_size += len; + current_file_entry_.crc32 = static_cast<uint32_t>( + crc32(current_file_entry_.crc32, reinterpret_cast<const Bytef*>(data), len32)); + current_file_entry_.uncompressed_size += len32; return kNoError; } -int32_t ZipWriter::StoreBytes(FileEntry* file, const void* data, size_t len) { +int32_t ZipWriter::StoreBytes(FileEntry* file, const void* data, uint32_t len) { CHECK(state_ == State::kWritingEntry); if (fwrite(data, 1, len, file_) != len) { @@ -351,7 +371,7 @@ return kNoError; } -int32_t ZipWriter::CompressBytes(FileEntry* file, const void* data, size_t len) { +int32_t ZipWriter::CompressBytes(FileEntry* file, const void* data, uint32_t len) { CHECK(state_ == State::kWritingEntry); CHECK(z_stream_); CHECK(z_stream_->next_out != nullptr); @@ -379,7 +399,8 @@ // Reset the output buffer for the next input. z_stream_->next_out = buffer_.data(); - z_stream_->avail_out = buffer_.size(); + DCHECK_EQ(buffer_.size(), kBufSize); + z_stream_->avail_out = static_cast<uint32_t>(buffer_.size()); } } return kNoError; @@ -404,7 +425,8 @@ current_offset_ += write_bytes; z_stream_->next_out = buffer_.data(); - z_stream_->avail_out = buffer_.size(); + DCHECK_EQ(buffer_.size(), kBufSize); + z_stream_->avail_out = static_cast<uint32_t>(buffer_.size()); } if (zerr != Z_STREAM_END) { return HandleError(kZlibError); @@ -491,7 +513,11 @@ cdr.crc32 = file.crc32; cdr.compressed_size = file.compressed_size; cdr.uncompressed_size = file.uncompressed_size; - cdr.file_name_length = file.path.size(); + // Checked in IsValidEntryName. + DCHECK_LE(file.path.size(), std::numeric_limits<uint16_t>::max()); + cdr.file_name_length = static_cast<uint16_t>(file.path.size()); + // Checked in StartAlignedEntryWithTime. + DCHECK_LE(file.local_file_header_offset, std::numeric_limits<uint32_t>::max()); cdr.local_file_header_offset = static_cast<uint32_t>(file.local_file_header_offset); if (fwrite(&cdr, sizeof(cdr), 1, file_) != 1) { return HandleError(kIoError); @@ -508,10 +534,15 @@ er.eocd_signature = EocdRecord::kSignature; er.disk_num = 0; er.cd_start_disk = 0; - er.num_records_on_disk = files_.size(); - er.num_records = files_.size(); - er.cd_size = current_offset_ - startOfCdr; - er.cd_start_offset = startOfCdr; + // Checked when adding entries. + DCHECK_LE(files_.size(), std::numeric_limits<uint16_t>::max()); + er.num_records_on_disk = static_cast<uint16_t>(files_.size()); + er.num_records = static_cast<uint16_t>(files_.size()); + if (current_offset_ > std::numeric_limits<uint32_t>::max()) { + return HandleError(kIoError); + } + er.cd_size = static_cast<uint32_t>(current_offset_ - startOfCdr); + er.cd_start_offset = static_cast<uint32_t>(startOfCdr); if (fwrite(&er, sizeof(er), 1, file_) != 1) { return HandleError(kIoError);
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc index 7322afb..63adbbc 100644 --- a/libziparchive/zip_writer_test.cc +++ b/libziparchive/zip_writer_test.cc
@@ -279,7 +279,8 @@ std::vector<uint8_t> decompress(kBufSize); memset(decompress.data(), 0, kBufSize); - ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(), decompress.size())); + ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(), + static_cast<uint32_t>(decompress.size()))); EXPECT_EQ(0, memcmp(decompress.data(), buffer.data(), kBufSize)) << "Input buffer and output buffer are different."; @@ -391,7 +392,7 @@ actual.resize(expected.size()); uint8_t* buffer = reinterpret_cast<uint8_t*>(&*actual.begin()); - if (ExtractToMemory(handle, zip_entry, buffer, actual.size()) != 0) { + if (ExtractToMemory(handle, zip_entry, buffer, static_cast<uint32_t>(actual.size())) != 0) { return ::testing::AssertionFailure() << "failed to extract entry"; }
diff --git a/rootdir/Android.mk b/rootdir/Android.mk index 0044534..f084cd2 100644 --- a/rootdir/Android.mk +++ b/rootdir/Android.mk
@@ -97,7 +97,7 @@ # # create some directories (some are mount points) and symlinks LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \ - dev proc sys system data odm oem acct config storage mnt apex $(BOARD_ROOT_EXTRA_FOLDERS)); \ + dev proc sys system data odm oem acct config storage mnt apex debug_ramdisk $(BOARD_ROOT_EXTRA_FOLDERS)); \ ln -sf /system/bin $(TARGET_ROOT_OUT)/bin; \ ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \ ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \ @@ -377,4 +377,13 @@ $(hide) $(foreach lib,$(PRIVATE_VNDK_SAMEPROCESS_LIBRARIES), \ echo $(lib).so >> $@;) +####################################### +# adb_debug.prop in debug ramdisk +include $(CLEAR_VARS) +LOCAL_MODULE := adb_debug.prop +LOCAL_SRC_FILES := $(LOCAL_MODULE) +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_DEBUG_RAMDISK_OUT) +include $(BUILD_PREBUILT) + include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/rootdir/adb_debug.prop b/rootdir/adb_debug.prop new file mode 100644 index 0000000..37e2f2d --- /dev/null +++ b/rootdir/adb_debug.prop
@@ -0,0 +1,12 @@ +# Note: This file will be loaded with highest priority to override +# other system properties, if a special ramdisk with "/force_debuggable" +# is used and the device is unlocked. + +# Disable adb authentication to allow test automation on user build GSI +ro.adb.secure=0 + +# Allow 'adb root' on user build GSI +ro.debuggable=1 + +# Introduce this property to indicate that init has loaded adb_debug.prop +ro.force.debuggable=1
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt index 8792671..e081bdf 100644 --- a/rootdir/etc/ld.config.legacy.txt +++ b/rootdir/etc/ld.config.legacy.txt
@@ -99,7 +99,6 @@ namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors namespace.media.links = default -namespace.media.link.default.shared_libs = libandroid.so namespace.media.link.default.shared_libs += libbinder_ndk.so namespace.media.link.default.shared_libs += libc.so namespace.media.link.default.shared_libs += libcgrouprc.so
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt index b486411..3f9882a 100644 --- a/rootdir/etc/ld.config.txt +++ b/rootdir/etc/ld.config.txt
@@ -179,7 +179,6 @@ namespace.media.links = default namespace.media.link.default.shared_libs = %LLNDK_LIBRARIES% -namespace.media.link.default.shared_libs += libandroid.so namespace.media.link.default.shared_libs += libbinder_ndk.so namespace.media.link.default.shared_libs += libmediametrics.so namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES% @@ -622,7 +621,6 @@ namespace.media.links = default namespace.media.link.default.shared_libs = %LLNDK_LIBRARIES% -namespace.media.link.default.shared_libs += libandroid.so namespace.media.link.default.shared_libs += libbinder_ndk.so namespace.media.link.default.shared_libs += libmediametrics.so namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt index 12007dc..6d89886 100644 --- a/rootdir/etc/ld.config.vndk_lite.txt +++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -121,7 +121,6 @@ namespace.media.links = default namespace.media.link.default.shared_libs = %LLNDK_LIBRARIES% -namespace.media.link.default.shared_libs += libandroid.so namespace.media.link.default.shared_libs += libbinder_ndk.so namespace.media.link.default.shared_libs += libmediametrics.so namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES% @@ -445,7 +444,6 @@ namespace.media.links = default namespace.media.link.default.shared_libs = %LLNDK_LIBRARIES% -namespace.media.link.default.shared_libs += libandroid.so namespace.media.link.default.shared_libs += libbinder_ndk.so namespace.media.link.default.shared_libs += libmediametrics.so namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
diff --git a/rootdir/init.rc b/rootdir/init.rc index e94b8e2..295e704 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc
@@ -13,9 +13,6 @@ # Cgroups are mounted right before early-init using list from /etc/cgroups.json on early-init - # Set init and its forked children's oom_adj. - write /proc/1/oom_score_adj -1000 - # Disable sysrq from keyboard write /proc/sys/kernel/sysrq 0 @@ -611,6 +608,10 @@ # IOCTLs on ashmem fds any more. setprop sys.use_memfd false + # Set fscklog permission + chown root system /dev/fscklogs/log + chmod 0770 /dev/fscklogs/log + # It is recommended to put unnecessary data/ initialization from post-fs-data # to start-zygote in device's init.rc to unblock zygote start. on zygote-start && property:ro.crypto.state=unencrypted