Skip to content

Commit

Permalink
Merge pull request #14 from gofractally/merge-upstream
Browse files Browse the repository at this point in the history
Merge from AntelopeIO
  • Loading branch information
swatanabe authored Apr 19, 2024
2 parents c8495e8 + fa71571 commit 5170cdf
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 14 deletions.
5 changes: 5 additions & 0 deletions include/eosio/vm/allocator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <cstring>
#include <map>
#include <set>
#include <span>
#include <memory>
#include <mutex>
#include <span>
Expand Down Expand Up @@ -386,6 +387,8 @@ namespace eosio { namespace vm {

const void* get_code_start() const { return _code_base; }

std::span<std::byte> get_code_span() const {return {(std::byte*)_code_base, _code_size};}

/* different semantics than free,
* the memory must be at the end of the most recently allocated block.
*/
Expand Down Expand Up @@ -557,5 +560,7 @@ namespace eosio { namespace vm {
{
return { raw - prefix_size(), max_memory + prefix_size() + suffix_size() };
}

std::span<std::byte> get_span() const {return {(std::byte*)raw, max_memory};}
};
}} // namespace eosio::vm
18 changes: 11 additions & 7 deletions include/eosio/vm/execution_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,8 @@ namespace eosio { namespace vm {
auto required_memory = static_cast<uint64_t>(offset) + data_seg.data.size();
EOS_VM_ASSERT(required_memory <= available_memory, wasm_memory_exception, "data out of range");
auto addr = _linear_memory + offset;
memcpy((char*)(addr), data_seg.data.data(), data_seg.data.size());
if(data_seg.data.size())
memcpy((char*)(addr), data_seg.data.data(), data_seg.data.size());
_dropped_data[i] = true;
}
}
Expand Down Expand Up @@ -248,7 +249,8 @@ namespace eosio { namespace vm {
} else {
uint32_t offset = elem_seg.offset.value.i32;
EOS_VM_ASSERT(static_cast<std::uint64_t>(offset) + elem_seg.elems.size() <= mod.tables[0].limits.initial, wasm_memory_exception, "elem out of range");
std::memcpy(table_start + offset, elem_seg.elems.data(), elem_seg.elems.size() * sizeof(table_entry));
if (elem_seg.elems.size())
std::memcpy(table_start + offset, elem_seg.elems.data(), elem_seg.elems.size() * sizeof(table_entry));
_dropped_elems[i] = true;
}
}
Expand All @@ -263,7 +265,8 @@ namespace eosio { namespace vm {
if (std::uint64_t{s} + n > data_len)
throw_<wasm_memory_exception>("data out of range");
void* dest = get_interface().template validate_pointer<unsigned char>(d, n);
std::memcpy(dest, data_seg.data.data() + s, n);
if (data_len)
std::memcpy(dest, data_seg.data.data() + s, n);
}

void drop_data(uint32_t x) {
Expand Down Expand Up @@ -291,7 +294,8 @@ namespace eosio { namespace vm {
throw_<wasm_memory_exception>("elem out of range");
if (std::uint64_t{d} + n > mod.tables[0].limits.initial)
throw_<wasm_memory_exception>("wasm memory out-of-bounds");
std::memcpy(get_table_base() + d, elem_seg.elems.data() + s, n * sizeof(table_entry));
if (elem_len)
std::memcpy(get_table_base() + d, elem_seg.elems.data() + s, n * sizeof(table_entry));
}

void drop_elem(uint32_t x) {
Expand Down Expand Up @@ -530,11 +534,11 @@ namespace eosio { namespace vm {

vm::invoke_with_signal_handler([&]() {
result = execute<args_count>(args_raw, fn, this, base_type::linear_memory(), stack, ft.return_type);
}, &handle_signal);
}, &handle_signal, {_mod->allocator.get_code_span(), base_type::get_wasm_allocator()->get_span()});
} else {
vm::invoke_with_signal_handler([&]() {
result = execute<args_count>(args_raw, fn, this, base_type::linear_memory(), stack, ft.return_type);
}, &handle_signal);
}, &handle_signal, {_mod->allocator.get_code_span(), base_type::get_wasm_allocator()->get_span()});
}
}
} catch(wasm_exit_exception&) {
Expand Down Expand Up @@ -976,7 +980,7 @@ namespace eosio { namespace vm {
setup_locals(func_index);
vm::invoke_with_signal_handler([&]() {
execute(visitor);
}, &handle_signal);
}, &handle_signal, {_mod->allocator.get_code_span(), base_type::get_wasm_allocator()->get_span()});
}

