Skip to content

Commit

Permalink
Merge branch 'corosegfault'
Browse files Browse the repository at this point in the history
  • Loading branch information
hkaiser committed Jul 15, 2017
2 parents 9161bbb + a7a947d commit c24d75e
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 3 deletions.
15 changes: 15 additions & 0 deletions CMakeLists.txt
Expand Up @@ -147,6 +147,21 @@ if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
# set(DEFAULT_MALLOC "jemalloc")
endif()

if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
set(HPX_WITH_THREAD_STACKOVERFLOW_DETECTION_DEFAULT OFF)
string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_UC)
if("${CMAKE_BUILD_TYPE_UC}" STREQUAL "DEBUG")
set(HPX_WITH_THREAD_STACKOVERFLOW_DETECTION_DEFAULT ON)
endif()
hpx_option(HPX_WITH_THREAD_STACKOVERFLOW_DETECTION
BOOL
"Enable stackoverflow detection for HPX threads/coroutines. (default: OFF, debug: ON)"
${HPX_WITH_THREAD_STACKOVERFLOW_DETECTION_DEFAULT} ADVANCED)
if(HPX_WITH_THREAD_STACKOVERFLOW_DETECTION)
hpx_add_config_define(HPX_HAVE_THREAD_STACKOVERFLOW_DETECTION)
endif()
endif()

hpx_option(HPX_WITH_MALLOC
STRING
"Define which allocator should be linked in. Options are: system, tcmalloc, jemalloc, tbbmalloc, and custom (default is: tcmalloc)"
Expand Down
79 changes: 79 additions & 0 deletions hpx/runtime/threads/coroutines/detail/context_linux_x86.hpp
Expand Up @@ -3,6 +3,7 @@
// Copyright (c) 2007-2016 Hartmut Kaiser
// Copyright (c) 2011 Bryce Adelstein-Lelbach
// Copyright (c) 2013-2016 Thomas Heller
// Copyright (c) 2017 Christopher Taylor
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
Expand All @@ -29,6 +30,22 @@
#include <boost/atomic.hpp>
#include <boost/format.hpp>

#if defined(HPX_HAVE_THREAD_STACKOVERFLOW_DETECTION)

#include <signal.h>
#include <stdlib.h>
#include <strings.h>
#include <cstring>

#ifndef SEGV_STACK_SIZE
#define SEGV_STACK_SIZE MINSIGSTKSZ+4096
#endif

#endif

#include <iostream>
#include <iomanip>

#if defined(HPX_HAVE_VALGRIND)
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
#if defined(HPX_GCC_DIAGNOSTIC_PRAGMA_CONTEXTS)
Expand Down Expand Up @@ -140,7 +157,29 @@ namespace hpx { namespace threads { namespace coroutines

x86_linux_context_impl()
: m_stack(nullptr)
#if defined(HPX_HAVE_THREAD_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_sigaction = &sigsegv_handler;

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

/**
* Create a context that on restore invokes Functor on
Expand Down Expand Up @@ -189,8 +228,45 @@ namespace hpx { namespace threads { namespace coroutines
VALGRIND_STACK_REGISTER(m_stack, eos));
}
#endif

#if defined(HPX_HAVE_THREAD_STACKOVERFLOW_DETECTION)
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_sigaction = &x86_linux_context_impl::sigsegv_handler;

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

#if defined(HPX_HAVE_THREAD_STACKOVERFLOW_DETECTION)
static void sigsegv_handler(int signum, siginfo_t *info,
void *data)
{
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();
}
#endif
~x86_linux_context_impl()
{
if (m_stack)
Expand Down Expand Up @@ -344,6 +420,9 @@ namespace hpx { namespace threads { namespace coroutines

std::ptrdiff_t m_stack_size;
void* m_stack;

struct sigaction action;
stack_t segv_stack;
};

typedef x86_linux_context_impl context_impl;
Expand Down
64 changes: 63 additions & 1 deletion hpx/runtime/threads/coroutines/detail/context_posix.hpp
Expand Up @@ -105,6 +105,22 @@ namespace posix { namespace pth
#include <cstddef> // ptrdiff_t
#include <ucontext.h>

#if defined(HPX_HAVE_THREAD_STACKOVERFLOW_DETECTION)

#include <signal.h>
#include <stdlib.h>
#include <strings.h>
#include <cstring>

#ifndef SEGV_STACK_SIZE
#define SEGV_STACK_SIZE MINSIGSTKSZ+4096
#endif

#endif

#include <iostream>
#include <iomanip>

namespace hpx { namespace threads { namespace coroutines { namespace detail {
namespace posix { namespace ucontext
{
Expand Down Expand Up @@ -223,8 +239,51 @@ namespace hpx { namespace threads { namespace coroutines
&m_ctx, m_stack, m_stack_size, funp_, cb_, nullptr);
HPX_UNUSED(error);
HPX_ASSERT(error == 0);

#if defined(HPX_HAVE_THREAD_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_sigaction = &ucontext_context_impl::sigsegv_handler;

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

#if defined(HPX_HAVE_THREAD_STACKOVERFLOW_DETECTION)
static void sigsegv_handler(int signum, siginfo_t *info,
void *data)
{
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();
}
#endif

~ucontext_context_impl()
{
if (m_stack)
Expand Down Expand Up @@ -316,6 +375,9 @@ namespace hpx { namespace threads { namespace coroutines
void * m_stack;
void * cb_;
void(*funp_)(void*);

struct sigaction action;
stack_t segv_stack;
};

typedef ucontext_context_impl context_impl;
Expand All @@ -328,7 +390,7 @@ namespace hpx { namespace threads { namespace coroutines
* This #else clause is essentially unchanged from the original Google Summer
* of Code version of Boost.Coroutine, which comments:
* "Context swapping can be implemented on most posix systems lacking *context
* using the sigaltstack+longjmp trick."
* using the signaltstack+longjmp trick."
* This is in fact what the (highly portable) Pth library does, so if you
* encounter such a system, perhaps the best approach would be to twiddle the
* #if logic in this header to use the pth.h implementation above.
Expand Down
11 changes: 9 additions & 2 deletions tests/unit/threads/CMakeLists.txt
Expand Up @@ -18,6 +18,10 @@ set(tests
thread_yield
)

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

if(HPX_WITH_THREAD_LOCAL_STORAGE)
set(tests ${tests} tss)
endif()
Expand Down Expand Up @@ -76,6 +80,9 @@ foreach(test ${tests})
endforeach()

set_property(TARGET lockfree_fifo_test_exe APPEND
PROPERTY COMPILE_DEFINITIONS
"HPX_NO_VERSION_CHECK")
PROPERTY COMPILE_DEFINITIONS "HPX_NO_VERSION_CHECK")

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()
50 changes: 50 additions & 0 deletions tests/unit/threads/thread_stacksize_overflow.cpp
@@ -0,0 +1,50 @@
// 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()
{
std::vector<hpx::id_type> localities = hpx::find_all_localities();

for (hpx::id_type const& id : localities)
{
{
test_small_stacksize_action test_action;
test_action(id);
}

}

return hpx::util::report_errors();
}

0 comments on commit c24d75e

Please sign in to comment.