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:
commit
5cd97dcd2f
1 changed files with 82 additions and 0 deletions
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue