Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
executor.cpp
Go to the documentation of this file.
11#include <sysio/chain/types.hpp>
13
14#include <fc/scoped_exit.hpp>
15
16#include <boost/hana/equal.hpp>
17
18#include <asm/prctl.h>
19#include <sys/prctl.h>
20#include <sys/syscall.h>
21#include <sys/mman.h>
22
23#if defined(__has_feature)
24#if __has_feature(shadow_call_stack)
25#error SYS VM OC is not compatible with Clang ShadowCallStack
26#endif
27#endif
28
29extern "C" int arch_prctl(int code, unsigned long* addr);
30
31namespace sysio { namespace chain { namespace eosvmoc {
32
33static constexpr auto signal_sentinel = 0x4D56534F45534559ul;
34
35static void(*chained_handler)(int,siginfo_t*,void*);
36static void segv_handler(int sig, siginfo_t* info, void* ctx) {
37 control_block* cb_in_main_segment;
38
39 //a 0 GS value is an indicator an executor hasn't been active on this thread recently
40 uint64_t current_gs;
41 syscall(SYS_arch_prctl, ARCH_GET_GS, &current_gs);
42 if(current_gs == 0)
43 goto notus;
44
45 cb_in_main_segment = reinterpret_cast<control_block*>(current_gs - memory::cb_offset);
46
47 //as a double check that the control block pointer is what we expect, look for the magic
48 if(cb_in_main_segment->magic != signal_sentinel)
49 goto notus;
50
51 //was wasm running? If not, this SEGV was not due to us
52 if(cb_in_main_segment->is_running == false)
53 goto notus;
54
55 //was the segfault within code?
56 if((uintptr_t)info->si_addr >= cb_in_main_segment->execution_thread_code_start &&
57 (uintptr_t)info->si_addr < cb_in_main_segment->execution_thread_code_start+cb_in_main_segment->execution_thread_code_length)
58 siglongjmp(*cb_in_main_segment->jmp, SYSVMOC_EXIT_CHECKTIME_FAIL);
59
60 //was the segfault within data?
61 if((uintptr_t)info->si_addr >= cb_in_main_segment->execution_thread_memory_start &&
62 (uintptr_t)info->si_addr < cb_in_main_segment->execution_thread_memory_start+cb_in_main_segment->execution_thread_memory_length)
63 siglongjmp(*cb_in_main_segment->jmp, SYSVMOC_EXIT_SEGV);
64
65notus:
66 if(chained_handler) {
67 chained_handler(sig, info, ctx);
68 return;
69 }
70 ::signal(sig, SIG_DFL);
71 ::raise(sig);
72 __builtin_unreachable();
73}
74
75static intrinsic grow_memory_intrinsic SYSVMOC_INTRINSIC_INIT_PRIORITY("eosvmoc_internal.grow_memory", IR::FunctionType::get(IR::ResultType::i32,{IR::ValueType::i32,IR::ValueType::i32}),
77 std::integral_constant<std::size_t, find_intrinsic_index("eosvmoc_internal.grow_memory")>::value
78);
79
80//This is effectively overriding the sysio_exit intrinsic in wasm_interface
81static void sysio_exit(int32_t code) {
83 __builtin_unreachable();
84}
85static intrinsic sysio_exit_intrinsic("env.sysio_exit", IR::FunctionType::get(IR::ResultType::none,{IR::ValueType::i32}), (void*)&sysio_exit,
86 std::integral_constant<std::size_t, find_intrinsic_index("env.sysio_exit")>::value
87);
88
89static void throw_internal_exception(const char* const s) {
90 *reinterpret_cast<std::exception_ptr*>(eos_vm_oc_get_exception_ptr()) = std::make_exception_ptr(wasm_execution_error(FC_LOG_MESSAGE(error, s)));
92 __builtin_unreachable();
93}
94
95#define DEFINE_SYSVMOC_TRAP_INTRINSIC(module,name) \
96 void name(); \
97 static intrinsic name##Function SYSVMOC_INTRINSIC_INIT_PRIORITY(#module "." #name,IR::FunctionType::get(),(void*)&name, \
98 std::integral_constant<std::size_t, find_intrinsic_index(#module "." #name)>::value \
99 ); \
100 void name()
101
102DEFINE_SYSVMOC_TRAP_INTRINSIC(eosvmoc_internal,depth_assert) {
103 throw_internal_exception("Exceeded call depth maximum");
104}
105
106DEFINE_SYSVMOC_TRAP_INTRINSIC(eosvmoc_internal,div0_or_overflow) {
107 throw_internal_exception("Division by 0 or integer overflow trapped");
108}
109
110DEFINE_SYSVMOC_TRAP_INTRINSIC(eosvmoc_internal,indirect_call_mismatch) {
111 throw_internal_exception("Indirect call function type mismatch");
112}
113
114DEFINE_SYSVMOC_TRAP_INTRINSIC(eosvmoc_internal,indirect_call_oob) {
115 throw_internal_exception("Indirect call index out of bounds");
116}
117
118DEFINE_SYSVMOC_TRAP_INTRINSIC(eosvmoc_internal,unreachable) {
119 throw_internal_exception("Unreachable reached");
120}
121
124 struct sigaction sig_action, old_sig_action;
125 sig_action.sa_sigaction = segv_handler;
126 sigemptyset(&sig_action.sa_mask);
127 sig_action.sa_flags = SA_SIGINFO | SA_NODEFER;
128 sigaction(SIGSEGV, &sig_action, &old_sig_action);
129 if(old_sig_action.sa_flags & SA_SIGINFO)
130 chained_handler = old_sig_action.sa_sigaction;
131 else if(old_sig_action.sa_handler != SIG_IGN && old_sig_action.sa_handler != SIG_DFL)
132 chained_handler = (void (*)(int,siginfo_t*,void*))old_sig_action.sa_handler;
133 }
134};
135
137 //if we're the first executor created, go setup the signal handling. For now we'll just leave this attached forever
138 static executor_signal_init the_executor_signal_init;
139
140 uint64_t current_gs;
141 if(arch_prctl(ARCH_GET_GS, &current_gs) || current_gs)
142 wlog("x86_64 GS register is not set as expected. SYS VM OC may not run correctly on this platform");
143
144 struct stat s;
145 FC_ASSERT(fstat(cc.fd(), &s) == 0, "executor failed to get code cache size");
146 code_mapping = (uint8_t*)mmap(nullptr, s.st_size, PROT_EXEC|PROT_READ, MAP_SHARED, cc.fd(), 0);
147 FC_ASSERT(code_mapping != MAP_FAILED, "failed to map code cache in to executor");
148 code_mapping_size = s.st_size;
149 mapping_is_executable = true;
150}
151
153 if(mapping_is_executable == false) {
154 mprotect(code_mapping, code_mapping_size, PROT_EXEC|PROT_READ);
155 mapping_is_executable = true;
156 }
157
159 uint64_t max_pages = sysio::chain::wasm_constraints::maximum_linear_memory/sysio::chain::wasm_constraints::wasm_page_size;
160 if(context.control.is_builtin_activated(builtin_protocol_feature_t::configurable_wasm_limits)) {
161 const wasm_config& config = context.control.get_global_properties().wasm_configuration;
162 max_call_depth = config.max_call_depth;
163 max_pages = config.max_pages;
164 }
165 stack.reset(max_call_depth);
166 SYS_ASSERT(code.starting_memory_pages <= (int)max_pages, wasm_execution_error, "Initial memory out of range");
167
168 //prepare initial memory, mutable globals, and table data
169 if(code.starting_memory_pages > 0 ) {
170 uint64_t initial_page_offset = std::min(static_cast<std::size_t>(code.starting_memory_pages), mem.size_of_memory_slice_mapping()/memory::stride - 1);
171 if(initial_page_offset < static_cast<uint64_t>(code.starting_memory_pages)) {
172 mprotect(mem.full_page_memory_base() + initial_page_offset * sysio::chain::wasm_constraints::wasm_page_size,
173 (code.starting_memory_pages - initial_page_offset) * sysio::chain::wasm_constraints::wasm_page_size, PROT_READ | PROT_WRITE);
174 }
175 arch_prctl(ARCH_SET_GS, (unsigned long*)(mem.zero_page_memory_base()+initial_page_offset*memory::stride));
176 memset(mem.full_page_memory_base(), 0, 64u*1024u*code.starting_memory_pages);
177 }
178 else
179 arch_prctl(ARCH_SET_GS, (unsigned long*)mem.zero_page_memory_base());
180
181 void* globals;
182 if(code.initdata_prologue_size > memory::max_prologue_size) {
183 globals_buffer.resize(code.initdata_prologue_size);
184 memcpy(globals_buffer.data(), code_mapping + code.initdata_begin, code.initdata_prologue_size);
186 code_mapping + code.initdata_begin + code.initdata_prologue_size - memory::max_prologue_size,
187 code.initdata_size - code.initdata_prologue_size + memory::max_prologue_size);
188 globals = globals_buffer.data() + globals_buffer.size();
189 } else {
190 memcpy(mem.full_page_memory_base() - code.initdata_prologue_size, code_mapping + code.initdata_begin, code.initdata_size);
191 globals = mem.full_page_memory_base();
192 }
193
194 control_block* const cb = mem.get_control_block();
195 cb->magic = signal_sentinel;
196 cb->execution_thread_code_start = (uintptr_t)code_mapping;
197 cb->execution_thread_code_length = code_mapping_size;
200 cb->ctx = &context;
201 executors_exception_ptr = nullptr;
202 cb->eptr = &executors_exception_ptr;
203 cb->current_call_depth_remaining = max_call_depth + 1;
204 cb->current_linear_memory_pages = code.starting_memory_pages;
205 cb->max_linear_memory_pages = max_pages;
206 cb->first_invalid_memory_address = code.starting_memory_pages*64*1024;
208 cb->jmp = &executors_sigjmp_buf;
209 cb->bounce_buffers = &executors_bounce_buffers;
210 cb->running_code_base = (uintptr_t)(code_mapping + code.code_begin);
211 cb->is_running = true;
212 cb->globals = globals;
213
214 context.trx_context.transaction_timer.set_expiration_callback([](void* user) {
215 executor* self = (executor*)user;
216 syscall(SYS_mprotect, self->code_mapping, self->code_mapping_size, PROT_NONE);
217 self->mapping_is_executable = false;
218 }, this);
219 context.trx_context.checktime(); //catch any expiration that might have occurred before setting up callback
220
221 auto cleanup = fc::make_scoped_exit([cb, &tt=context.trx_context.transaction_timer, &mem=mem](){
222 cb->is_running = false;
223 cb->bounce_buffers->clear();
224 tt.set_expiration_callback(nullptr, nullptr);
225
226 uint64_t base_pages = mem.size_of_memory_slice_mapping()/memory::stride - 1;
227 if(cb->current_linear_memory_pages > base_pages) {
228 mprotect(mem.full_page_memory_base() + base_pages * sysio::chain::wasm_constraints::wasm_page_size,
229 (cb->current_linear_memory_pages - base_pages) * sysio::chain::wasm_constraints::wasm_page_size, PROT_NONE);
230 }
231 });
232
233 void(*apply_func)(uint64_t, uint64_t, uint64_t) = (void(*)(uint64_t, uint64_t, uint64_t))(cb->running_code_base + code.apply_offset);
234
235 switch(sigsetjmp(*cb->jmp, 0)) {
236 case 0:
237 stack.run([&]{
238 std::visit(overloaded {
239 [&](const no_offset&) {},
240 [&](const intrinsic_ordinal& i) {
241 void(*start_func)() = (void(*)())(*(uintptr_t*)((uintptr_t)mem.zero_page_memory_base() - memory::first_intrinsic_offset - i.ordinal*8));
242 start_func();
243 },
244 [&](const code_offset& offs) {
245 void(*start_func)() = (void(*)())(cb->running_code_base + offs.offset);
246 start_func();
247 }
248 }, code.start);
249 apply_func(context.get_receiver().to_uint64_t(), context.get_action().account.to_uint64_t(), context.get_action().name.to_uint64_t());
250 });
251 break;
252 //case 1: clean sysio_exit
254 context.trx_context.checktime();
255 break;
257 SYS_ASSERT(false, wasm_execution_error, "access violation");
258 break;
259 case SYSVMOC_EXIT_EXCEPTION: //exception
260 std::rethrow_exception(*cb->eptr);
261 break;
262 }
263}
264
265executor::~executor() {
266 arch_prctl(ARCH_SET_GS, nullptr);
267}
268
269}}}
#define SYS_ASSERT(expr, exc_type, FORMAT,...)
Definition exceptions.hpp:7
void execute(const code_descriptor &code, memory &mem, apply_context &context)
Definition executor.cpp:152
executor(const code_cache_base &cc)
Definition executor.cpp:136
static constexpr size_t stride
Definition memory.hpp:44
uint8_t *const full_page_memory_base() const
Definition memory.hpp:34
static constexpr uintptr_t first_intrinsic_offset
Definition memory.hpp:49
uint8_t *const zero_page_memory_base() const
Definition memory.hpp:33
uint8_t *const start_of_memory_slices() const
Definition memory.hpp:39
static constexpr uintptr_t cb_offset
Definition memory.hpp:48
control_block *const get_control_block() const
Definition memory.hpp:36
size_t size_of_memory_slice_mapping() const
Definition memory.hpp:40
static constexpr uintptr_t max_prologue_size
Definition memory.hpp:51
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
int arch_prctl(int code, unsigned long *addr)
#define DEFINE_SYSVMOC_TRAP_INTRINSIC(module, name)
Definition executor.cpp:95
sigjmp_buf * eos_vm_oc_get_jmp_buf()
void * eos_vm_oc_get_exception_ptr()
int32_t eos_vm_oc_grow_memory(int32_t grow, int32_t max)
#define FC_LOG_MESSAGE(LOG_LEVEL, FORMAT,...)
A helper method for generating log messages.
#define wlog(FORMAT,...)
Definition logger.hpp:124
scoped_exit< Callback > make_scoped_exit(Callback &&c)
constexpr std::size_t find_intrinsic_index(std::string_view hf)
eos_vm_oc_control_block control_block
Definition sys-vm-oc.hpp:21
@ self
the connection is to itself
Definition protocol.hpp:48
#define value
Definition pkcs11.h:157
sysio::client::http::http_context context
Definition main.cpp:200
_W64 unsigned int uintptr_t
Definition stdint.h:165
signed int int32_t
Definition stdint.h:123
unsigned char uint8_t
Definition stdint.h:124
unsigned __int64 uint64_t
Definition stdint.h:136
static IR_API const FunctionType * get(ResultType ret, const std::initializer_list< ValueType > &parameters)
Definition Types.cpp:38
int64_t current_linear_memory_pages
Definition sys-vm-oc.h:27
uintptr_t running_code_base
Definition sys-vm-oc.h:35
unsigned current_call_depth_remaining
Definition sys-vm-oc.h:26
char * full_linear_memory_start
Definition sys-vm-oc.h:28
size_t execution_thread_code_length
Definition sys-vm-oc.h:16
int64_t max_linear_memory_pages
Definition sys-vm-oc.h:38
size_t execution_thread_memory_length
Definition sys-vm-oc.h:18
uintptr_t execution_thread_code_start
Definition sys-vm-oc.h:15
uintptr_t execution_thread_memory_start
Definition sys-vm-oc.h:17
int64_t first_invalid_memory_address
Definition sys-vm-oc.h:36
void reset(std::size_t max_call_depth)
Definition stack.cpp:7
#define SYSVMOC_INTRINSIC_INIT_PRIORITY
Definition sys-vm-oc.hpp:62
char * s
memset(pInfo->slotDescription, ' ', 64)
memcpy((char *) pInfo->slotDescription, s, l)