Merge "ueventd: add test to ensure selabel_lookup() is thread safe" am: 2c3a2a8c5c am: 3df3ec34e4

am: 7fd1bc8fc3

Change-Id: Id053ade023074737b2096bb5061f2ad7798e40d6
This commit is contained in:
Tom Cherry 2017-08-08 23:27:17 +00:00 committed by android-build-merger
commit 5cd97dcd2f

View file

@ -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 @@ 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<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);
}