diff --git a/emper/Context.cpp b/emper/Context.cpp index a9ad011b711847dcd25eccfc3959ce473774d08f..4c696f5ab850dbbbb1520b4f083d5288172f5f14 100644 --- a/emper/Context.cpp +++ b/emper/Context.cpp @@ -30,6 +30,18 @@ auto Context::calcGuardPage(char* context) -> void* { } auto Context::calcGuardPage() -> void* { return calcGuardPage(context); } +auto Context::inGuardPage(void* mem) -> bool { + if constexpr (!emper::STACK_GUARD_PAGE) return false; + + const auto _guardPage = (uintptr_t)this->guardPage; + const auto _mem = (uintptr_t)mem; + return _mem >= _guardPage && _mem < _guardPage + PAGE_SIZE; +} +auto Context::inCurrentGuardPage(void* mem) -> bool { + if (!currentContext) return false; + return currentContext->inGuardPage(mem); +} + auto Context::calcBos(char* context) -> void* { auto contextBase = (uintptr_t)context; uintptr_t bosOffset = 0; diff --git a/emper/Context.hpp b/emper/Context.hpp index f0482de4edd84e3a9c67d461ab25b6cfc836f01d..b0f0cff97b8a6588c377e780e58ea6d9afbd1451 100644 --- a/emper/Context.hpp +++ b/emper/Context.hpp @@ -171,4 +171,7 @@ class EMPER_CONTEXT_ALIGNAS Context : Logger<LogSubsystem::C> { } inline static auto getCurrentContext() -> Context* { return currentContext; } + + static auto inCurrentGuardPage(void* mem) -> bool; + auto inGuardPage(void* mem) -> bool; }; diff --git a/emper/Debug.hpp b/emper/Debug.hpp index f7bb4f341b6fea9a4c15f6113913a7b9ad94673c..595be832f7e81f5c36153ffc36abb820aa17678c 100644 --- a/emper/Debug.hpp +++ b/emper/Debug.hpp @@ -57,16 +57,25 @@ // clang-format on +#define PUSH_DIAGNOSTIC _Pragma("GCC diagnostic push") + #define IGNORE_UNUSED_FUNCTION \ - _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wunused-function\"") + PUSH_DIAGNOSTIC _Pragma("GCC diagnostic ignored \"-Wunused-function\"") // test for GCC > 11.1.0 #if __GNUC__ > 11 || \ (__GNUC__ == 11 && (__GNUC_MINOR__ > 1 || (__GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ >= 0))) #define IGNORE_MAYBE_UNINITIALIZED \ - _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") + PUSH_DIAGNOSTIC _Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +#else +#define IGNORE_MAYBE_UNINITIALIZED PUSH_DIAGNOSTIC +#endif + +#if __clang__ +#define IGNORE_INFINITE_RECURSION \ + PUSH_DIAGNOSTIC _Pragma("GCC diagnostic ignored \"-Winfinite-recursion\"") #else -#define IGNORE_MAYBE_UNINITIALIZED _Pragma("GCC diagnostic push") +#define IGNORE_INFINITE_RECURSION PUSH_DIAGNOSTIC #endif #define POP_DIAGNOSTIC _Pragma("GCC diagnostic pop") diff --git a/tests/ContextOutOfBoundsTest.cpp b/tests/ContextOutOfBoundsTest.cpp index 9edd38ada715b1a5aa876f54a71e75c3938cf039..6faa67aa59171e447043921b2f5f699a1222ca0d 100644 --- a/tests/ContextOutOfBoundsTest.cpp +++ b/tests/ContextOutOfBoundsTest.cpp @@ -1,6 +1,57 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // Copyright © 2022 Florian Fischer +#include <bits/types/siginfo_t.h> +#include <bits/types/stack_t.h> +#include <unistd.h> + +#include <array> +#include <csignal> +#include <cstdlib> +#include <string> + +#include "Common.hpp" +#include "Context.hpp" +#include "Debug.hpp" +#include "emper-common.h" + +// Alternative stack to use for the signal handler. +// We need an alternative stack because the infinite recursion causes an +// stack overflow and thus the signal handler can not run on the already overflown +// stack. +static std::array<char, SIGSTKSZ> segvHandlerStack; +static stack_t ss = {segvHandlerStack.data(), 0, segvHandlerStack.size()}; + +const std::string handlerMsg = "Received SIGSEGV\n"; + +// Check if the segfault was triggered in the guard page of the current context. +// SIGSEGV is a thread-directed signal an thus linux delivers it to the thread +// causing the segmentation fault. See man signal(7). +void handler(ATTR_UNUSED int sig, siginfo_t *info, ATTR_UNUSED void *ucontext) { + write(STDERR_FILENO, handlerMsg.data(), handlerMsg.size()); + if (Context::inCurrentGuardPage(info->si_addr)) { + exit(EXIT_SUCCESS); + } + exit(EXIT_FAILURE); +} + // Infinite recursion should run out of stack space and hit the guard page -static void recur() { recur(); } +IGNORE_INFINITE_RECURSION static void recur() { recur(); } +POP_DIAGNOSTIC + +void emperTest() { + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_sigaction = &handler; + sa.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; + + if (sigaltstack(&ss, nullptr)) { + DIE_MSG_ERRNO("setting alternative signal stack failed"); + } + + if (sigaction(SIGSEGV, &sa, nullptr)) { + DIE_MSG_ERRNO("setting SIGSEGV handler failed"); + } -void emperTest() { recur(); } + recur(); +} diff --git a/tests/meson.build b/tests/meson.build index a589675e5ba32decfe0cdd6e11f6747adebae82e..dd9fc59e8854697fcec47f43055f72f411ab5fcc 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -132,7 +132,6 @@ tests = [ 'name': 'ContextOutOfBoundsTest', 'description': 'Simple test for our stack guard page', 'is_parallel': true, - 'should_fail': true, 'test_runner': 'guard-page', }, ]