FEXLinuxTests: Migrate signal tests to Catch2

This commit is contained in:
Tony Wasserka committed 2022-09-22 10:03:37 +02:00
1 parent 16be2792ab
commit 41aaeb1ff0
15 files changed
+305 -271

No files matched your search

-10
View File
@@ -5,13 +5,3 @@
# These sometimes crash FEX with SIGSEGV
timer-sigev-thread.32
timer-sigev-thread.64
# These fail on arm because of sigbus handling
synchronous-signal-block-sbus.32
synchronous-signal-block-sbus.64
synchronous-signal-block-abus.32
synchronous-signal-block-abus.64
# These fail on arm because we don't raise FPE
synchronous-signal-block-sfpe.32
synchronous-signal-block-sfpe.64
-8
View File
@@ -1,8 +0,0 @@
synchronous-signal-block-ssegv.32 -11
synchronous-signal-block-ssegv.64 -11
synchronous-signal-block-sill.32 -4
synchronous-signal-block-sill.64 -4
synchronous-signal-block-sbus.32 -7
synchronous-signal-block-sbus.64 -7
synchronous-signal-block-sfpe.32 -8
synchronous-signal-block-sfpe.64 -8
+3 -21
View File
@@ -6,15 +6,9 @@
timer-sigev-thread.32
timer-sigev-thread.64
# These fail on arm because of sigbus handling
synchronous-signal-block-sbus.32
synchronous-signal-block-sbus.64
synchronous-signal-block-abus.32
synchronous-signal-block-abus.64
# These fail on arm because we don't raise FPE
synchronous-signal-block-sfpe.32
synchronous-signal-block-sfpe.64
# These trigger various quirks in FEX's signal handling
synchronous-signal-block.32
synchronous-signal-block.64
###
### Failing Tests ###
@@ -25,15 +19,3 @@ sigtest_samask.32
sigtest_samask.64
sigtest_sigmask.32
sigtest_sigmask.64
# These fail to do default signal catching behaviour
synchronous-signal-block-ssegv.32
synchronous-signal-block-ssegv.64
synchronous-signal-block-sill.32
synchronous-signal-block-sill.64
# These fail to queue the signals
synchronous-signal-block-asegv.32
synchronous-signal-block-asegv.64
synchronous-signal-block-aill.32
synchronous-signal-block-aill.64
@@ -1,3 +1,7 @@
#include "invalid_util.h"
#include <catch2/catch.hpp>
#include <atomic>
#include <signal.h>
#include <sys/mman.h>
@@ -9,7 +13,7 @@ __attribute__((naked, nocf_check))
static void InvalidINT() {
__asm volatile(R"(
hlt;
ret; # Just incase it gets past the int
ret;
)");
}
@@ -19,31 +23,25 @@ constexpr int EXPECTED_ERR = 0;
constexpr int EXPECTED_SI_CODE = 128;
constexpr int EXPECTED_SIGNAL = SIGSEGV;
static void handler(int signal, siginfo_t *siginfo, void* context) {
ucontext_t* _context = (ucontext_t*)context;
#ifndef REG_RIP
#define REG_RIP REG_EIP
#endif
if (_context->uc_mcontext.gregs[REG_RIP] == EXPECTED_RIP &&
_context->uc_mcontext.gregs[REG_TRAPNO] == EXPECTED_TRAPNO &&
_context->uc_mcontext.gregs[REG_ERR] == EXPECTED_ERR &&
siginfo->si_code == EXPECTED_SI_CODE &&
signal == EXPECTED_SIGNAL) {
exit(0);
}
else {
exit(1);
}
}
int main() {
TEST_CASE("Signals: Invalid HLT") {
capturing_handler_skip = 1;
struct sigaction act{};
act.sa_sigaction = handler;
act.sa_sigaction = CapturingHandler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &act, nullptr);
sigaction(SIGTRAP, &act, nullptr);
sigaction(SIGILL, &act, nullptr);
InvalidINT();
return 1;
#ifndef REG_RIP
#define REG_RIP REG_EIP
#endif
REQUIRE(from_handler.has_value());
CHECK(from_handler->mctx.gregs[REG_RIP] == EXPECTED_RIP);
CHECK(from_handler->mctx.gregs[REG_TRAPNO] == EXPECTED_TRAPNO);
CHECK(from_handler->mctx.gregs[REG_ERR] == EXPECTED_ERR);
CHECK(from_handler->si_code == EXPECTED_SI_CODE);
CHECK(from_handler->signal == EXPECTED_SIGNAL);
}
@@ -1,3 +1,7 @@
#include "invalid_util.h"
#include <catch2/catch.hpp>
#include <atomic>
#include <signal.h>
#include <sys/mman.h>
@@ -9,7 +13,7 @@ __attribute__((naked, nocf_check))
static void InvalidINT() {
__asm volatile(R"(
int $0x2d;
ret; # Just incase it gets past the int
ret;
)");
}
@@ -19,33 +23,25 @@ constexpr int EXPECTED_ERR = 362;
constexpr int EXPECTED_SI_CODE = 128;
constexpr int EXPECTED_SIGNAL = SIGSEGV;
static void handler(int signal, siginfo_t *siginfo, void* context) {
ucontext_t* _context = (ucontext_t*)context;
#ifndef REG_RIP
#define REG_RIP REG_EIP
#endif
if (_context->uc_mcontext.gregs[REG_RIP] == EXPECTED_RIP &&
_context->uc_mcontext.gregs[REG_TRAPNO] == EXPECTED_TRAPNO &&
_context->uc_mcontext.gregs[REG_ERR] == EXPECTED_ERR &&
siginfo->si_code == EXPECTED_SI_CODE &&
signal == EXPECTED_SIGNAL) {
exit(0);
}
else {
exit(1);
}
}
int main() {
TEST_CASE("Signals: Invalid INT") {
capturing_handler_skip = 2;
struct sigaction act{};
act.sa_sigaction = handler;
act.sa_sigaction = CapturingHandler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &act, nullptr);
sigaction(SIGTRAP, &act, nullptr);
sigaction(SIGILL, &act, nullptr);
InvalidINT();
return 1;
#ifndef REG_RIP
#define REG_RIP REG_EIP
#endif
REQUIRE(from_handler.has_value());
CHECK(from_handler->mctx.gregs[REG_RIP] == EXPECTED_RIP);
CHECK(from_handler->mctx.gregs[REG_TRAPNO] == EXPECTED_TRAPNO);
CHECK(from_handler->mctx.gregs[REG_ERR] == EXPECTED_ERR);
CHECK(from_handler->si_code == EXPECTED_SI_CODE);
CHECK(from_handler->signal == EXPECTED_SIGNAL);
}
@@ -1,3 +1,7 @@
#include "invalid_util.h"
#include <catch2/catch.hpp>
#include <atomic>
#include <signal.h>
#include <sys/mman.h>
@@ -9,7 +13,7 @@ __attribute__((naked, nocf_check))
static void InvalidINT() {
__asm volatile(R"(
.byte 0xF1; # int1
ret; # Just incase it gets past the int
ret;
)");
}
@@ -19,31 +23,25 @@ constexpr int EXPECTED_ERR = 0;
constexpr int EXPECTED_SI_CODE = 1;
constexpr int EXPECTED_SIGNAL = SIGTRAP;
static void handler(int signal, siginfo_t *siginfo, void* context) {
ucontext_t* _context = (ucontext_t*)context;
#ifndef REG_RIP
#define REG_RIP REG_EIP
#endif
if (_context->uc_mcontext.gregs[REG_RIP] == EXPECTED_RIP &&
_context->uc_mcontext.gregs[REG_TRAPNO] == EXPECTED_TRAPNO &&
_context->uc_mcontext.gregs[REG_ERR] == EXPECTED_ERR &&
siginfo->si_code == EXPECTED_SI_CODE &&
signal == EXPECTED_SIGNAL) {
exit(0);
}
else {
exit(1);
}
}
int main() {
TEST_CASE("Signals: Invalid INT1") {
capturing_handler_skip = 0;
struct sigaction act{};
act.sa_sigaction = handler;
act.sa_sigaction = CapturingHandler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &act, nullptr);
sigaction(SIGTRAP, &act, nullptr);
sigaction(SIGILL, &act, nullptr);
InvalidINT();
return 1;
#ifndef REG_RIP
#define REG_RIP REG_EIP
#endif
REQUIRE(from_handler.has_value());
CHECK(from_handler->mctx.gregs[REG_RIP] == EXPECTED_RIP);
CHECK(from_handler->mctx.gregs[REG_TRAPNO] == EXPECTED_TRAPNO);
CHECK(from_handler->mctx.gregs[REG_ERR] == EXPECTED_ERR);
CHECK(from_handler->si_code == EXPECTED_SI_CODE);
CHECK(from_handler->signal == EXPECTED_SIGNAL);
}
@@ -1,3 +1,7 @@
#include "invalid_util.h"
#include <catch2/catch.hpp>
#include <atomic>
#include <signal.h>
#include <sys/mman.h>
@@ -9,7 +13,7 @@ __attribute__((naked, nocf_check))
static void InvalidINT() {
__asm volatile(R"(
int3;
ret; # Just incase it gets past the int
ret;
)");
}
@@ -19,32 +23,25 @@ constexpr int EXPECTED_ERR = 0;
constexpr int EXPECTED_SI_CODE = 128;
constexpr int EXPECTED_SIGNAL = SIGTRAP;
static void handler(int signal, siginfo_t *siginfo, void* context) {
ucontext_t* _context = (ucontext_t*)context;
#ifndef REG_RIP
#define REG_RIP REG_EIP
#endif
if (_context->uc_mcontext.gregs[REG_RIP] == EXPECTED_RIP &&
_context->uc_mcontext.gregs[REG_TRAPNO] == EXPECTED_TRAPNO &&
_context->uc_mcontext.gregs[REG_ERR] == EXPECTED_ERR &&
siginfo->si_code == EXPECTED_SI_CODE &&
signal == EXPECTED_SIGNAL) {
exit(0);
}
else {
exit(1);
}
}
int main() {
TEST_CASE("Signals: Invalid INT3") {
capturing_handler_skip = 0;
struct sigaction act{};
act.sa_sigaction = handler;
act.sa_sigaction = CapturingHandler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &act, nullptr);
sigaction(SIGTRAP, &act, nullptr);
sigaction(SIGILL, &act, nullptr);
InvalidINT();
return 1;
#ifndef REG_RIP
#define REG_RIP REG_EIP
#endif
REQUIRE(from_handler.has_value());
CHECK(from_handler->mctx.gregs[REG_RIP] == EXPECTED_RIP);
CHECK(from_handler->mctx.gregs[REG_TRAPNO] == EXPECTED_TRAPNO);
CHECK(from_handler->mctx.gregs[REG_ERR] == EXPECTED_ERR);
CHECK(from_handler->si_code == EXPECTED_SI_CODE);
CHECK(from_handler->signal == EXPECTED_SIGNAL);
}
@@ -1,3 +1,7 @@
#include "invalid_util.h"
#include <catch2/catch.hpp>
#include <atomic>
#include <signal.h>
#include <sys/mman.h>
@@ -9,7 +13,7 @@ __attribute__((naked, nocf_check))
static void InvalidINT() {
__asm volatile(R"(
ud2;
ret; # Just incase it gets past the int
ret;
)");
}
@@ -19,31 +23,25 @@ constexpr int EXPECTED_ERR = 0;
constexpr int EXPECTED_SI_CODE = 2;
constexpr int EXPECTED_SIGNAL = SIGILL;
static void handler(int signal, siginfo_t *siginfo, void* context) {
ucontext_t* _context = (ucontext_t*)context;
#ifndef REG_RIP
#define REG_RIP REG_EIP
#endif
if (_context->uc_mcontext.gregs[REG_RIP] == EXPECTED_RIP &&
_context->uc_mcontext.gregs[REG_TRAPNO] == EXPECTED_TRAPNO &&
_context->uc_mcontext.gregs[REG_ERR] == EXPECTED_ERR &&
siginfo->si_code == EXPECTED_SI_CODE &&
signal == EXPECTED_SIGNAL) {
exit(0);
}
else {
exit(1);
}
}
int main() {
TEST_CASE("Signals: Invalid UD2") {
capturing_handler_skip = 2;
struct sigaction act{};
act.sa_sigaction = handler;
act.sa_sigaction = CapturingHandler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &act, nullptr);
sigaction(SIGTRAP, &act, nullptr);
sigaction(SIGILL, &act, nullptr);
InvalidINT();
return 1;
#ifndef REG_RIP
#define REG_RIP REG_EIP
#endif
REQUIRE(from_handler.has_value());
CHECK(from_handler->mctx.gregs[REG_RIP] == EXPECTED_RIP);
CHECK(from_handler->mctx.gregs[REG_TRAPNO] == EXPECTED_TRAPNO);
CHECK(from_handler->mctx.gregs[REG_ERR] == EXPECTED_ERR);
CHECK(from_handler->si_code == EXPECTED_SI_CODE);
CHECK(from_handler->signal == EXPECTED_SIGNAL);
}
@@ -0,0 +1,26 @@
#include <signal.h>
#include <optional>
struct CapturedHandlerState {
mcontext_t mctx;
int signal;
int si_code;
};
std::optional<CapturedHandlerState> from_handler;
// Number of bytes to skip to resume from the signal handler
int capturing_handler_skip = 0;
// Signal handler that writes its context data to the global from_handler
static void CapturingHandler(int signal, siginfo_t *siginfo, void* context) {
ucontext_t* _context = (ucontext_t*)context;
from_handler = { _context->uc_mcontext, signal, siginfo->si_code };
#ifdef REG_RIP
#define FEX_IP_REG REG_RIP
#else
#define FEX_IP_REG REG_EIP
#endif
_context->uc_mcontext.gregs[FEX_IP_REG] += capturing_handler_skip;
#undef FEX_IP_REG
}
@@ -1,3 +1,5 @@
#include <catch2/catch.hpp>
#include <atomic>
#include <errno.h>
#include <pthread.h>
@@ -59,36 +61,26 @@ static void *thread_func(void *ignored_argument) {
return NULL;
}
int main(void) {
TEST_CASE("pthreads cancel") {
pthread_t thr;
void *res;
int s;
/* Start a thread and then send it a cancellation request. */
s = pthread_create(&thr, NULL, &thread_func, NULL);
if (s != 0)
handle_error_en(s, "pthread_create");
REQUIRE(pthread_create(&thr, NULL, &thread_func, NULL) == 0);
while (!thread_ready.load())
;
printf("main(): sending cancellation request\n");
s = pthread_cancel(thr);
if (s != 0)
handle_error_en(s, "pthread_cancel");
REQUIRE(pthread_cancel(thr) == 0);
cancel_sent = true;
/* Join with thread to see what its exit status was. */
s = pthread_join(thr, &res);
if (s != 0)
handle_error_en(s, "pthread_join");
REQUIRE(pthread_join(thr, &res) == 0);
if (res == PTHREAD_CANCELED)
printf("main(): thread was canceled\n");
else
printf("main(): thread wasn't canceled (shouldn't happen!)\n");
exit(EXIT_SUCCESS);
CHECK(res == PTHREAD_CANCELED);
}
@@ -1,9 +1,10 @@
#include <catch2/catch.hpp>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
volatile bool loop = false;
volatile int count = 0;
volatile int count2 = 0;
@@ -11,8 +12,6 @@ volatile int count2 = 0;
#define SIGN SIGTSTP
void sig_handler(int signum, siginfo_t *info, void *context) {
loop = false;
printf("Inside handler function\n");
if (count != 0) {
printf("SA_NODEFER bug\n");
@@ -26,35 +25,25 @@ void sig_handler(int signum, siginfo_t *info, void *context) {
sigprocmask(0, 0, &old);
sigprocmask(SIG_SETMASK, &old, 0);
}
if (count2 < NUMCOUNT) {
printf("Nested Raising %d, %d of %d times\n", signum, 1 + count, NUMCOUNT);
count2++;
raise(signum);
count++;
} else {
exit(0);
printf("Exiting\n");
// Return to caller
}
}
int main() {
TEST_CASE("Signals: No defer") {
struct sigaction act = {0};
act.sa_flags = SA_SIGINFO | SA_NODEFER;
act.sa_sigaction = &sig_handler;
if (sigaction(SIGN, &act, NULL) != 0) {
printf("sigaction failed\n");
return -3;
}
loop = true;
while (loop) {
printf("Inside main loop, raising signal\n");
raise(SIGN);
if (loop) {
printf("Error: Signal did not get raised\n");
return -3;
}
}
return -2;
REQUIRE(sigaction(SIGN, &act, NULL) == 0);
raise(SIGN);
CHECK(count == 10);
CHECK(count2 == 10);
}
@@ -1,3 +1,5 @@
#include <catch2/catch.hpp>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
@@ -27,7 +29,7 @@ void sig_handler(int signum) {
printf("Signal reentering bug\n");
exit(-1);
}
if (count < NUMCOUNT) {
printf("Nested Raising sig%d, %d of %d times\n", signum, 1 + count, NUMCOUNT);
count2++;
@@ -42,11 +44,8 @@ void sig_handler(int signum) {
}
}
int main() {
if (signal(SIGN, sig_handler) != 0) {
printf("Signal() failed\n");
return -2;
}
TEST_CASE("Signals: samask") {
REQUIRE(signal(SIGN, sig_handler) == 0);
// test if sigmask blocks during execution as expected
last = false;
@@ -54,10 +53,7 @@ int main() {
while (loop) {
printf("Inside main loop, raising signal\n");
raise(SIGN);
if (loop) {
printf("Error: Signal did not get raised\n");
return -4;
}
REQUIRE_FALSE(loop);
}
last = true;
loop = true;
@@ -70,11 +66,6 @@ int main() {
while (loop) {
printf("Inside last loop, raising signal\n");
raise(SIGN);
if (loop) {
printf("Error: Signal did not get raised\n");
return -3;
}
REQUIRE_FALSE(loop);
}
printf("All good, Exiting\n");
return 0;
}
@@ -1,3 +1,5 @@
#include <catch2/catch.hpp>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
@@ -23,26 +25,19 @@ void sig_handler(int signum, siginfo_t *info, void *context) {
sigfillset(&uctx->uc_sigmask);
}
int main() {
TEST_CASE("Signals: sigmask") {
struct sigaction act = {0};
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = &sig_handler;
if (sigaction(SIGN, &act, NULL) != 0) {
printf("sigaction() failed\n");
return -2;
}
REQUIRE(sigaction(SIGN, &act, NULL) == 0);
loop = true;
while (loop) {
printf("Inside main loop, raising signal\n");
raise(SIGN);
if (loop) {
printf("Error: Signal did not get raised\n");
return -3;
}
}
printf("Exiting\n");
return 0;
// Ensure the signal got indeed raised
REQUIRE_FALSE(loop);
}
}
@@ -1,73 +1,159 @@
auto args = "ssegv, asegv, sill, aill, sbus, abus, sfpe, afpe";
// Triggering synchronous POSIX signals while they're masked triggers a
// process exit. In contrast, if an asynchronous signals is triggered, the
// corresponding signal handler will be invoked once the signal is unmasked.
//
// To test synchronous signals, the test forks and triggers the signal in the
// child process. For asynchronous signals, a signal handler that sets a global
// variable is used.
#include <cstring>
#include <errno.h>
#include <malloc.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <cstdint>
#include <optional>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>
#define handle_error(msg) \
do { \
perror(msg); \
exit(EXIT_FAILURE); \
} while (0)
#include <catch2/catch.hpp>
char *buffer;
int flag = 0;
static jmp_buf jmpbuf;
static void handler(int sig, siginfo_t *si, void *unused) {
struct HandledSignal {
int signal;
uintptr_t addr;
};
static std::optional<HandledSignal> handled_signal;
static void handler(int sig, siginfo_t *si, void *context) {
printf("Got %d at address: 0x%lx\n", sig, (long)si->si_addr);
exit(1);
handled_signal = { sig, reinterpret_cast<uintptr_t>(si->si_addr) };
siglongjmp(jmpbuf, 1);
}
int main(int argc, char *argv[]) {
if (argc == 1) {
printf("please specify one of %s\n", args);
// Helper that masks all signals and unmasks them on destruction
struct GuardedSignalMask {
sigset_t oldset {};
GuardedSignalMask() {
sigset_t set;
sigfillset(&set);
sigprocmask(SIG_SETMASK, &set, &oldset);
}
struct sigaction sa;
~GuardedSignalMask() {
sigprocmask(SIG_SETMASK, &oldset, nullptr);
}
};
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = handler;
sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGBUS, &sa, NULL);
sigaction(SIGILL, &sa, NULL);
sigaction(SIGFPE, &sa, NULL);
// Checks if the given function causes the process to exit.
// The function is executed in a process fork.
template<typename F>
std::optional<int> CheckIfExitsFromSignal(F&& f) {
if (fork() == 0) {
GuardedSignalMask guard;
std::forward<F>(f)();
exit(1);
} else {
int status = 0;
wait(&status);
return status;
}
}
// Checks if the given function causes a signal handler to be invoked
template<typename F>
std::optional<HandledSignal> CheckIfSignalHandlerCalled(F&& f) {
handled_signal = {};
struct sigaction oldsa[4];
if (!sigsetjmp(jmpbuf, 1)) {
// Handle all signals by the test handler
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = handler;
sigaction(SIGSEGV, &sa, &oldsa[0]);
sigaction(SIGBUS, &sa, &oldsa[1]);
sigaction(SIGILL, &sa, &oldsa[2]);
sigaction(SIGFPE, &sa, &oldsa[3]);
// Mask signals and run given callback
GuardedSignalMask guard;
std::forward<F>(f)();
}
// Restore previous signal handlers
sigaction(SIGSEGV, &oldsa[0], nullptr);
sigaction(SIGBUS, &oldsa[1], nullptr);
sigaction(SIGILL, &oldsa[2], nullptr);
sigaction(SIGFPE, &oldsa[3], nullptr);
return handled_signal;
}
// For ssegv, we fail to do default signal catching behaviour
TEST_CASE("Signals: ssegv") {
auto status = CheckIfExitsFromSignal([]() { *(int*)0x32 = 0x64; });
REQUIRE(status.has_value());
CHECK(WIFSIGNALED(*status) == true);
CHECK(WTERMSIG(*status) == SIGSEGV);
}
// For sill, we fail to do default signal catching behaviour
TEST_CASE("Signals: sill") {
auto status = CheckIfExitsFromSignal([]() { asm volatile("ud2\n"); });
REQUIRE(status.has_value());
CHECK(WIFSIGNALED(*status) == true);
CHECK(WTERMSIG(*status) == SIGILL);
}
// sbus and abus fail on arm because of sigbus handling
TEST_CASE("Signals: sbus") {
auto map1 = mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
auto map2 = (char *)mremap(map1, 4096, 8192, MREMAP_MAYMOVE);
sigset_t set;
sigfillset(&set);
auto status = CheckIfExitsFromSignal([&]() { map2[4096] = 2; });
REQUIRE(status.has_value());
CHECK(WIFSIGNALED(*status) == true);
CHECK(WTERMSIG(*status) == SIGBUS);
}
sigprocmask(SIG_SETMASK, &set, nullptr);
if (strcmp(argv[1], "ssegv") == 0) {
*(int *)(0x32) = 0x64;
} else if (strcmp(argv[1], "sill") == 0) {
asm volatile("ud2\n");
} else if (strcmp(argv[1], "sbus") == 0) {
map2[4096] = 2;
} else if (strcmp(argv[1], "sfpe") == 0) {
// sfpe and afpe fail on arm because we don't raise FPE
TEST_CASE("Signals: sfpe") {
auto status = CheckIfExitsFromSignal([&]() {
volatile int a = 10;
volatile int b = 0;
volatile int c = a / b;
printf("result: %d\n", c);
} else if (strcmp(argv[1], "asegv") == 0) {
raise(SIGSEGV);
} else if (strcmp(argv[1], "aill") == 0) {
raise(SIGILL);
} else if (strcmp(argv[1], "abus") == 0) {
raise(SIGBUS);
} else if (strcmp(argv[1], "afpe") == 0) {
raise(SIGFPE);
} else {
printf("Invalid argument %s\n", argv[1]);
printf("please specify one of %s\n", args);
}
exit(0);
});
REQUIRE(status.has_value());
CHECK(WIFSIGNALED(*status) == true);
CHECK(WTERMSIG(*status) == SIGFPE);
}
// These fail to queue the signals
TEST_CASE("Signals: asynchronous") {
int tested_signal = GENERATE(SIGSEGV, SIGILL, SIGBUS, SIGFPE);
// Check that the signal handler is delayed until unmasking.
bool handled_asynchronously = false;
auto handled_signal = CheckIfSignalHandlerCalled([&]() {
GuardedSignalMask guard{};
raise(tested_signal);
// Verify the rest of this function is still executed
handled_asynchronously = true;
// Destructor of GuardedSignalMask will unmask signals now,
// after which the signal handler should run
});
REQUIRE(handled_signal.has_value());
CHECK(handled_signal->signal == tested_signal);
CHECK(handled_asynchronously);
}
@@ -1,25 +1,28 @@
// Simple test of timer_create + SIGEV_THREAD, glibc implements it via SIG32
#include <catch2/catch.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <cassert>
#include <optional>
#include <signal.h>
#include <time.h>
int test;
void timer_handler(union sigval sv) {
auto ok = sv.sival_ptr == &test;
printf("timer_handler called, ok = %d\n", ok);
std::optional<bool> sigval_ack;
exit(ok ? 0 : -1);
void timer_handler(union sigval sv) {
sigval_ack = sv.sival_ptr == &test;
printf("timer_handler called, ok = %d\n", *sigval_ack);
}
int main() {
// These sometimes crash FEX with SIGSEGV
TEST_CASE("timer_create and SIGEV_THREAD", "[!mayfail]") {
timer_t timer;
sigevent sige;
itimerspec spec;
@@ -39,9 +42,10 @@ int main() {
timer_settime(timer, 0, &spec, NULL);
for (;;)
sleep(1);
while (!sigval_ack) {
usleep(10);
}
assert(false && "should never get here");
return -2;
REQUIRE(sigval_ack.has_value());
CHECK(*sigval_ack == true);
}