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
diff --git a/init/ueventd_test.cpp b/init/ueventd_test.cpp
index 4d9a1fa..7290051 100644
--- a/init/ueventd_test.cpp
+++ b/init/ueventd_test.cpp
@@ -19,6 +19,8 @@
#include <sys/stat.h>
#include <unistd.h>
+#include <atomic>
+#include <chrono>
#include <string>
#include <thread>
#include <vector>
@@ -27,8 +29,11 @@
#include <android-base/scopeguard.h>
#include <android-base/test_utils.h>
#include <gtest/gtest.h>
+#include <selinux/android.h>
+#include <selinux/label.h>
#include <selinux/selinux.h>
+using namespace std::chrono_literals;
using namespace std::string_literals;
template <typename T, typename F>
@@ -120,3 +125,80 @@
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<selabel_handle, decltype(&selabel_close)> 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<std::thread> 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);
+}