#include <darwintest.h>
#include <sys/types.h>
#include <pthread.h>
#include <mach/mach_types.h>
#include <unistd.h>
#include <stdlib.h>
#include <dispatch/dispatch.h>
typedef enum { PTHREAD, WORKQUEUE } thread_type_t;
typedef enum {
NO_CORRUPTION,
SIG_CORRUPTION,
FULL_CORRUPTION,
} corrupt_type_t;
static void *
body(void *ctx)
{
corrupt_type_t corrupt_type = (corrupt_type_t)ctx;
pthread_t self = pthread_self();
T_LOG("Helper thread running: %d", corrupt_type);
switch (corrupt_type) {
case NO_CORRUPTION:
break;
case SIG_CORRUPTION:
memset(self, 0x41, 128);
break;
case FULL_CORRUPTION:
memset(self, 0x41, 4096);
break;
}
abort();
T_FAIL("Abort didn't?");
}
static void
abort_test(thread_type_t type, corrupt_type_t corrupt_type)
{
pid_t child = fork();
if (child == 0) {
T_LOG("Child running");
switch (type) {
case PTHREAD: {
pthread_t tid;
T_QUIET;
T_ASSERT_POSIX_ZERO(
pthread_create(&tid, NULL, body, (void *)corrupt_type), NULL);
break;
}
case WORKQUEUE: {
dispatch_async_f(dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
(void *)corrupt_type, &body);
break;
}
}
sleep(5);
T_FAIL("Child didn't abort");
exit(-1);
}
int status = 0;
pid_t pid = wait(&status);
T_QUIET;
T_ASSERT_EQ(pid, child, NULL);
T_QUIET;
T_EXPECT_FALSE(WIFEXITED(status), "WIFEXITED Status: %x", status);
T_QUIET;
T_EXPECT_TRUE(WIFSIGNALED(status), "WIFSIGNALED Status: %x", status);
T_QUIET;
T_EXPECT_FALSE(WIFSTOPPED(status), "WIFSTOPPED Status: %x", status);
int signal = WTERMSIG(status);
#if defined(__i386__) || defined(__x86_64__)
if (corrupt_type == FULL_CORRUPTION) {
if (signal == SIGBUS) {
T_LOG("Converting %d to SIGSEGV", signal);
signal = SIGSEGV;
}
T_EXPECT_EQ(signal, SIGSEGV, NULL);
T_END;
}
#endif
T_EXPECT_EQ(signal, SIGABRT, NULL);
}
static void
signal_handler(int signo)
{
T_FAIL("Unexpected signal: %d\n", signo);
}
T_DECL(abort_pthread_corrupt_test_full, "Tests abort")
{
abort_test(PTHREAD, FULL_CORRUPTION);
}
T_DECL(abort_workqueue_corrupt_test_full, "Tests abort")
{
abort_test(WORKQUEUE, FULL_CORRUPTION);
}
T_DECL(abort_pthread_handler_test_full, "Tests abort")
{
T_SKIP("Abort hangs if the user registers their own SIGSEGV handler");
signal(SIGSEGV, signal_handler);
abort_test(PTHREAD, FULL_CORRUPTION);
}
T_DECL(abort_workqueue_handler_test_full, "Tests abort")
{
T_SKIP("Abort hangs if the user registers their own SIGSEGV handler");
signal(SIGSEGV, signal_handler);
abort_test(WORKQUEUE, FULL_CORRUPTION);
}
T_DECL(abort_pthread_corrupt_test_sig, "Tests abort")
{
abort_test(PTHREAD, SIG_CORRUPTION);
}
T_DECL(abort_workqueue_corrupt_test_sig, "Tests abort")
{
abort_test(WORKQUEUE, SIG_CORRUPTION);
}
T_DECL(abort_pthread_test, "Tests abort")
{
abort_test(PTHREAD, NO_CORRUPTION);
}
T_DECL(abort_workqueue_test, "Tests abort")
{
abort_test(WORKQUEUE, NO_CORRUPTION);
}