[wasm] Make traps non-catchable

The spec was changed such that traps are not catchable in wasm:
https://github.com/WebAssembly/exception-handling/pull/93

This CL implements this in V8 by adding a private symbol as a property
to all uncatchable exceptions. It also adds a number of tests.

R=jkummerow@chromium.org
CC=aheejin@chromium.org

Bug: v8:10194
Change-Id: I498531762e8876f809d3b8aeb72ccc053e0e3cd4
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2113375
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66824}
This commit is contained in:
Clemens Backes 2020-03-23 13:56:45 +01:00 committed by Commit Bot
parent 033118eea6
commit 31846fae92
11 changed files with 206 additions and 79 deletions

View File

@ -83,6 +83,18 @@ bool Isolate::is_catchable_by_javascript(Object exception) {
return exception != ReadOnlyRoots(heap()).termination_exception();
}
bool Isolate::is_catchable_by_wasm(Object exception) {
if (!is_catchable_by_javascript(exception)) return false;
if (!exception.IsJSObject()) return true;
// We don't allocate, but the LookupIterator interface expects a handle.
DisallowHeapAllocation no_gc;
HandleScope handle_scope(this);
LookupIterator it(this, handle(JSReceiver::cast(exception), this),
factory()->wasm_uncatchable_symbol(),
LookupIterator::OWN_SKIP_INTERCEPTOR);
return !JSReceiver::HasProperty(&it).FromJust();
}
void Isolate::FireBeforeCallEnteredCallback() {
for (auto& callback : before_call_entered_callbacks_) {
callback(reinterpret_cast<v8::Isolate*>(this));

View File

@ -1614,6 +1614,7 @@ Object Isolate::UnwindAndFindHandler() {
// Special handling of termination exceptions, uncatchable by JavaScript and
// Wasm code, we unwind the handlers until the top ENTRY handler is found.
bool catchable_by_js = is_catchable_by_javascript(exception);
bool catchable_by_wasm = is_catchable_by_wasm(exception);
// Compute handler and stack unwinding information by performing a full walk
// over the stack and dispatching according to the frame type.
@ -1664,8 +1665,9 @@ Object Isolate::UnwindAndFindHandler() {
trap_handler::ClearThreadInWasm();
}
if (!catchable_by_wasm) break;
// For WebAssembly frames we perform a lookup in the handler table.
if (!catchable_by_js) break;
// This code ref scope is here to avoid a check failure when looking up
// the code. It's not actually necessary to keep the code alive as it's
// currently being executed.

View File

@ -653,6 +653,7 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
bool IsExternalHandlerOnTop(Object exception);
inline bool is_catchable_by_javascript(Object exception);
inline bool is_catchable_by_wasm(Object exception);
// JS execution stack (see frames.h).
static Address c_entry_fp(ThreadLocalTop* thread) {

View File

@ -359,6 +359,7 @@
V(_, strict_function_transition_symbol) \
V(_, wasm_exception_tag_symbol) \
V(_, wasm_exception_values_symbol) \
V(_, wasm_uncatchable_symbol) \
V(_, uninitialized_symbol)
#define PUBLIC_SYMBOL_LIST_GENERATOR(V, _) \

View File

@ -77,7 +77,10 @@ class ClearThreadInWasmScope {
Object ThrowWasmError(Isolate* isolate, MessageTemplate message) {
HandleScope scope(isolate);
Handle<Object> error_obj = isolate->factory()->NewWasmRuntimeError(message);
Handle<JSObject> error_obj = isolate->factory()->NewWasmRuntimeError(message);
JSObject::AddProperty(isolate, error_obj,
isolate->factory()->wasm_uncatchable_symbol(),
isolate->factory()->true_value(), NONE);
return isolate->Throw(*error_obj);
}
} // namespace

View File

@ -190,8 +190,11 @@ class InterpreterHandle {
case WasmInterpreter::State::TRAPPED: {
MessageTemplate message_id =
WasmOpcodes::TrapReasonToMessageId(thread->GetTrapReason());
Handle<Object> exception =
Handle<JSObject> exception =
isolate_->factory()->NewWasmRuntimeError(message_id);
JSObject::AddProperty(isolate_, exception,
isolate_->factory()->wasm_uncatchable_symbol(),
isolate_->factory()->true_value(), NONE);
auto result = thread->RaiseException(isolate_, exception);
if (result == WasmInterpreter::Thread::HANDLED) break;
// If no local handler was found, we fall-thru to {STOPPED}.

View File

@ -1302,12 +1302,14 @@ class ThreadImpl {
WasmInterpreter::Thread::ExceptionHandlingResult HandleException(
Isolate* isolate) {
DCHECK(isolate->has_pending_exception());
bool catchable =
isolate->is_catchable_by_wasm(isolate->pending_exception());
DCHECK_LT(0, activations_.size());
Activation& act = activations_.back();
while (frames_.size() > act.fp) {
Frame& frame = frames_.back();
InterpreterCode* code = frame.code;
if (code->side_table->HasEntryAt(frame.pc)) {
if (catchable && code->side_table->HasEntryAt(frame.pc)) {
TRACE("----- HANDLE -----\n");
Push(WasmValue(handle(isolate->pending_exception(), isolate)));
isolate->clear_pending_exception();

View File

@ -160,17 +160,15 @@ WASM_EXEC_TEST(TryCatchTrapTypeError) {
namespace {
// TODO(8729): The semantics of this are not yet specified and might change,
// this test aims at keeping semantics of various execution tiers consistent.
void TestTryCatchTrap(byte* code, size_t code_size,
ExecutionTier execution_tier) {
void TestTrapNotCaught(byte* code, size_t code_size,
ExecutionTier execution_tier) {
TestSignatures sigs;
EXPERIMENTAL_FLAG_SCOPE(eh);
WasmRunner<uint32_t, uint32_t> r(execution_tier, nullptr, "main",
kRuntimeExceptionSupport);
WasmRunner<uint32_t> r(execution_tier, nullptr, "main",
kRuntimeExceptionSupport);
r.builder().AddMemory(kWasmPageSize);
constexpr uint32_t kResult0 = 23;
constexpr uint32_t kResult1 = 42;
constexpr uint32_t kResultSuccess = 23;
constexpr uint32_t kResultCaught = 47;
// Build a trapping helper function.
WasmFunctionCompiler& trap_func = r.NewFunction(sigs.i_ii());
@ -179,39 +177,36 @@ void TestTryCatchTrap(byte* code, size_t code_size,
// Build the main test function.
BUILD(r, WASM_TRY_CATCH_T(
kWasmI32,
WASM_STMTS(WASM_I32V(kResult1),
WASM_IF(WASM_I32_EQZ(WASM_GET_LOCAL(0)),
WASM_STMTS(WASM_CALL_FUNCTION(
trap_func.function_index(),
WASM_I32V(7), WASM_I32V(9)),
WASM_DROP))),
WASM_STMTS(WASM_DROP, WASM_I32V(kResult0))));
WASM_STMTS(WASM_I32V(kResultSuccess),
WASM_CALL_FUNCTION(trap_func.function_index(),
WASM_I32V(7), WASM_I32V(9)),
WASM_DROP),
WASM_STMTS(WASM_DROP, WASM_I32V(kResultCaught))));
// Need to call through JS to allow for creation of stack traces.
r.CheckCallViaJS(kResult0, 0);
r.CheckCallViaJS(kResult1, 1);
r.CheckCallViaJSTraps();
}
} // namespace
WASM_EXEC_TEST(TryCatchTrapUnreachable) {
byte code[] = {WASM_UNREACHABLE};
TestTryCatchTrap(code, arraysize(code), execution_tier);
TestTrapNotCaught(code, arraysize(code), execution_tier);
}
WASM_EXEC_TEST(TryCatchTrapMemOutOfBounds) {
byte code[] = {WASM_LOAD_MEM(MachineType::Int32(), WASM_I32V_1(-1))};
TestTryCatchTrap(code, arraysize(code), execution_tier);
TestTrapNotCaught(code, arraysize(code), execution_tier);
}
WASM_EXEC_TEST(TryCatchTrapDivByZero) {
byte code[] = {WASM_I32_DIVS(WASM_GET_LOCAL(0), WASM_I32V_1(0))};
TestTryCatchTrap(code, arraysize(code), execution_tier);
TestTrapNotCaught(code, arraysize(code), execution_tier);
}
WASM_EXEC_TEST(TryCatchTrapRemByZero) {
byte code[] = {WASM_I32_REMS(WASM_GET_LOCAL(0), WASM_I32V_1(0))};
TestTryCatchTrap(code, arraysize(code), execution_tier);
TestTrapNotCaught(code, arraysize(code), execution_tier);
}
} // namespace test_run_wasm_exceptions

View File

@ -602,6 +602,10 @@ class WasmRunner : public WasmRunnerBase {
CheckCallApplyViaJS(expected, function()->func_index, buffer, sizeof...(p));
}
void CheckCallViaJSTraps(ParamTypes... p) {
CheckCallViaJS(static_cast<double>(0xDEADBEEF), p...);
}
void CheckUsedExecutionTier(ExecutionTier expected_tier) {
// Liftoff can fail and fallback to Turbofan, so check that the function
// gets compiled by the tier requested, to guard against accidental success.

View File

@ -85,6 +85,110 @@ load("test/mjsunit/wasm/exceptions-utils.js");
assertEquals(42, instance.exports.simple_throw_catch_to_0_1(1));
})();
(function TestTrapNotCaught() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addFunction('unreachable_in_try', kSig_v_v)
.addBody([
kExprTry, kWasmStmt,
kExprUnreachable,
kExprCatch,
kExprDrop,
kExprEnd
]).exportFunc();
let instance = builder.instantiate();
assertTraps(kTrapUnreachable, () => instance.exports.unreachable_in_try());
})();
(function TestTrapInCalleeNotCaught() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let func_div = builder.addFunction('div', kSig_i_ii).addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprI32DivU
]);
builder.addFunction('trap_in_callee', kSig_i_ii)
.addBody([
kExprTry, kWasmI32,
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprCallFunction, func_div.index,
kExprCatch,
kExprDrop,
kExprI32Const, 11,
kExprEnd
]).exportFunc();
let instance = builder.instantiate();
assertEquals(3, instance.exports.trap_in_callee(7, 2));
assertTraps(kTrapDivByZero, () => instance.exports.trap_in_callee(1, 0));
})();
(function TestTrapViaJSNotCaught() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let imp = builder.addImport('imp', 'ort', kSig_i_v);
builder.addFunction('div', kSig_i_ii)
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprI32DivU
]).exportFunc();
builder.addFunction('call_import', kSig_i_v)
.addBody([
kExprTry, kWasmI32,
kExprCallFunction, imp,
kExprCatch,
kExprDrop,
kExprI32Const, 11,
kExprEnd
]).exportFunc();
let exception = undefined;
let instance;
function js_import() {
try {
instance.exports.div(1, 0);
} catch (e) {
exception = e;
}
throw exception;
}
instance = builder.instantiate({imp: {ort: js_import}});
let caught = undefined;
try {
let res = instance.exports.call_import();
assertUnreachable('call_import should trap, but returned with ' + res);
} catch (e) {
caught = e;
}
assertSame(exception, caught);
assertInstanceof(exception, WebAssembly.RuntimeError);
assertEquals(exception.message, kTrapMsgs[kTrapDivByZero]);
})();
(function TestManuallyThrownRuntimeErrorCaught() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let imp = builder.addImport('imp', 'ort', kSig_i_v);
builder.addFunction('call_import', kSig_i_v)
.addBody([
kExprTry, kWasmI32,
kExprCallFunction, imp,
kExprCatch,
kExprDrop,
kExprI32Const, 11,
kExprEnd
]).exportFunc();
function throw_exc() {
throw exception = new WebAssembly.RuntimeError('My user text');
}
let instance = builder.instantiate({imp: {ort: throw_exc}});
assertEquals(11, instance.exports.call_import());
})();
// Test that we can distinguish which exception was thrown by using a cascaded
// sequence of nested try blocks with a single handler in each catch block.
(function TestCatchComplex1() {

View File

@ -289,60 +289,60 @@ KNOWN_MAPS = {
("read_only_space", 0x01179): (95, "EnumCacheMap"),
("read_only_space", 0x011c9): (86, "ArrayBoilerplateDescriptionMap"),
("read_only_space", 0x012c5): (98, "InterceptorInfoMap"),
("read_only_space", 0x032d5): (71, "PromiseFulfillReactionJobTaskMap"),
("read_only_space", 0x032fd): (72, "PromiseRejectReactionJobTaskMap"),
("read_only_space", 0x03325): (73, "CallableTaskMap"),
("read_only_space", 0x0334d): (74, "CallbackTaskMap"),
("read_only_space", 0x03375): (75, "PromiseResolveThenableJobTaskMap"),
("read_only_space", 0x0339d): (78, "FunctionTemplateInfoMap"),
("read_only_space", 0x033c5): (79, "ObjectTemplateInfoMap"),
("read_only_space", 0x033ed): (80, "AccessCheckInfoMap"),
("read_only_space", 0x03415): (81, "AccessorInfoMap"),
("read_only_space", 0x0343d): (82, "AccessorPairMap"),
("read_only_space", 0x03465): (83, "AliasedArgumentsEntryMap"),
("read_only_space", 0x0348d): (84, "AllocationMementoMap"),
("read_only_space", 0x034b5): (87, "AsmWasmDataMap"),
("read_only_space", 0x034dd): (88, "AsyncGeneratorRequestMap"),
("read_only_space", 0x03505): (89, "BreakPointMap"),
("read_only_space", 0x0352d): (90, "BreakPointInfoMap"),
("read_only_space", 0x03555): (91, "CachedTemplateObjectMap"),
("read_only_space", 0x0357d): (93, "ClassPositionsMap"),
("read_only_space", 0x035a5): (94, "DebugInfoMap"),
("read_only_space", 0x035cd): (97, "FunctionTemplateRareDataMap"),
("read_only_space", 0x035f5): (99, "InterpreterDataMap"),
("read_only_space", 0x0361d): (100, "PromiseCapabilityMap"),
("read_only_space", 0x03645): (101, "PromiseReactionMap"),
("read_only_space", 0x0366d): (102, "PropertyDescriptorObjectMap"),
("read_only_space", 0x03695): (103, "PrototypeInfoMap"),
("read_only_space", 0x036bd): (104, "ScriptMap"),
("read_only_space", 0x036e5): (105, "SourceTextModuleInfoEntryMap"),
("read_only_space", 0x0370d): (106, "StackFrameInfoMap"),
("read_only_space", 0x03735): (107, "StackTraceFrameMap"),
("read_only_space", 0x0375d): (108, "TemplateObjectDescriptionMap"),
("read_only_space", 0x03785): (109, "Tuple2Map"),
("read_only_space", 0x037ad): (110, "WasmCapiFunctionDataMap"),
("read_only_space", 0x037d5): (111, "WasmDebugInfoMap"),
("read_only_space", 0x037fd): (112, "WasmExceptionTagMap"),
("read_only_space", 0x03825): (113, "WasmExportedFunctionDataMap"),
("read_only_space", 0x0384d): (114, "WasmIndirectFunctionTableMap"),
("read_only_space", 0x03875): (115, "WasmJSFunctionDataMap"),
("read_only_space", 0x0389d): (134, "InternalClassWithSmiElementsMap"),
("read_only_space", 0x038c5): (165, "InternalClassWithStructElementsMap"),
("read_only_space", 0x038ed): (164, "InternalClassMap"),
("read_only_space", 0x03915): (172, "SmiPairMap"),
("read_only_space", 0x0393d): (171, "SmiBoxMap"),
("read_only_space", 0x03965): (68, "ExportedSubClassBaseMap"),
("read_only_space", 0x0398d): (69, "ExportedSubClassMap"),
("read_only_space", 0x039b5): (173, "SortStateMap"),
("read_only_space", 0x039dd): (85, "AllocationSiteWithWeakNextMap"),
("read_only_space", 0x03a05): (85, "AllocationSiteWithoutWeakNextMap"),
("read_only_space", 0x03a2d): (76, "LoadHandler1Map"),
("read_only_space", 0x03a55): (76, "LoadHandler2Map"),
("read_only_space", 0x03a7d): (76, "LoadHandler3Map"),
("read_only_space", 0x03aa5): (77, "StoreHandler0Map"),
("read_only_space", 0x03acd): (77, "StoreHandler1Map"),
("read_only_space", 0x03af5): (77, "StoreHandler2Map"),
("read_only_space", 0x03b1d): (77, "StoreHandler3Map"),
("read_only_space", 0x032e5): (71, "PromiseFulfillReactionJobTaskMap"),
("read_only_space", 0x0330d): (72, "PromiseRejectReactionJobTaskMap"),
("read_only_space", 0x03335): (73, "CallableTaskMap"),
("read_only_space", 0x0335d): (74, "CallbackTaskMap"),
("read_only_space", 0x03385): (75, "PromiseResolveThenableJobTaskMap"),
("read_only_space", 0x033ad): (78, "FunctionTemplateInfoMap"),
("read_only_space", 0x033d5): (79, "ObjectTemplateInfoMap"),
("read_only_space", 0x033fd): (80, "AccessCheckInfoMap"),
("read_only_space", 0x03425): (81, "AccessorInfoMap"),
("read_only_space", 0x0344d): (82, "AccessorPairMap"),
("read_only_space", 0x03475): (83, "AliasedArgumentsEntryMap"),
("read_only_space", 0x0349d): (84, "AllocationMementoMap"),
("read_only_space", 0x034c5): (87, "AsmWasmDataMap"),
("read_only_space", 0x034ed): (88, "AsyncGeneratorRequestMap"),
("read_only_space", 0x03515): (89, "BreakPointMap"),
("read_only_space", 0x0353d): (90, "BreakPointInfoMap"),
("read_only_space", 0x03565): (91, "CachedTemplateObjectMap"),
("read_only_space", 0x0358d): (93, "ClassPositionsMap"),
("read_only_space", 0x035b5): (94, "DebugInfoMap"),
("read_only_space", 0x035dd): (97, "FunctionTemplateRareDataMap"),
("read_only_space", 0x03605): (99, "InterpreterDataMap"),
("read_only_space", 0x0362d): (100, "PromiseCapabilityMap"),
("read_only_space", 0x03655): (101, "PromiseReactionMap"),
("read_only_space", 0x0367d): (102, "PropertyDescriptorObjectMap"),
("read_only_space", 0x036a5): (103, "PrototypeInfoMap"),
("read_only_space", 0x036cd): (104, "ScriptMap"),
("read_only_space", 0x036f5): (105, "SourceTextModuleInfoEntryMap"),
("read_only_space", 0x0371d): (106, "StackFrameInfoMap"),
("read_only_space", 0x03745): (107, "StackTraceFrameMap"),
("read_only_space", 0x0376d): (108, "TemplateObjectDescriptionMap"),
("read_only_space", 0x03795): (109, "Tuple2Map"),
("read_only_space", 0x037bd): (110, "WasmCapiFunctionDataMap"),
("read_only_space", 0x037e5): (111, "WasmDebugInfoMap"),
("read_only_space", 0x0380d): (112, "WasmExceptionTagMap"),
("read_only_space", 0x03835): (113, "WasmExportedFunctionDataMap"),
("read_only_space", 0x0385d): (114, "WasmIndirectFunctionTableMap"),
("read_only_space", 0x03885): (115, "WasmJSFunctionDataMap"),
("read_only_space", 0x038ad): (134, "InternalClassWithSmiElementsMap"),
("read_only_space", 0x038d5): (165, "InternalClassWithStructElementsMap"),
("read_only_space", 0x038fd): (164, "InternalClassMap"),
("read_only_space", 0x03925): (172, "SmiPairMap"),
("read_only_space", 0x0394d): (171, "SmiBoxMap"),
("read_only_space", 0x03975): (68, "ExportedSubClassBaseMap"),
("read_only_space", 0x0399d): (69, "ExportedSubClassMap"),
("read_only_space", 0x039c5): (173, "SortStateMap"),
("read_only_space", 0x039ed): (85, "AllocationSiteWithWeakNextMap"),
("read_only_space", 0x03a15): (85, "AllocationSiteWithoutWeakNextMap"),
("read_only_space", 0x03a3d): (76, "LoadHandler1Map"),
("read_only_space", 0x03a65): (76, "LoadHandler2Map"),
("read_only_space", 0x03a8d): (76, "LoadHandler3Map"),
("read_only_space", 0x03ab5): (77, "StoreHandler0Map"),
("read_only_space", 0x03add): (77, "StoreHandler1Map"),
("read_only_space", 0x03b05): (77, "StoreHandler2Map"),
("read_only_space", 0x03b2d): (77, "StoreHandler3Map"),
("map_space", 0x00121): (1057, "ExternalMap"),
("map_space", 0x00149): (1073, "JSMessageObjectMap"),
}