Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Segfault fix #3021

Merged
merged 18 commits into from Dec 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
86 changes: 56 additions & 30 deletions hpx/runtime/threads/coroutines/detail/context_linux_x86.hpp
Expand Up @@ -156,9 +156,8 @@ namespace hpx { namespace threads { namespace coroutines

x86_linux_context_impl()
: m_stack(nullptr)
#if defined(HPX_HAVE_STACKOVERFLOW_DETECTION)
{

#if defined(HPX_HAVE_STACKOVERFLOW_DETECTION)
// concept inspired by the following links:
//
// https://rethinkdb.com/blog/handling-stack-overflow-on-custom-stacks/
Expand All @@ -169,16 +168,15 @@ namespace hpx { namespace threads { namespace coroutines
segv_stack.ss_size = SEGV_STACK_SIZE;

std::memset(&action, '\0', sizeof(action));
action.sa_flags = SA_SIGINFO|SA_ONSTACK; //SA_STACK
action.sa_sigaction = &sigsegv_handler;
action.sa_flags = SA_SIGINFO|SA_ONSTACK;
action.sa_sigaction = &x86_linux_context_impl::sigsegv_handler;

sigaltstack(&segv_stack, nullptr);
sigfillset(&action.sa_mask);
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask, SIGSEGV);
sigaction(SIGSEGV, &action, nullptr);
}
#else
{}
#endif
}

/**
* Create a context that on restore invokes Functor on
Expand Down Expand Up @@ -229,41 +227,69 @@ namespace hpx { namespace threads { namespace coroutines
#endif

#if defined(HPX_HAVE_STACKOVERFLOW_DETECTION)
// concept inspired by the following links:
//
// https://rethinkdb.com/blog/handling-stack-overflow-on-custom-stacks/
// http://www.evanjones.ca/software/threading.html
//
segv_stack.ss_sp = valloc(SEGV_STACK_SIZE);
segv_stack.ss_flags = 0;
segv_stack.ss_size = SEGV_STACK_SIZE;

std::memset(&action, '\0', sizeof(action));
action.sa_flags = SA_SIGINFO|SA_ONSTACK; //SA_STACK
action.sa_flags = SA_SIGINFO|SA_ONSTACK;
action.sa_sigaction = &x86_linux_context_impl::sigsegv_handler;

sigaltstack(&segv_stack, nullptr);
sigfillset(&action.sa_mask);
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask, SIGSEGV);
sigaction(SIGSEGV, &action, nullptr);
#endif
}
}

#if defined(HPX_HAVE_STACKOVERFLOW_DETECTION)
static void sigsegv_handler(int signum, siginfo_t *info,
void *data)

// heuristic value 1 kilobyte
//
#define COROUTINE_STACKOVERFLOW_ADDR_EPSILON 1000UL

static void sigsegv_handler(int signum, siginfo_t *infoptr,
void *ctxptr)
{
void *addr = info->si_addr;

std::cerr << "Stack overflow in coroutine at address "
<< std::internal << std::hex
<< std::setw(sizeof(addr)*2+2)
<< std::setfill('0') << static_cast<int*>(addr)
<< ".\n\n";

std::cerr
<< "Configure the hpx runtime to allocate a larger coroutine "
"stack size.\n Use the hpx.stacks.small_size, "
"hpx.stacks.medium_size,\n hpx.stacks.large_size, "
"or hpx.stacks.huge_size configuration\nflags to configure "
"coroutine stack sizes.\n"
<< std::endl;

std::terminate();
ucontext_t * uc_ctx = static_cast< ucontext_t* >(ctxptr);
char* sigsegv_ptr = static_cast< char* >(infoptr->si_addr);

// https://www.gnu.org/software/libc/manual/html_node/Signal-Stack.html
//
char* stk_ptr = static_cast<char*>(uc_ctx->uc_stack.ss_sp);

std::ptrdiff_t addr_delta = (sigsegv_ptr > stk_ptr)
? (sigsegv_ptr - stk_ptr)
: (stk_ptr - sigsegv_ptr);

// check the stack addresses, if they're < 10 apart, terminate
// program should filter segmentation faults caused by
// coroutine stack overflows from 'genuine' stack overflows
//
if( static_cast<size_t>(addr_delta) <
COROUTINE_STACKOVERFLOW_ADDR_EPSILON ) {

std::cerr << "Stack overflow in coroutine at address "
<< std::internal << std::hex
<< std::setw(sizeof(sigsegv_ptr)*2+2)
<< std::setfill('0') << sigsegv_ptr
<< ".\n\n";

std::cerr
<< "Configure the hpx runtime to allocate a larger coroutine "
"stack size.\n Use the hpx.stacks.small_size, "
"hpx.stacks.medium_size,\n hpx.stacks.large_size, "
"or hpx.stacks.huge_size configuration\nflags to configure "
"coroutine stack sizes.\n"
<< std::endl;

std::terminate();
}
}
#endif
~x86_linux_context_impl()
Expand Down
66 changes: 45 additions & 21 deletions hpx/runtime/threads/coroutines/detail/context_posix.hpp
Expand Up @@ -257,36 +257,60 @@ namespace hpx { namespace threads { namespace coroutines
segv_stack.ss_size = SEGV_STACK_SIZE;

std::memset(&action, '\0', sizeof(action));
action.sa_flags = SA_SIGINFO|SA_ONSTACK; //SA_STACK
action.sa_flags = SA_SIGINFO|SA_ONSTACK;
action.sa_sigaction = &ucontext_context_impl::sigsegv_handler;

sigaltstack(&segv_stack, nullptr);
sigfillset(&action.sa_mask);
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask, SIGSEGV);
sigaction(SIGSEGV, &action, nullptr);
#endif
}

#if defined(HPX_HAVE_STACKOVERFLOW_DETECTION)
static void sigsegv_handler(int signum, siginfo_t *info,
void *data)

// heuristic value 1 kilobyte
//

#define COROUTINE_STACKOVERFLOW_ADDR_EPSILON 1000UL

static void sigsegv_handler(int signum, siginfo_t *infoptr,
void *ctxptr)
{
void *addr = info->si_addr;

std::cerr << "Stack overflow in coroutine at address "
<< std::internal << std::hex
<< std::setw(sizeof(addr)*2+2)
<< std::setfill('0') << static_cast<int*>(addr)
<< ".\n\n";

std::cerr
<< "Configure the hpx runtime to allocate a larger coroutine "
"stack size.\n Use the hpx.stacks.small_size, "
"hpx.stacks.medium_size,\n hpx.stacks.large_size, "
"or hpx.stacks.huge_size configuration\nflags to configure "
"coroutine stack sizes.\n"
<< std::endl;

std::terminate();
ucontext_t * uc_ctx = static_cast< ucontext_t* >(ctxptr);
char* sigsegv_ptr = static_cast< char* >(infoptr->si_addr);

// https://www.gnu.org/software/libc/manual/html_node/Signal-Stack.html
//
char* stk_ptr = static_cast<char*>(uc_ctx->uc_stack.ss_sp);

std::ptrdiff_t addr_delta = (sigsegv_ptr > stk_ptr)
? (sigsegv_ptr - stk_ptr)
: (stk_ptr - sigsegv_ptr);

// check the stack addresses, if they're < 10 apart, terminate
// program should filter segmentation faults caused by
// coroutine stack overflows from 'genuine' stack overflows
//
if( static_cast<size_t>(addr_delta) <
COROUTINE_STACKOVERFLOW_ADDR_EPSILON ) {

std::cerr << "Stack overflow in coroutine at address "
<< std::internal << std::hex
<< std::setw(sizeof(sigsegv_ptr)*2+2)
<< std::setfill('0') << sigsegv_ptr
<< ".\n\n";

std::cerr
<< "Configure the hpx runtime to allocate a larger coroutine "
"stack size.\n Use the hpx.stacks.small_size, "
"hpx.stacks.medium_size,\n hpx.stacks.large_size, "
"or hpx.stacks.huge_size configuration\nflags to configure "
"coroutine stack sizes.\n"
<< std::endl;

std::terminate();
}
}
#endif

Expand Down
6 changes: 5 additions & 1 deletion tests/unit/threads/CMakeLists.txt
Expand Up @@ -20,6 +20,7 @@ set(tests

if(HPX_WITH_THREAD_STACKOVERFLOW_DETECTION)
set(tests ${tests} thread_stacksize_overflow)
set(tests ${tests} thread_stacksize_overflow_v2)
endif()

if(HPX_WITH_THREAD_LOCAL_STORAGE)
Expand Down Expand Up @@ -85,4 +86,7 @@ set_property(TARGET lockfree_fifo_test_exe APPEND
if(HPX_WITH_THREAD_STACKOVERFLOW_DETECTION)
set_tests_properties(tests.unit.threads.thread_stacksize_overflow PROPERTIES
PASS_REGULAR_EXPRESSION "Stack overflow in coroutine at address 0x[0-9a-fA-F]*")
endif()

set_tests_properties(tests.unit.threads.thread_stacksize_overflow_v2 PROPERTIES
PASS_REGULAR_EXPRESSION "Stack overflow in coroutine at address 0x[0-9a-fA-F]*")
endif()
41 changes: 41 additions & 0 deletions tests/unit/threads/thread_stacksize_overflow_v2.cpp
@@ -0,0 +1,41 @@
// Copyright (C) 2012 Hartmut Kaiser
// Copyright (C) 2017 Christopher Taylor
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <hpx/hpx_main.hpp>
#include <hpx/include/actions.hpp>
#include <hpx/include/runtime.hpp>
#include <hpx/runtime/threads/thread_data.hpp>
#include <hpx/util/lightweight_test.hpp>

#include <cstring>
#include <vector>

///////////////////////////////////////////////////////////////////////////////
void test_small_stacksize()
{
HPX_TEST(hpx::threads::get_self_ptr());
// verify that sufficient stack has been allocated
HPX_TEST_EQ(hpx::threads::get_ctx_ptr()->get_stacksize(),
hpx::get_runtime().get_config().get_stack_size(
hpx::threads::thread_stacksize_small));

// allocate HPX_SMALL_STACK_SIZE - HPX_THREADS_STACK_OVERHEAD memory on the stack
char array[HPX_SMALL_STACK_SIZE*HPX_THREADS_STACK_OVERHEAD];

// do something to that array
std::memset(array, '\0', sizeof(array));
}
HPX_DECLARE_ACTION(test_small_stacksize, test_small_stacksize_action)
HPX_ACTION_USES_SMALL_STACK(test_small_stacksize_action)
HPX_PLAIN_ACTION(test_small_stacksize, test_small_stacksize_action)

int main()
{
int *i = nullptr;
int x = (*i);
return hpx::util::report_errors();
}