LibWasm: Correctly handle failed validation
This commit is contained in:
parent
1f9bb13115
commit
68f6c03a67
5 changed files with 49 additions and 50 deletions
|
|
@ -190,43 +190,6 @@ static void install_compiled_fault_handlers() { }
|
|||
|
||||
#endif
|
||||
|
||||
class ScopedCompiledFaultRecovery {
|
||||
public:
|
||||
ScopedCompiledFaultRecovery(Wasm::BytecodeInterpreter& interpreter, Wasm::Configuration& configuration)
|
||||
{
|
||||
m_context.interpreter = &interpreter;
|
||||
m_context.configuration = &configuration;
|
||||
m_context.previous = s_compiled_fault_recovery;
|
||||
}
|
||||
|
||||
~ScopedCompiledFaultRecovery()
|
||||
{
|
||||
if (m_armed)
|
||||
s_compiled_fault_recovery = m_context.previous;
|
||||
}
|
||||
|
||||
bool arm()
|
||||
{
|
||||
install_compiled_fault_handlers();
|
||||
s_compiled_fault_recovery = &m_context;
|
||||
m_armed = true;
|
||||
if (setjmp(m_context.jump_buffer) != 0) {
|
||||
// Disarm immediately after longjmp return; the compiled code may
|
||||
// have corrupted our stack frame, so the destructor must be a no-op.
|
||||
s_compiled_fault_recovery = m_context.previous;
|
||||
m_armed = false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool faulted() const { return m_context.faulted; }
|
||||
|
||||
private:
|
||||
CompiledFaultRecoveryContext m_context;
|
||||
bool m_armed { false };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#ifdef AK_COMPILER_CLANG
|
||||
|
|
@ -354,10 +317,17 @@ void BytecodeInterpreter::interpret(Configuration& configuration)
|
|||
{
|
||||
m_trap = Empty {};
|
||||
auto& expression = configuration.frame().expression();
|
||||
Optional<ScopedCompiledFaultRecovery> compiled_fault_recovery;
|
||||
CompiledFaultRecoveryContext compiled_fault_recovery;
|
||||
bool did_install_compiled_fault_recovery = false;
|
||||
if (expression.compiled_instructions.cranelift_compiled && !s_compiled_fault_recovery) {
|
||||
compiled_fault_recovery.emplace(*this, configuration);
|
||||
if (!compiled_fault_recovery->arm()) {
|
||||
install_compiled_fault_handlers();
|
||||
compiled_fault_recovery.interpreter = this;
|
||||
compiled_fault_recovery.configuration = &configuration;
|
||||
compiled_fault_recovery.previous = s_compiled_fault_recovery;
|
||||
s_compiled_fault_recovery = &compiled_fault_recovery;
|
||||
did_install_compiled_fault_recovery = true;
|
||||
if (setjmp(compiled_fault_recovery.jump_buffer) != 0) {
|
||||
s_compiled_fault_recovery = compiled_fault_recovery.previous;
|
||||
m_trap = Trap::from_string("Memory access out of bounds");
|
||||
return;
|
||||
}
|
||||
|
|
@ -365,15 +335,25 @@ void BytecodeInterpreter::interpret(Configuration& configuration)
|
|||
auto const should_limit_instruction_count = configuration.should_limit_instruction_count();
|
||||
if (!expression.compiled_instructions.dispatches.is_empty()) {
|
||||
if (expression.compiled_instructions.direct) {
|
||||
if (should_limit_instruction_count)
|
||||
return interpret_impl<true, true, true>(configuration, expression);
|
||||
return interpret_impl<true, false, true>(configuration, expression);
|
||||
if (should_limit_instruction_count) {
|
||||
interpret_impl<true, true, true>(configuration, expression);
|
||||
goto done;
|
||||
}
|
||||
interpret_impl<true, false, true>(configuration, expression);
|
||||
goto done;
|
||||
}
|
||||
return interpret_impl<true, false, false>(configuration, expression);
|
||||
interpret_impl<true, false, false>(configuration, expression);
|
||||
goto done;
|
||||
}
|
||||
if (should_limit_instruction_count)
|
||||
return interpret_impl<false, true, false>(configuration, expression);
|
||||
return interpret_impl<false, false, false>(configuration, expression);
|
||||
if (should_limit_instruction_count) {
|
||||
interpret_impl<false, true, false>(configuration, expression);
|
||||
goto done;
|
||||
}
|
||||
interpret_impl<false, false, false>(configuration, expression);
|
||||
|
||||
done:
|
||||
if (did_install_compiled_fault_recovery)
|
||||
s_compiled_fault_recovery = compiled_fault_recovery.previous;
|
||||
}
|
||||
|
||||
constexpr static u32 default_sources_and_destination = (to_underlying(Dispatch::RegisterOrStack::Stack) | (to_underlying(Dispatch::RegisterOrStack::Stack) << 2) | (to_underlying(Dispatch::RegisterOrStack::Stack) << 4));
|
||||
|
|
|
|||
|
|
@ -287,7 +287,10 @@ ErrorOr<void, ValidationError> Validator::validate(CodeSection const& section)
|
|||
|
||||
ScopeGuard cleanup = [&] {
|
||||
auto cranelift_start = MonotonicTime::now();
|
||||
flush_cranelift_batch();
|
||||
if (validation_succeeded)
|
||||
flush_cranelift_batch();
|
||||
else
|
||||
discard_cranelift_batch();
|
||||
auto cranelift_duration = MonotonicTime::now() - cranelift_start;
|
||||
|
||||
if (installing)
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ struct CacheState {
|
|||
Vector<BatchInput> pending_batch;
|
||||
};
|
||||
|
||||
static CacheState s_cranelift_cache_state;
|
||||
static thread_local CacheState s_cranelift_cache_state;
|
||||
static thread_local u32 s_active_function_index = NumericLimits<u32>::max();
|
||||
|
||||
static u64 compute_layout_hash(RuntimeHelpers const& h)
|
||||
|
|
@ -775,7 +775,7 @@ static ALWAYS_INLINE i32 wasm_cl_direct_call_impl(BytecodeInterpreter& interpret
|
|||
auto const& entry = (*table)[index];
|
||||
|
||||
if (config.depth() > 500) [[unlikely]] {
|
||||
interpreter.set_trap("call stack exhausted"sv);
|
||||
interpreter.set_trap(Constants::stack_exhaustion_message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -1195,6 +1195,8 @@ static void try_cranelift_compile_batch(Vector<BatchInput>& batch)
|
|||
|
||||
auto code_offset = static_cast<size_t>(output->code_offset);
|
||||
auto code_size = static_cast<size_t>(output->code_size);
|
||||
if (code_offset > code_region_size || code_size > code_region_size - code_offset)
|
||||
continue;
|
||||
auto code_start = code_base_offset + code_offset;
|
||||
if (code_start + code_size > total_size)
|
||||
continue;
|
||||
|
|
@ -1202,6 +1204,10 @@ static void try_cranelift_compile_batch(Vector<BatchInput>& batch)
|
|||
auto const reloc_offset = static_cast<size_t>(output->reloc_offset);
|
||||
auto const reloc_count = static_cast<size_t>(output->reloc_count);
|
||||
auto const reloc_bytes = reloc_count * sizeof(HelperReloc);
|
||||
if (reloc_count != 0 && reloc_bytes / sizeof(HelperReloc) != reloc_count)
|
||||
continue;
|
||||
if (reloc_offset > reloc_region_size || reloc_bytes > reloc_region_size - reloc_offset)
|
||||
continue;
|
||||
if (reloc_region_start + reloc_offset + reloc_bytes > total_size)
|
||||
continue;
|
||||
|
||||
|
|
@ -1245,6 +1251,9 @@ bool try_cranelift_compile(CompiledInstructions& compiled, u32 result_arity)
|
|||
if (compiled.cranelift_compiled)
|
||||
return true;
|
||||
|
||||
if (s_active_function_index == NumericLimits<u32>::max())
|
||||
return false;
|
||||
|
||||
// Cache hit: install from the parsed blob instead of going through cranelift.
|
||||
// dispatches[] has just been populated by try_compile_instructions, so handler_ptr is ready to be set.
|
||||
if (s_cranelift_cache_state.pending_install.active && s_active_function_index != NumericLimits<u32>::max()) {
|
||||
|
|
@ -1388,6 +1397,11 @@ void flush_cranelift_batch()
|
|||
s_cranelift_cache_state.pending_batch.clear();
|
||||
}
|
||||
|
||||
void discard_cranelift_batch()
|
||||
{
|
||||
s_cranelift_cache_state.pending_batch.clear();
|
||||
}
|
||||
|
||||
void free_cranelift_code(void* handle)
|
||||
{
|
||||
if (handle) {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ namespace Wasm {
|
|||
|
||||
bool try_cranelift_compile(CompiledInstructions&, u32) { return false; }
|
||||
void flush_cranelift_batch() { }
|
||||
void discard_cranelift_batch() { }
|
||||
void free_cranelift_code(void*) { }
|
||||
void set_cranelift_active_function_index(u32) { }
|
||||
void begin_cranelift_cache_capture() { }
|
||||
|
|
|
|||
|
|
@ -1548,6 +1548,7 @@ private:
|
|||
CompiledInstructions try_compile_instructions(Expression const&, Span<FunctionType const> functions);
|
||||
bool try_cranelift_compile(CompiledInstructions& compiled, u32 result_arity = 0);
|
||||
void flush_cranelift_batch();
|
||||
void discard_cranelift_batch();
|
||||
|
||||
// Caller-supplied hooks for the Cranelift on-disk cache.
|
||||
// - `wasm_hash` is a 32-byte digest of the wasm bytes; embedded in produced blobs
|
||||
|
|
|
|||
Loading…
Reference in a new issue