Skip to content

Commit

Permalink
Towards a safe solution
Browse files Browse the repository at this point in the history
  • Loading branch information
apolukhin committed Dec 2, 2023
1 parent f33e01d commit 708c4ed
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 111 deletions.
179 changes: 70 additions & 109 deletions src/from_exception.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@
#include <cstddef>
#include <dlfcn.h>

#if defined(__x86_64__) || defined(_M_X64)
#define BOOST_STACKTRACE_ALWAYS_STORE_IN_PADDING 1
#else
#define BOOST_STACKTRACE_ALWAYS_STORE_IN_PADDING 0
#endif

#if !BOOST_STACKTRACE_ALWAYS_STORE_IN_PADDING
#include <mutex>
#include <unordered_map>
#endif

namespace {

constexpr std::size_t kStacktraceDumpSize = 4096;
Expand Down Expand Up @@ -60,96 +71,46 @@ struct decrement_on_destroy {
~decrement_on_destroy() { --to_decrement; }
};

} // namespace
#if !BOOST_STACKTRACE_ALWAYS_STORE_IN_PADDING
// Inspired by the coursework by Andrei Nekrashevich in the `libsfe`
std::mutex mutex;
std::unordered_map<void*, const char*> exception_to_dump_mapping;
#endif

#ifndef BOOST_STACKTRACE_USE_GLOBAL_MAP
} // namespace