if (_mod->get_function_type(func_index).return_count && !_state.exiting) {
Expand Down
2 changes: 1 addition & 1 deletion include/eosio/vm/profile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ struct scoped_profile {
#else

__attribute__((visibility("default")))
inline thread_local std::atomic<profile_data*> per_thread_profile_data = ATOMIC_VAR_INIT(nullptr);
inline thread_local std::atomic<profile_data*> per_thread_profile_data{nullptr};

inline void profile_handler(int sig, siginfo_t* info, void* uc) {
static_assert(std::atomic<profile_data*>::is_always_lock_free);
Expand Down
32 changes: 28 additions & 4 deletions include/eosio/vm/signals.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@
#include <cstdlib>
#include <exception>
#include <utility>
#include <span>
#include <signal.h>
#include <setjmp.h>

namespace eosio { namespace vm {

// Fixes a duplicate symbol build issue when building with `-fvisibility=hidden`
__attribute__((visibility("default")))
inline thread_local std::atomic<sigjmp_buf*> signal_dest = ATOMIC_VAR_INIT(nullptr);
inline thread_local std::atomic<sigjmp_buf*> signal_dest{nullptr};

__attribute__((visibility("default")))
inline thread_local std::atomic<const std::vector<std::span<std::byte>>*> protected_memory_ranges;

// Fixes a duplicate symbol build issue when building with `-fvisibility=hidden`
__attribute__((visibility("default")))
Expand All @@ -23,9 +27,23 @@ namespace eosio { namespace vm {
template<int Sig>
inline struct sigaction prev_signal_handler;

inline bool in_protected_range(void* addr) {
//empty protection list means legacy catch-all behavior; useful for some of the old tests
const auto* ranges = protected_memory_ranges.load();
if(!ranges || ranges->empty())
return true;

for(const std::span<std::byte>& range : *ranges) {
if(addr >= range.data() && addr < range.data() + range.size())
return true;
}
return false;
}

inline void signal_handler(int sig, siginfo_t* info, void* uap) {
sigjmp_buf* dest = std::atomic_load(&signal_dest);
if (dest) {

if (dest && in_protected_range(info->si_addr)) {
siglongjmp(*dest, sig);
} else {
struct sigaction* prev_action;
Expand Down Expand Up @@ -98,7 +116,9 @@ namespace eosio { namespace vm {
sigaddset(&sa.sa_mask, SIGPROF);
sa.sa_flags = SA_NODEFER | SA_SIGINFO;
sigaction(SIGSEGV, &sa, &prev_signal_handler<SIGSEGV>);
#ifndef __linux__
sigaction(SIGBUS, &sa, &prev_signal_handler<SIGBUS>);
#endif
sigaction(SIGFPE, &sa, &prev_signal_handler<SIGFPE>);
}

Expand All @@ -114,16 +134,17 @@ namespace eosio { namespace vm {
/// with non-trivial destructors, then it must mask the relevant signals
/// during the lifetime of these objects or the behavior is undefined.
///
/// signals handled: SIGSEGV, SIGBUS, SIGFPE
/// signals handled: SIGSEGV, SIGBUS (except on Linux), SIGFPE
///
// Make this noinline to prevent possible corruption of the caller's local variables.
// It's unlikely, but I'm not sure that it can definitely be ruled out if both
// this and f are inlined and f modifies locals from the caller.
template<typename F, typename E>
[[gnu::noinline]] auto invoke_with_signal_handler(F&& f, E&& e) {
[[gnu::noinline]] auto invoke_with_signal_handler(F&& f, E&& e, const std::vector<std::span<std::byte>>& protect_ranges) {
setup_signal_handler();
sigjmp_buf dest;
sigjmp_buf* volatile old_signal_handler = nullptr;
const auto old_protected_memory_ranges = protected_memory_ranges.exchange(&protect_ranges);
int sig;
if((sig = sigsetjmp(dest, 1)) == 0) {
// Note: Cannot use RAII, as non-trivial destructors w/ longjmp
Expand All @@ -145,13 +166,16 @@ namespace eosio { namespace vm {
f();
pthread_sigmask(SIG_SETMASK, &old_sigmask, nullptr);
std::atomic_store(&signal_dest, old_signal_handler);
std::atomic_store(&protected_memory_ranges, old_protected_memory_ranges);
} catch(...) {
pthread_sigmask(SIG_SETMASK, &old_sigmask, nullptr);
std::atomic_store(&signal_dest, old_signal_handler);
std::atomic_store(&protected_memory_ranges, old_protected_memory_ranges);
throw;
}
} else {
std::atomic_store(&signal_dest, old_signal_handler);
std::atomic_store(&protected_memory_ranges, old_protected_memory_ranges);
if (sig == -1) {
std::exception_ptr exception = std::move(saved_exception);
saved_exception = nullptr;
Expand Down
8 changes: 6 additions & 2 deletions tests/signals_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ TEST_CASE("Testing signals", "[invoke_with_signal_handler]") {
std::raise(SIGSEGV);
}, [](int sig) {
throw test_exception{};
});
}, {});
} catch(test_exception&) {
okay = true;
}
Expand All @@ -25,7 +25,7 @@ TEST_CASE("Testing signals", "[invoke_with_signal_handler]") {
TEST_CASE("Testing throw", "[signal_handler_throw]") {
CHECK_THROWS_AS(eosio::vm::invoke_with_signal_handler([](){
eosio::vm::throw_<eosio::vm::wasm_exit_exception>( "Exiting" );
}, [](int){}), eosio::vm::wasm_exit_exception);
}, [](int){}, {}), eosio::vm::wasm_exit_exception);
}

static volatile sig_atomic_t sig_handled;
Expand Down Expand Up @@ -54,9 +54,11 @@ TEST_CASE("Test signal handler forwarding", "[signal_handler_forward]") {
sig_handled = 0;
std::raise(SIGSEGV);
CHECK(sig_handled == 42 + SIGSEGV);
#ifndef __linux__
sig_handled = 0;
std::raise(SIGBUS);
CHECK(sig_handled == 42 + SIGBUS);
#endif
sig_handled = 0;
std::raise(SIGFPE);
CHECK(sig_handled == 42 + SIGFPE);
Expand All @@ -73,9 +75,11 @@ TEST_CASE("Test signal handler forwarding", "[signal_handler_forward]") {
sig_handled = 0;
std::raise(SIGSEGV);
CHECK(sig_handled == 142 + SIGSEGV);
#ifndef __linux__
sig_handled = 0;
std::raise(SIGBUS);
CHECK(sig_handled == 142 + SIGBUS);
#endif
sig_handled = 0;
std::raise(SIGFPE);
CHECK(sig_handled == 142 + SIGFPE);
Expand Down

0 comments on commit 5170cdf

Please sign in to comment.