v8/test/cctest/wasm/test-wasm-trap-position.cc

141 lines
5.5 KiB
C++
Raw Normal View History

// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/assembler-inl.h"
[wasm] Initial signal handler This is basically the minimum viable signal handler for Wasm bounds checks. It includes the TLS check and the fine grained instructions checks. These two checks provide most of the safety for the signal handler. Future CLs will add code range and data range checks for more robustness. The trap handling code and data structures are all in src/trap-handler, with the code that actually runs in the signal handler confined to src/trap-handler/signal-handler.cc. This changes adds a new V8 API that the embedder should call from a signal handler that will give V8 the chance to handle the fault first. For hosts that do not want to implement their own signal handler, we include the option to install a simple one. This simple handler is also used for the tests. When a Wasm module is instantiated, information about each function is passed to the trap handler, which is used to classify faults. These are removed during the instance finalizer. Several future enhancements are planned before turning this on by default. Obviously, the additional checks will be added to MaybeHandleFault. We are also planning to add a two-level CodeObjectData table that is grouped by isolates to make cleanup easier and also reduce potential for contending on a single data structure. BUG= https://bugs.chromium.org/p/v8/issues/detail?id=5277 Review-Url: https://codereview.chromium.org/2371833007 Cr-Original-Original-Commit-Position: refs/heads/master@{#43523} Committed: https://chromium.googlesource.com/v8/v8/+/a5af7fe9ee388a636675f4a6872b1d34fa7d1a7a Review-Url: https://codereview.chromium.org/2371833007 Cr-Original-Commit-Position: refs/heads/master@{#43755} Committed: https://chromium.googlesource.com/v8/v8/+/338622d7cae787a63cece1f2e79a8b030023940b Review-Url: https://codereview.chromium.org/2371833007 Cr-Commit-Position: refs/heads/master@{#43759}
2017-03-13 22:12:23 +00:00
#include "src/trap-handler/trap-handler.h"
#include "test/cctest/cctest.h"
#include "test/cctest/compiler/value-helper.h"
#include "test/cctest/wasm/wasm-run-utils.h"
#include "test/common/wasm/test-signatures.h"
#include "test/common/wasm/wasm-macro-gen.h"
using namespace v8::base;
using namespace v8::internal;
using namespace v8::internal::compiler;
using namespace v8::internal::wasm;
using v8::Local;
using v8::Utils;
namespace {
#define CHECK_CSTREQ(exp, found) \
do { \
const char* exp_ = (exp); \
const char* found_ = (found); \
DCHECK_NOT_NULL(exp); \
if (V8_UNLIKELY(found_ == nullptr || strcmp(exp_, found_) != 0)) { \
V8_Fatal(__FILE__, __LINE__, \
"Check failed: (%s) != (%s) ('%s' vs '%s').", #exp, #found, \
exp_, found_ ? found_ : "<null>"); \
} \
} while (0)
struct ExceptionInfo {
const char* func_name;
int line_nr;
int column;
};
template <int N>
void CheckExceptionInfos(Handle<Object> exc,
const ExceptionInfo (&excInfos)[N]) {
// Check that it's indeed an Error object.
CHECK(exc->IsJSError());
exc->Print();
// Extract stack frame from the exception.
Local<v8::Value> localExc = Utils::ToLocal(exc);
v8::Local<v8::StackTrace> stack = v8::Exception::GetStackTrace(localExc);
CHECK(!stack.IsEmpty());
CHECK_EQ(N, stack->GetFrameCount());
for (int frameNr = 0; frameNr < N; ++frameNr) {
v8::Local<v8::StackFrame> frame = stack->GetFrame(frameNr);
v8::String::Utf8Value funName(frame->GetFunctionName());
CHECK_CSTREQ(excInfos[frameNr].func_name, *funName);
CHECK_EQ(excInfos[frameNr].line_nr, frame->GetLineNumber());
CHECK_EQ(excInfos[frameNr].column, frame->GetColumn());
}
}
} // namespace
// Trigger a trap for executing unreachable.
TEST(Unreachable) {
// Create a WasmRunner with stack checks and traps enabled.
WasmRunner<void> r(kExecuteCompiled, "main", true);
TestSignatures sigs;
BUILD(r, WASM_UNREACHABLE);
uint32_t wasm_index = r.function()->func_index;
Handle<JSFunction> js_wasm_wrapper = r.module().WrapCode(wasm_index);
Handle<JSFunction> js_trampoline = Handle<JSFunction>::cast(
v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
CompileRun("(function callFn(fn) { fn(); })"))));
Isolate* isolate = js_wasm_wrapper->GetIsolate();
isolate->SetCaptureStackTraceForUncaughtExceptions(true, 10,
v8::StackTrace::kOverview);
Handle<Object> global(isolate->context()->global_object(), isolate);
MaybeHandle<Object> maybe_exc;
Handle<Object> args[] = {js_wasm_wrapper};
MaybeHandle<Object> returnObjMaybe =
Execution::TryCall(isolate, js_trampoline, global, 1, args,
Execution::MessageHandling::kReport, &maybe_exc);
CHECK(returnObjMaybe.is_null());
// Line and column are 1-based, so add 1 for the expected wasm output.
ExceptionInfo expected_exceptions[] = {
{"main", static_cast<int>(wasm_index) + 1, 2}, // --
{"callFn", 1, 24} // --
};
CheckExceptionInfos(maybe_exc.ToHandleChecked(), expected_exceptions);
}
// Trigger a trap for loading from out-of-bounds.
TEST(IllegalLoad) {
WasmRunner<void> r(kExecuteCompiled, "main", true);
TestSignatures sigs;
r.module().AddMemory(0L);
BUILD(r, WASM_IF(WASM_ONE, WASM_SEQ(WASM_LOAD_MEM(MachineType::Int32(),
WASM_I32V_1(-3)),
WASM_DROP)));
uint32_t wasm_index_1 = r.function()->func_index;
WasmFunctionCompiler& f2 = r.NewFunction<void>("call_main");
// Insert a NOP such that the position of the call is not one.
BUILD(f2, WASM_NOP, WASM_CALL_FUNCTION0(wasm_index_1));
uint32_t wasm_index_2 = f2.function_index();
Handle<JSFunction> js_wasm_wrapper = r.module().WrapCode(wasm_index_2);
Handle<JSFunction> js_trampoline = Handle<JSFunction>::cast(
v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
CompileRun("(function callFn(fn) { fn(); })"))));
Isolate* isolate = js_wasm_wrapper->GetIsolate();
isolate->SetCaptureStackTraceForUncaughtExceptions(true, 10,
v8::StackTrace::kOverview);
Handle<Object> global(isolate->context()->global_object(), isolate);
MaybeHandle<Object> maybe_exc;
Handle<Object> args[] = {js_wasm_wrapper};
MaybeHandle<Object> returnObjMaybe =
Execution::TryCall(isolate, js_trampoline, global, 1, args,
Execution::MessageHandling::kReport, &maybe_exc);
CHECK(returnObjMaybe.is_null());
// Line and column are 1-based, so add 1 for the expected wasm output.
ExceptionInfo expected_exceptions[] = {
{"main", static_cast<int>(wasm_index_1) + 1, 8}, // --
{"call_main", static_cast<int>(wasm_index_2) + 1, 3}, // --
{"callFn", 1, 24} // --
};
CheckExceptionInfos(maybe_exc.ToHandleChecked(), expected_exceptions);
}