Commit c0f0863c by FritzFlorian

Add test case for mmaped stacks.

Make sure that the guard page leads to sigsegv as expected.
parent 872c1a72
Pipeline #1422 failed with stages
in 28 seconds
......@@ -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;
};
......
......@@ -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)
......@@ -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 <vector>
#include <mutex>
#include <atomic>
#include <thread>
using namespace pls::internal::base;
static bool base_tests_visited;
static int base_tests_local_value_one;
static std::vector<int> 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<int> 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--;
......
#ifndef PLS_TEST_TEST_HELPERS_H_
#define PLS_TEST_TEST_HELPERS_H_
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <cstdlib>
#include <csignal>
// 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<class F>
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_
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment