Skip to content

Commit

Permalink
test/basic: add a new test to check mprotect-based stack guard
Browse files Browse the repository at this point in the history
stack_guard tests the mprotect() behavior by catching the SEGV signal
that should be issued when a program accesses the mprotect()'ed stack
guard.
  • Loading branch information
shintaro-iwasaki committed Apr 19, 2021
1 parent 727ddda commit b19b27a
Show file tree
Hide file tree
Showing 3 changed files with 209 additions and 0 deletions.
1 change: 1 addition & 0 deletions test/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ basic/ext_thread_future
basic/ext_thread_join
basic/ext_thread_mutex
basic/ext_thread_rwlock
basic/stack_guard
basic/timer
basic/info_print
basic/info_print_stack
Expand Down
3 changes: 3 additions & 0 deletions test/basic/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ TESTS = \
ext_thread_join \
ext_thread_mutex \
ext_thread_rwlock \
stack_guard \
timer \
info_print \
info_print_stack \
Expand Down Expand Up @@ -173,6 +174,7 @@ ext_thread_future_SOURCES = ext_thread_future.c
ext_thread_join_SOURCES = ext_thread_join.c
ext_thread_mutex_SOURCES = ext_thread_mutex.c
ext_thread_rwlock_SOURCES = ext_thread_rwlock.c
stack_guard_SOURCES = stack_guard.c
timer_SOURCES = timer.c
info_print_SOURCES = info_print.c
info_print_stack_SOURCES = info_print_stack.c
Expand Down Expand Up @@ -255,6 +257,7 @@ testing:
./ext_thread_join
./ext_thread_mutex
./ext_thread_rwlock
./stack_guard
./timer
./info_print
./info_print_stack
Expand Down
205 changes: 205 additions & 0 deletions test/basic/stack_guard.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */
/*
* See COPYRIGHT in top-level directory.
*/

#include <stdio.h>
#include <stdlib.h>

#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L

#include <pthread.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>
#include "abt.h"
#include "abttest.h"

#define DUMMY_SIZE ((int)(1024 / sizeof(double)))
#define SYS_PAGE_SIZE 4096

int g_mprotect_signal = 0;
volatile int g_sig_err = 0;
volatile int g_is_segv = 0;
volatile char *gp_stack = NULL;

void segv_handler(int sig, siginfo_t *si, void *unused)
{
if (sig != SIGSEGV) {
g_sig_err = 1; /* We cannot call assert(). */
} else if (si->si_addr != gp_stack) {
g_sig_err = 2;
} else {
/* Since POSIX does not mark mprotect() as async-signal safe, we need to
* ask another thread to call mprotect() instead of this thread even if
* we control where the signal happens; calling an async signal-unsafe
* function can cause any unexpected issues. */
ATS_atomic_store(&g_mprotect_signal, 1);
while (ATS_atomic_load(&g_mprotect_signal) == 1) {
; /* Waiting for the helper thread. */
}
/* mprotect() finished. */
g_is_segv = 1;
}
}

void *helper_func(void *arg)
{
/* Waiting for g_mprotect_signal from a signal handler. */
while (ATS_atomic_load(&g_mprotect_signal) == 0)
;
/* Call mprotect() to temporarily allow an access. */
int ret = mprotect((void *)gp_stack, SYS_PAGE_SIZE, PROT_READ | PROT_WRITE);
assert(ret == 0);
/* Tell the signal handler that mprotect has finished. */
ATS_atomic_store(&g_mprotect_signal, 0);
return NULL;
}

void thread_func(void *arg)
{
int ret;
void *p_stack;
size_t stack_size;
/* Get the stack information. */
{
ABT_thread self_thread;
ABT_thread_attr self_thread_attr;
ret = ABT_self_get_thread(&self_thread);
ATS_ERROR(ret, "ABT_self_get_thread");
ret = ABT_thread_get_attr(self_thread, &self_thread_attr);
ATS_ERROR(ret, "ABT_thread_get_attr");
ret =
ABT_thread_attr_get_stack(self_thread_attr, &p_stack, &stack_size);
ATS_ERROR(ret, "ABT_thread_attr_get_stack");
ret = ABT_thread_attr_free(&self_thread_attr);
ATS_ERROR(ret, "ABT_thread_attr_free");
}

/* We can reasonably assume that we do not corrupt the function stack of
* thread_func(). Let's assume that the protected page is within a few
* pages from the bottom of the stack.
* gp_stack should be aligned with the page size. */
gp_stack = (char *)(((((uintptr_t)p_stack) + SYS_PAGE_SIZE - 1) / SYS_PAGE_SIZE) *
SYS_PAGE_SIZE +
SYS_PAGE_SIZE * 2);
while (1) {
/* Using this stack variable to see if we can observe SEGV. */
gp_stack -= SYS_PAGE_SIZE;
assert(((char *)p_stack) <= gp_stack);
volatile char val = gp_stack[0];
/* Though we use "volatile", we'd like to put a compiler barrier just in
* case. */
__asm__ __volatile__("" ::: "memory");
/* The following should cause SEGV. If SEGV happens, the signal handler
* will allow this ULT to temporarily access this. */
gp_stack[0] = val;
__asm__ __volatile__("" ::: "memory");
/* Signal might have happened. */
if (g_is_segv) {
assert(g_sig_err == 0);
/* Succeeded! Undo the mprotect setting. Originally it should be
* read-protected. */
g_is_segv = 0;
ret = mprotect((void *)gp_stack, SYS_PAGE_SIZE, PROT_READ);
assert(ret == 0);
return;
}
/* We must catch SEGV until we touch stack_size */
}
}

int main(int argc, char *argv[])
{
int ret, i;
/* Catch SEGV. */
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = segv_handler;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGSEGV, &sa, NULL) == -1) {
/* Unsupported. */
return 77;
}
/* Set memory protection */
putenv("ABT_STACK_OVERFLOW_CHECK=mprotect_strict");

/* Initialize */
ATS_read_args(argc, argv);
ATS_init(argc, argv, 2);

/* Check if the mprotect-based stack guard is enabled. */
int stack_overflow_check_mode = 0;
ret =
ABT_info_query_config(ABT_INFO_QUERY_KIND_ENABLED_STACK_OVERFLOW_CHECK,
&stack_overflow_check_mode);
ATS_ERROR(ret, "ABT_info_query_config");
if (stack_overflow_check_mode != 3) {
/* Unsupported. */
return 77;
}

ABT_xstream xstream;
ABT_pool main_pool;
ret = ABT_self_get_xstream(&xstream);
ATS_ERROR(ret, "ABT_self_get_xstream");
ret = ABT_xstream_get_main_pools(xstream, 1, &main_pool);
ATS_ERROR(ret, "ABT_xstream_get_main_pools");

for (i = 0; i < 3; i++) {
pthread_t helper_thread;
ret = pthread_create(&helper_thread, NULL, helper_func, NULL);
assert(ret == 0);
ABT_thread thread;
void *stack = NULL;
if (i == 0) {
/* 1. ULT + default parameters. */
ret = ABT_thread_create(main_pool, thread_func, NULL,
ABT_THREAD_ATTR_NULL, &thread);
ATS_ERROR(ret, "ABT_thread_create");
} else if (i == 1) {
/* 2. ULT + user-given stack size. */
ABT_thread_attr thread_attr;
ret = ABT_thread_attr_create(&thread_attr);
ATS_ERROR(ret, "ABT_thread_attr_create");
ret = ABT_thread_attr_set_stacksize(thread_attr, 1024 * 1024);
ATS_ERROR(ret, "ABT_thread_attr_set_stacksize");
ret = ABT_thread_create(main_pool, thread_func, NULL, thread_attr,
&thread);
ATS_ERROR(ret, "ABT_thread_create");
ret = ABT_thread_attr_free(&thread_attr);
ATS_ERROR(ret, "ABT_thread_attr_free");
} else if (i == 2) {
/* 3. ULT + user-given stack. */
ABT_thread_attr thread_attr;
ret = ABT_thread_attr_create(&thread_attr);
ATS_ERROR(ret, "ABT_thread_attr_create");
stack = calloc(1, 1024 * 1024);
ret = ABT_thread_attr_set_stack(thread_attr, stack, 1024 * 1024);
ATS_ERROR(ret, "ABT_thread_attr_set_stack");
ret = ABT_thread_create(main_pool, thread_func, NULL,
ABT_THREAD_ATTR_NULL, &thread);
ATS_ERROR(ret, "ABT_thread_create");
ret = ABT_thread_attr_free(&thread_attr);
ATS_ERROR(ret, "ABT_thread_attr_free");
}
ret = ABT_thread_free(&thread);
ATS_ERROR(ret, "ABT_thread_free");
if (stack)
free(stack);
ret = pthread_join(helper_thread, NULL);
assert(ret == 0);
}
/* Finalize */
return ATS_finalize(0);
}

#else /* _POSIX_C_SOURCE */

int main()
{
/* Unsupported. */
return 77;
}

#endif

0 comments on commit b19b27a

Please sign in to comment.