namespace __cxxabiv1 {

#if defined(__GNUC__) && defined(__ELF__)

// libc++-runtime specific function
extern "C" BOOST_NOINLINE BOOST_SYMBOL_VISIBLE __attribute__((weak))
void __cxa_decrement_exception_refcount(void *thrown_object) throw();
void __cxa_increment_exception_refcount(void *primary_exception) throw();

#endif

static const char*& reference_to_empty_padding(void* ptr) noexcept {
#if defined(__GNUC__) && defined(__ELF__)
if (__cxa_decrement_exception_refcount) {
// libc++-runtime
BOOST_ASSERT_MSG(sizeof(void*) == 4,
"32bit platforms are unsupported with libc++ runtime");
return exception_begin_llvm_ptr(ptr)->reserve;
}
#endif
return exception_begin_gcc_ptr(ptr)->reserve;
static bool is_libcpp_runtime() noexcept {
return __cxa_increment_exception_refcount;
}

extern "C" BOOST_SYMBOL_EXPORT
void* __cxa_allocate_exception(size_t thrown_size) throw() {
static const auto orig_allocate_exception = []() {
void* const ptr = ::dlsym(RTLD_NEXT, "__cxa_allocate_exception");
BOOST_ASSERT_MSG(ptr, "Failed to find '__cxa_allocate_exception'");
return reinterpret_cast<void*(*)(size_t)>(ptr);
}();

#ifndef NDEBUG
static thread_local std::size_t in_allocate_exception = 0;
BOOST_ASSERT_MSG(in_allocate_exception < 10, "Suspicious recursion");
++in_allocate_exception;
const decrement_on_destroy guard{in_allocate_exception};
#endif

static constexpr std::size_t kAlign = alignof(std::max_align_t);
thrown_size = (thrown_size + kAlign - 1) & (~(kAlign - 1));

void* const ptr = orig_allocate_exception(thrown_size + kStacktraceDumpSize);
char* const dump_ptr = static_cast<char*>(ptr) + thrown_size;

constexpr size_t kSkip = 1;
boost::stacktrace::safe_dump_to(kSkip, dump_ptr, kStacktraceDumpSize);

BOOST_ASSERT_MSG(reference_to_empty_padding(ptr) == nullptr,
"Not zeroed out, unsupported implementation");
reference_to_empty_padding(ptr) = dump_ptr;

return ptr;
}
#else

} // namespace __cxxabiv1
static bool is_libcpp_runtime() noexcept { return false; }

namespace boost { namespace stacktrace { namespace impl {
#endif

BOOST_SYMBOL_EXPORT const char* current_exception_stacktrace() noexcept {
void* const exc_raw_ptr = get_current_exception_raw_ptr();
if (!exc_raw_ptr) {
return nullptr;
static const char*& reference_to_empty_padding(void* ptr) noexcept {
if (is_libcpp_runtime()) {
// libc++-runtime
BOOST_ASSERT_MSG(
sizeof(void*) == 4,
"32bit platforms are unsupported with libc++ runtime padding reusage. "
"Please report this issue to the library maintainters."
);
return exception_begin_llvm_ptr(ptr)->reserve;
}

return __cxxabiv1::reference_to_empty_padding(exc_raw_ptr);
return exception_begin_gcc_ptr(ptr)->reserve;
}

BOOST_SYMBOL_EXPORT void assert_no_pending_traces() noexcept {}

}}} // namespace boost::stacktrace::impl

#else

// Inspired by the coursework by Andrei Nekrashevich in the `libsfe`

#include <mutex>
#include <unordered_map>

namespace {

std::mutex mutex;
std::unordered_map<void*, const char*> exception_to_dump_mapping;

} // namespace

namespace __cxxabiv1 {

extern "C" BOOST_SYMBOL_EXPORT
void* __cxa_allocate_exception(size_t thrown_size) throw() {
static const auto orig_allocate_exception = []() {
Expand All @@ -174,42 +135,31 @@ void* __cxa_allocate_exception(size_t thrown_size) throw() {
constexpr size_t kSkip = 1;
boost::stacktrace::safe_dump_to(kSkip, dump_ptr, kStacktraceDumpSize);

{
#if !BOOST_STACKTRACE_ALWAYS_STORE_IN_PADDING
if (is_libcpp_runtime()) {
const std::lock_guard<std::mutex> guard{mutex};
exception_to_dump_mapping[ptr] = dump_ptr;
}

return ptr;
}

// Not called in libc++ as the __cxa_decrement_exception_refcount has an inlined
// call to __cxa_free_exception
extern "C" BOOST_SYMBOL_EXPORT
void __cxa_free_exception(void* thrown_object) throw() {
static const auto orig_free_exception = []() {
void* const ptr = ::dlsym(RTLD_NEXT, "__cxa_free_exception");
BOOST_ASSERT_MSG(ptr, "Failed to find '__cxa_free_exception'");
return reinterpret_cast<void(*)(void*)>(ptr);
}();

#ifndef NDEBUG
static thread_local std::size_t in_free_exception = 0;
BOOST_ASSERT_MSG(in_free_exception < 10, "Suspicious recursion");
++in_free_exception;
const decrement_on_destroy guard{in_free_exception};
} else
#endif

{
const std::lock_guard<std::mutex> guard{mutex};
exception_to_dump_mapping.erase(thrown_object);
BOOST_ASSERT_MSG(
reference_to_empty_padding(ptr) == nullptr,
"Not zeroed out, unsupported implementation"
);
reference_to_empty_padding(ptr) = dump_ptr;
}

orig_free_exception(thrown_object);
return ptr;
}

// libc++ specific function
#if !BOOST_STACKTRACE_ALWAYS_STORE_IN_PADDING

// __cxa_free_exception is not called in libc++ as the
// __cxa_decrement_exception_refcount has an inlined call to
// __cxa_free_exception. Overriding libc++ specific function
extern "C" BOOST_SYMBOL_EXPORT
void __cxa_decrement_exception_refcount(void *thrown_object) throw() {
BOOST_ASSERT(is_libcpp_runtime());
if (!thrown_object) {
return;
}
Expand All @@ -224,7 +174,7 @@ void __cxa_decrement_exception_refcount(void *thrown_object) throw() {

// The following line has a race and could give false positives and false
// negatives. In first case we remove the trace earlier, in the second case
// we get a small memory leak.
// we get a memory leak.
if (exception_header->referenceCount == 1) {
const std::lock_guard<std::mutex> guard{mutex};
exception_to_dump_mapping.erase(thrown_object);
Expand All @@ -233,6 +183,8 @@ void __cxa_decrement_exception_refcount(void *thrown_object) throw() {
orig_decrement_refcount(thrown_object);
}

#endif

} // namespace __cxxabiv1

namespace boost { namespace stacktrace { namespace impl {
Expand All @@ -243,21 +195,30 @@ BOOST_SYMBOL_EXPORT const char* current_exception_stacktrace() noexcept {
return nullptr;
}

const std::lock_guard<std::mutex> guard{mutex};
const auto it = exception_to_dump_mapping.find(exc_raw_ptr);
if (it != exception_to_dump_mapping.end()) {
return it->second;
#if !BOOST_STACKTRACE_ALWAYS_STORE_IN_PADDING
if (__cxxabiv1::is_libcpp_runtime()) {
const std::lock_guard<std::mutex> guard{mutex};
const auto it = exception_to_dump_mapping.find(exc_raw_ptr);
if (it != exception_to_dump_mapping.end()) {
return it->second;
} else {
return nullptr;
}
} else
#endif
{
return __cxxabiv1::reference_to_empty_padding(exc_raw_ptr);
}

return nullptr;
}

BOOST_SYMBOL_EXPORT void assert_no_pending_traces() noexcept {
const std::lock_guard<std::mutex> guard{mutex};
BOOST_ASSERT(exception_to_dump_mapping.empty());
#if !BOOST_STACKTRACE_ALWAYS_STORE_IN_PADDING
if (__cxxabiv1::is_libcpp_runtime()) {
const std::lock_guard<std::mutex> guard{mutex};
BOOST_ASSERT(exception_to_dump_mapping.empty());
}
#endif
}

}}} // namespace boost::stacktrace::impl

#endif

4 changes: 2 additions & 2 deletions test/Jamfile.v2
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,10 @@ test-suite stacktrace_tests
[ run test_from_exception_none.cpp : : : $(LINKSHARED_BT) <debug-symbols>on : from_exception_none_bt ]
[ run test_from_exception_none.cpp : : : <define>BOOST_STACKTRACE_USE_BACKTRACE $(BT_DEPS) <debug-symbols>on : from_exception_none_bt_ho ]

[ link test_from_exception.cpp : <library>/boost/stacktrace//boost_stacktrace_from_exception $(LINKSHARED_BASIC) <debug-symbols>on : from_exception_basic ]
[ link test_from_exception.cpp : <library>/boost/stacktrace//boost_stacktrace_from_exception $(LINKSHARED_BASIC) <debug-symbols>on : from_exception_basic ]
[ run test_from_exception.cpp : : : <library>/boost/stacktrace//boost_stacktrace_from_exception $(LINKSHARED_BT) <debug-symbols>on : from_exception_bt ]

[ link test_from_exception.cpp : <library>/boost/stacktrace//boost_stacktrace_from_exception $(FORCE_SYMBOL_EXPORT) $(BASIC_DEPS) <debug-symbols>on : from_exception_basic_ho ]
[ link test_from_exception.cpp : <library>/boost/stacktrace//boost_stacktrace_from_exception $(FORCE_SYMBOL_EXPORT) $(BASIC_DEPS) <debug-symbols>on : from_exception_basic_ho ]
[ run test_from_exception.cpp : : : <library>/boost/stacktrace//boost_stacktrace_from_exception <define>BOOST_STACKTRACE_USE_BACKTRACE $(BT_DEPS) <debug-symbols>on : from_exception_bt_ho ]
;

Expand Down

0 comments on commit 708c4ed

Please sign in to comment.