From 57ef66b6fa0c0b881f1fe35ad7794da9c16de9fe Mon Sep 17 00:00:00 2001 From: Tom Cherry Date: Wed, 19 Jul 2017 14:51:54 -0700 Subject: [PATCH] ueventd: add test to ensure selabel_lookup() is thread safe selabel_lookup() must be threadsafe, but had failed in the past. Bug: 63861738 Test: this newly added test Change-Id: I78bdb8e555433e8217ac6d4be112ba91de9f03bb --- init/ueventd_test.cpp | 82 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/init/ueventd_test.cpp b/init/ueventd_test.cpp index 4d9a1fa59..729005149 100644 --- a/init/ueventd_test.cpp +++ b/init/ueventd_test.cpp @@ -19,6 +19,8 @@ #include #include +#include +#include #include #include #include @@ -27,8 +29,11 @@ #include #include #include +#include +#include #include +using namespace std::chrono_literals; using namespace std::string_literals; template @@ -120,3 +125,80 @@ TEST(ueventd, setfscreatecon_IsPerThread) { freecon(file_context); } } + +TEST(ueventd, selabel_lookup_MultiThreaded) { + if (getuid() != 0) { + GTEST_LOG_(INFO) << "Skipping test, must be run as root."; + return; + } + + // Test parameters + constexpr auto num_threads = 10; + constexpr auto run_time = 200ms; + + std::unique_ptr sehandle( + selinux_android_file_context_handle(), &selabel_close); + + ASSERT_TRUE(sehandle); + + struct { + const char* file; + int mode; + std::string expected_context; + } files_and_modes[] = { + {"/dev/zero", 020666, ""}, + {"/dev/null", 020666, ""}, + {"/dev/random", 020666, ""}, + {"/dev/urandom", 020666, ""}, + }; + + // Precondition, ensure that we can lookup all of these from a single thread, and store the + // expected context for each. + for (size_t i = 0; i < arraysize(files_and_modes); ++i) { + char* secontext; + ASSERT_EQ(0, selabel_lookup(sehandle.get(), &secontext, files_and_modes[i].file, + files_and_modes[i].mode)); + files_and_modes[i].expected_context = secontext; + freecon(secontext); + } + + // Now that we know we can access them, and what their context should be, run in parallel. + std::atomic_bool stopped = false; + std::atomic_uint num_api_failures = 0; + std::atomic_uint num_context_check_failures = 0; + std::atomic_uint num_successes = 0; + + auto thread_function = [&]() { + while (!stopped) { + for (size_t i = 0; i < arraysize(files_and_modes); ++i) { + char* secontext; + int result = selabel_lookup(sehandle.get(), &secontext, files_and_modes[i].file, + files_and_modes[i].mode); + if (result != 0) { + num_api_failures++; + } else { + if (files_and_modes[i].expected_context != secontext) { + num_context_check_failures++; + } else { + num_successes++; + } + freecon(secontext); + } + } + } + }; + + std::vector threads; + std::generate_n(back_inserter(threads), num_threads, + [&]() { return std::thread(thread_function); }); + + std::this_thread::sleep_for(run_time); + stopped = true; + for (auto& thread : threads) { + thread.join(); + } + + EXPECT_EQ(0U, num_api_failures); + EXPECT_EQ(0U, num_context_check_failures); + EXPECT_GT(num_successes, 0U); +}