Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2c3a2a8c authored by Tom Cherry's avatar Tom Cherry Committed by Gerrit Code Review
Browse files

Merge "ueventd: add test to ensure selabel_lookup() is thread safe"

parents 24c39f0b 57ef66b6
Loading
Loading
Loading
Loading
+82 −0
Original line number Diff line number Diff line
@@ -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);
}