From c0f0863ca4d60fc66fa74f5bfc6b67b127528399 Mon Sep 17 00:00:00 2001 From: FritzFlorian Date: Wed, 25 Mar 2020 10:57:17 +0100 Subject: [PATCH] Add test case for mmaped stacks. Make sure that the guard page leads to sigsegv as expected. --- lib/pls/include/pls/internal/base/stack_allocator.h | 1 + test/CMakeLists.txt | 3 ++- test/base_tests.cpp | 42 ++++++++++++++++++++++++++++++++++++------ test/test_helpers.h | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 test/test_helpers.h diff --git a/lib/pls/include/pls/internal/base/stack_allocator.h b/lib/pls/include/pls/internal/base/stack_allocator.h index 918c707..ec99bf5 100644 --- a/lib/pls/include/pls/internal/base/stack_allocator.h +++ b/lib/pls/include/pls/internal/base/stack_allocator.h @@ -18,6 +18,7 @@ class heap_stack_allocator : public stack_allocator { }; class mmap_stack_allocator : public stack_allocator { + public: char *allocate_stack(size_t size) override; void free_stack(size_t, char *stack) override; }; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a648e7d..ff36afe 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,5 +2,6 @@ add_executable(tests main.cpp data_structures_test.cpp base_tests.cpp - scheduling_tests.cpp) + scheduling_tests.cpp + test_helpers.h) target_link_libraries(tests catch2 pls) diff --git a/test/base_tests.cpp b/test/base_tests.cpp index 03a5d33..99feac7 100644 --- a/test/base_tests.cpp +++ b/test/base_tests.cpp @@ -2,26 +2,53 @@ #include "pls/internal/base/spin_lock.h" #include "pls/internal/base/system_details.h" +#include "pls/internal/base/stack_allocator.h" + +#include "test_helpers.h" -#include #include +#include #include using namespace pls::internal::base; -static bool base_tests_visited; -static int base_tests_local_value_one; -static std::vector base_tests_local_value_two; - int base_tests_shared_counter; +TEST_CASE("mmap stack allocator", "[internal/base/stack_allocator.h") { + mmap_stack_allocator stack_allocator; + + char *stack = stack_allocator.allocate_stack(4096); + + SECTION("stack passes in valid range") { + stack[0] = 'a'; + stack[4096 - 1] = 'a'; + + REQUIRE(true); + } + + SECTION("guard page sigsev on overflow") { + REQUIRE(CHECK_ABORT([&]() { stack[-10] = 'a'; })); + } + + stack_allocator.free_stack(4096, stack); + + SECTION("stack unmaps after free") { + REQUIRE(CHECK_ABORT([&]() { stack[0] = 'a'; })); + } +} + TEST_CASE("spinlock protects concurrent counter", "[internal/data_structures/spinlock.h]") { - constexpr int num_iterations = 1000000; + constexpr int num_iterations = 10000; base_tests_shared_counter = 0; + std::atomic barrier{2}; + spin_lock lock{}; SECTION("lock can be used by itself") { std::thread t1{[&]() { + barrier--; + while (barrier != 0); + for (int i = 0; i < num_iterations; i++) { lock.lock(); base_tests_shared_counter++; @@ -29,6 +56,9 @@ TEST_CASE("spinlock protects concurrent counter", "[internal/data_structures/spi } }}; std::thread t2{[&]() { + barrier--; + while(barrier != 0); + for (int i = 0; i < num_iterations; i++) { lock.lock(); base_tests_shared_counter--; diff --git a/test/test_helpers.h b/test/test_helpers.h new file mode 100644 index 0000000..95d3f36 --- /dev/null +++ b/test/test_helpers.h @@ -0,0 +1,52 @@ +#ifndef PLS_TEST_TEST_HELPERS_H_ +#define PLS_TEST_TEST_HELPERS_H_ + +#include +#include +#include + +#include +#include + +// Source: https://stackoverflow.com/questions/15749071/fork-and-wait-in-c +// Works well enough for our purpose. +void abort_handler(int) { + std::exit(1); +} + +template +bool CHECK_ABORT(F &&f) { + //spawn a new process + auto child_pid = fork(); + + //if the fork succeed + if (child_pid >= 0) { + + //if we are in the child process + if (child_pid == 0) { + // Re-direct the signal handlers added by catch to simply exit with an error code + std::signal(SIGABRT, abort_handler); + std::signal(SIGFPE, abort_handler); + std::signal(SIGSEGV, abort_handler); + std::signal(SIGILL, abort_handler); + + //call the lambda that we expect to abort + f(); + + //if the function didn't abort, we'll exit cleanly + std::exit(EXIT_SUCCESS); + } + } + + //determine if the child process aborted + int exit_status; + wait(&exit_status); + + //we check the exit status instead of a signal interrupt, because + //Catch is going to catch the signal and exit with an error + bool aborted = exit_status != 0; + + return aborted; +} + +#endif //PLS_TEST_TEST_HELPERS_H_ -- libgit2 0.26.0