mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 11:00:19 +02:00
FEXLinuxTests: Migrate signal tests to Catch2
This commit is contained in:
1 parent
16be2792ab
commit
41aaeb1ff0
15 files changed
+305
-271
No files matched your search
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
Reference in new issue
Block a user