From e6d37cdbf98247f2d9dfd574644fa342de8655bc Mon Sep 17 00:00:00 2001 From: Tom Cherry Date: Thu, 19 Oct 2017 14:15:37 -0700 Subject: [PATCH] init: clean up subcontext_test subcontext_test had been failing due to setexeccon() failing to transition to vendor_init context. This is a good thing as nothing other than init should be able to transition into this context. I don't want to add code to skip the setexeccon() call only for the tests, so I instead call setexeccon() with the return value of getcon(). This works however only for root, so these tests are skipped for non-root. Test: init unit tests Change-Id: I8a415599e0ec5506511202f7f5018c0e5265837d --- init/subcontext.h | 17 +--- init/subcontext_benchmark.cpp | 20 ++++- init/subcontext_test.cpp | 147 +++++++++++++++++++--------------- 3 files changed, 103 insertions(+), 81 deletions(-) diff --git a/init/subcontext.h b/init/subcontext.h index e920034bc..eadabee8d 100644 --- a/init/subcontext.h +++ b/init/subcontext.h @@ -35,7 +35,7 @@ extern const std::string kVendorContext; class Subcontext { public: Subcontext(std::string path_prefix, std::string context) - : path_prefix_(std::move(path_prefix)), context_(std::move(context)) { + : path_prefix_(std::move(path_prefix)), context_(std::move(context)), pid_(0) { Fork(); } @@ -55,21 +55,6 @@ class Subcontext { android::base::unique_fd socket_; }; -// For testing, to kill the subcontext after the test has completed. -class SubcontextKiller { - public: - SubcontextKiller(const Subcontext& subcontext) : subcontext_(subcontext) {} - ~SubcontextKiller() { - if (subcontext_.pid() > 0) { - kill(subcontext_.pid(), SIGTERM); - kill(subcontext_.pid(), SIGKILL); - } - } - - private: - const Subcontext& subcontext_; -}; - int SubcontextMain(int argc, char** argv, const KeywordFunctionMap* function_map); std::vector* InitializeSubcontexts(); bool SubcontextChildReap(pid_t pid); diff --git a/init/subcontext_benchmark.cpp b/init/subcontext_benchmark.cpp index a62b9592e..630799378 100644 --- a/init/subcontext_benchmark.cpp +++ b/init/subcontext_benchmark.cpp @@ -17,6 +17,7 @@ #include "subcontext.h" #include +#include #include "test_function_map.h" @@ -24,12 +25,27 @@ namespace android { namespace init { static void BenchmarkSuccess(benchmark::State& state) { - auto subcontext = Subcontext("path", kVendorContext); - auto subcontext_killer = SubcontextKiller(subcontext); + if (getuid() != 0) { + state.SkipWithError("Skipping benchmark, must be run as root."); + return; + } + char* context; + if (getcon(&context) != 0) { + state.SkipWithError("getcon() failed"); + return; + } + + auto subcontext = Subcontext("path", context); + free(context); while (state.KeepRunning()) { subcontext.Execute(std::vector{"return_success"}); } + + if (subcontext.pid() > 0) { + kill(subcontext.pid(), SIGTERM); + kill(subcontext.pid(), SIGKILL); + } } BENCHMARK(BenchmarkSuccess); diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp index 60b45b9f7..ca45266cc 100644 --- a/init/subcontext_test.cpp +++ b/init/subcontext_test.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "builtin_arguments.h" #include "test_function_map.h" @@ -38,88 +39,108 @@ using android::base::WaitForProperty; namespace android { namespace init { +// I would use test fixtures, but I cannot skip the test if not root with them, so instead we have +// this test runner. +template +void RunTest(F&& test_function) { + if (getuid() != 0) { + GTEST_LOG_(INFO) << "Skipping test, must be run as root."; + return; + } + + char* context; + ASSERT_EQ(0, getcon(&context)); + auto context_string = std::string(context); + free(context); + + auto subcontext = Subcontext("dummy_path", context_string); + ASSERT_NE(0, subcontext.pid()); + + test_function(subcontext, context_string); + + if (subcontext.pid() > 0) { + kill(subcontext.pid(), SIGTERM); + kill(subcontext.pid(), SIGKILL); + } +} + TEST(subcontext, CheckDifferentPid) { - auto subcontext = Subcontext("path", kVendorContext); - auto subcontext_killer = SubcontextKiller(subcontext); + RunTest([](auto& subcontext, auto& context_string) { + auto result = subcontext.Execute(std::vector{"return_pids_as_error"}); + ASSERT_FALSE(result); - auto result = subcontext.Execute(std::vector{"return_pids_as_error"}); - ASSERT_FALSE(result); - - auto pids = Split(result.error_string(), " "); - ASSERT_EQ(2U, pids.size()); - auto our_pid = std::to_string(getpid()); - EXPECT_NE(our_pid, pids[0]); - EXPECT_EQ(our_pid, pids[1]); + auto pids = Split(result.error_string(), " "); + ASSERT_EQ(2U, pids.size()); + auto our_pid = std::to_string(getpid()); + EXPECT_NE(our_pid, pids[0]); + EXPECT_EQ(our_pid, pids[1]); + }); } TEST(subcontext, SetProp) { - auto subcontext = Subcontext("path", kVendorContext); - auto subcontext_killer = SubcontextKiller(subcontext); + RunTest([](auto& subcontext, auto& context_string) { + SetProperty("init.test.subcontext", "fail"); + WaitForProperty("init.test.subcontext", "fail"); - SetProperty("init.test.subcontext", "fail"); - WaitForProperty("init.test.subcontext", "fail"); - - auto args = std::vector{ - "setprop", - "init.test.subcontext", - "success", - }; - auto result = subcontext.Execute(args); - ASSERT_TRUE(result) << result.error(); - - EXPECT_TRUE(WaitForProperty("init.test.subcontext", "success", 10s)); -} - -TEST(subcontext, MultipleCommands) { - auto subcontext = Subcontext("path", kVendorContext); - auto subcontext_killer = SubcontextKiller(subcontext); - - auto first_pid = subcontext.pid(); - - auto expected_words = std::vector{ - "this", - "is", - "a", - "test", - }; - - for (const auto& word : expected_words) { auto args = std::vector{ - "add_word", - word, + "setprop", + "init.test.subcontext", + "success", }; auto result = subcontext.Execute(args); ASSERT_TRUE(result) << result.error(); - } - auto result = subcontext.Execute(std::vector{"return_words_as_error"}); - ASSERT_FALSE(result); - EXPECT_EQ(Join(expected_words, " "), result.error_string()); - EXPECT_EQ(first_pid, subcontext.pid()); + EXPECT_TRUE(WaitForProperty("init.test.subcontext", "success", 10s)); + }); +} + +TEST(subcontext, MultipleCommands) { + RunTest([](auto& subcontext, auto& context_string) { + auto first_pid = subcontext.pid(); + + auto expected_words = std::vector{ + "this", + "is", + "a", + "test", + }; + + for (const auto& word : expected_words) { + auto args = std::vector{ + "add_word", + word, + }; + auto result = subcontext.Execute(args); + ASSERT_TRUE(result) << result.error(); + } + + auto result = subcontext.Execute(std::vector{"return_words_as_error"}); + ASSERT_FALSE(result); + EXPECT_EQ(Join(expected_words, " "), result.error_string()); + EXPECT_EQ(first_pid, subcontext.pid()); + }); } TEST(subcontext, RecoverAfterAbort) { - auto subcontext = Subcontext("path", kVendorContext); - auto subcontext_killer = SubcontextKiller(subcontext); + RunTest([](auto& subcontext, auto& context_string) { + auto first_pid = subcontext.pid(); - auto first_pid = subcontext.pid(); + auto result = subcontext.Execute(std::vector{"cause_log_fatal"}); + ASSERT_FALSE(result); - auto result = subcontext.Execute(std::vector{"cause_log_fatal"}); - ASSERT_FALSE(result); - - auto result2 = subcontext.Execute(std::vector{"generate_sane_error"}); - ASSERT_FALSE(result2); - EXPECT_EQ("Sane error!", result2.error_string()); - EXPECT_NE(subcontext.pid(), first_pid); + auto result2 = subcontext.Execute(std::vector{"generate_sane_error"}); + ASSERT_FALSE(result2); + EXPECT_EQ("Sane error!", result2.error_string()); + EXPECT_NE(subcontext.pid(), first_pid); + }); } TEST(subcontext, ContextString) { - auto subcontext = Subcontext("path", kVendorContext); - auto subcontext_killer = SubcontextKiller(subcontext); - - auto result = subcontext.Execute(std::vector{"return_context_as_error"}); - ASSERT_FALSE(result); - ASSERT_EQ(kVendorContext, result.error_string()); + RunTest([](auto& subcontext, auto& context_string) { + auto result = subcontext.Execute(std::vector{"return_context_as_error"}); + ASSERT_FALSE(result); + ASSERT_EQ(context_string, result.error_string()); + }); } TestFunctionMap BuildTestFunctionMap() {