summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Nandana Dutt <nandana@google.com> 2018-11-02 16:17:05 +0000
committer Nandana Dutt <nandana@google.com> 2018-11-13 14:44:50 +0000
commit4b392be4068fba0307191d15145f3306433c9ebc (patch)
tree3085cf8870b6f7d432b29780563db03b49e345e4
parent13ad9e89040015ea830a8f6a6dbff0ae5ab64d83 (diff)
Added system property (dumpstate.unroot).
Dumpstate starts off as root but drops root as soon as it's done with some important commands and actions that need root privilege. After that, on non-user builds it's still possible to assume root with 'su root <cmd>', which it does with AsRoot() option. This means that the output of such commands (i.e. commands that are run after dropping root, and require root priviliges) on user-debug builds differ from those on user devices. This can make it hard for developers to anticipate what bugreports from user builds are going to look like. dumpstate.unroot property allows ignoring the availability of "su" in non-user builds and emulate the user build behavior for those commands. Fixes: 117980186 Test: adb shell setprop dumpstate.unroot true && \ adb shell cmd activity bug-report --progress Test: adb shell /data/nativetest64/dumpstate_test/dumpstate_test --gtest_filter=DumpstateTest.RunCommandAsRootNonUserBuild_withUnroot Test: adb shell /data/nativetest64/dumpstate_test/dumpstate_test --gtest_filter=DumpstateTest.RunCommandAsRootIfAvailableOnDebugBuild_withUnroot Change-Id: I8d8d2f0733a8d13754c7d2e6a0d323c73afcbbbb
-rw-r--r--cmds/dumpstate/DumpstateUtil.cpp17
-rw-r--r--cmds/dumpstate/DumpstateUtil.h19
-rw-r--r--cmds/dumpstate/README.md6
-rw-r--r--cmds/dumpstate/tests/dumpstate_test.cpp56
4 files changed, 93 insertions, 5 deletions
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
index 600a500e86..97c8ae2045 100644
--- a/cmds/dumpstate/DumpstateUtil.cpp
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -101,13 +101,16 @@ CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Al
}
CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRoot() {
- values.account_mode_ = SU_ROOT;
+ if (!PropertiesHelper::IsUnroot()) {
+ values.account_mode_ = SU_ROOT;
+ }
return *this;
}
CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRootIfAvailable() {
- if (!PropertiesHelper::IsUserBuild())
- values.account_mode_ = SU_ROOT;
+ if (!PropertiesHelper::IsUserBuild()) {
+ return AsRoot();
+ }
return *this;
}
@@ -176,6 +179,7 @@ CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeoutInMs(int64_t ti
std::string PropertiesHelper::build_type_ = "";
int PropertiesHelper::dry_run_ = -1;
+int PropertiesHelper::unroot_ = -1;
bool PropertiesHelper::IsUserBuild() {
if (build_type_.empty()) {
@@ -191,6 +195,13 @@ bool PropertiesHelper::IsDryRun() {
return dry_run_ == 1;
}
+bool PropertiesHelper::IsUnroot() {
+ if (unroot_ == -1) {
+ unroot_ = android::base::GetBoolProperty("dumpstate.unroot", false) ? 1 : 0;
+ }
+ return unroot_ == 1;
+}
+
int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
if (fd.get() < 0) {
diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h
index 8342099821..d69ffbfc98 100644
--- a/cmds/dumpstate/DumpstateUtil.h
+++ b/cmds/dumpstate/DumpstateUtil.h
@@ -97,9 +97,16 @@ class CommandOptions {
public:
/* Sets the command to always run, even on `dry-run` mode. */
CommandOptionsBuilder& Always();
- /* Sets the command's PrivilegeMode as `SU_ROOT` */
+ /*
+ * Sets the command's PrivilegeMode as `SU_ROOT` unless overridden by system property
+ * 'dumpstate.unroot'.
+ */
CommandOptionsBuilder& AsRoot();
- /* If !IsUserBuild(), sets the command's PrivilegeMode as `SU_ROOT` */
+ /*
+ * Runs AsRoot() on userdebug builds. No-op on user builds since 'su' is
+ * not available. This is used for commands that return some useful information even
+ * when run as shell.
+ */
CommandOptionsBuilder& AsRootIfAvailable();
/* Sets the command's PrivilegeMode as `DROP_ROOT` */
CommandOptionsBuilder& DropRoot();
@@ -162,9 +169,17 @@ class PropertiesHelper {
*/
static bool IsDryRun();
+ /**
+ * Checks whether root availability should be overridden.
+ *
+ * Useful to verify how dumpstate would work in a device with an user build.
+ */
+ static bool IsUnroot();
+
private:
static std::string build_type_;
static int dry_run_;
+ static int unroot_;
};
/*
diff --git a/cmds/dumpstate/README.md b/cmds/dumpstate/README.md
index 0302ea5f7d..6e07b3164b 100644
--- a/cmds/dumpstate/README.md
+++ b/cmds/dumpstate/README.md
@@ -52,6 +52,12 @@ mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest/dump
adb shell setprop dumpstate.dry_run true
```
+## To emulate a device with user build
+
+```
+adb shell setprop dumpstate.unroot true
+```
+
## To change the `dumpstate` version
```
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index b675c515fe..2cb980002a 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -85,6 +85,10 @@ class DumpstateBaseTest : public Test {
PropertiesHelper::build_type_ = build_type;
}
+ void SetUnroot(bool unroot) const {
+ PropertiesHelper::unroot_ = unroot;
+ }
+
bool IsStandalone() const {
return calls_ == 1;
}
@@ -650,6 +654,32 @@ TEST_F(DumpstateTest, RunCommandAsRootNonUserBuild) {
EXPECT_THAT(err, StrEq("stderr\n"));
}
+TEST_F(DumpstateTest, RunCommandAsRootNonUserBuild_withUnroot) {
+ if (!IsStandalone()) {
+ // TODO: temporarily disabled because it might cause other tests to fail after dropping
+ // to Shell - need to refactor tests to avoid this problem)
+ MYLOGE(
+ "Skipping DumpstateTest.RunCommandAsRootNonUserBuild_withUnroot() "
+ "on test suite\n")
+ return;
+ }
+ if (PropertiesHelper::IsUserBuild()) {
+ ALOGI("Skipping RunCommandAsRootNonUserBuild_withUnroot on user builds\n");
+ return;
+ }
+
+ // Same test as above, but with unroot property set, which will override su availability.
+ SetUnroot(true);
+ DropRoot();
+
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+ CommandOptions::WithTimeout(1).AsRoot().Build()));
+
+ // AsRoot is ineffective.
+ EXPECT_THAT(out, StrEq("2000\nstdout\n"));
+ EXPECT_THAT(err, StrEq("drop_root_user(): already running as Shell\nstderr\n"));
+}
+
TEST_F(DumpstateTest, RunCommandAsRootIfAvailableOnUserBuild) {
if (!IsStandalone()) {
// TODO: temporarily disabled because it might cause other tests to fail after dropping
@@ -692,6 +722,32 @@ TEST_F(DumpstateTest, RunCommandAsRootIfAvailableOnDebugBuild) {
EXPECT_THAT(err, StrEq("stderr\n"));
}
+TEST_F(DumpstateTest, RunCommandAsRootIfAvailableOnDebugBuild_withUnroot) {
+ if (!IsStandalone()) {
+ // TODO: temporarily disabled because it might cause other tests to fail after dropping
+ // to Shell - need to refactor tests to avoid this problem)
+ MYLOGE(
+ "Skipping DumpstateTest.RunCommandAsRootIfAvailableOnDebugBuild_withUnroot() "
+ "on test suite\n")
+ return;
+ }
+ if (PropertiesHelper::IsUserBuild()) {
+ ALOGI("Skipping RunCommandAsRootIfAvailableOnDebugBuild_withUnroot on user builds\n");
+ return;
+ }
+ // Same test as above, but with unroot property set, which will override su availability.
+ SetUnroot(true);
+
+ DropRoot();
+
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+ CommandOptions::WithTimeout(1).AsRootIfAvailable().Build()));
+
+ // It's a userdebug build, so "su root" should be available, but unroot=true overrides it.
+ EXPECT_THAT(out, StrEq("2000\nstdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
TEST_F(DumpstateTest, DumpFileNotFoundNoTitle) {
EXPECT_EQ(-1, DumpFile("", "/I/cant/believe/I/exist"));
EXPECT_THAT(out,