[wasm] Make br_on_exn trap on nullptr

The behaviour was clarified in the spec:
https://github.com/WebAssembly/exception-handling/pull/97

br_on_exn (and also rethrow, which will be added in another CL) should
trap on nullptr. This CL implements this by an explicit check on each
br_on_exn (within {GetExceptionTag}). This check will be redundant if
several br_on_exn follow each other. Since also the runtime call for
{GetExceptionTag} is redundant, and also the fact that we do a runtime
call is suboptimal, I consider the whole implementation prototypical for
now anyway.

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

Bug: v8:10128
Change-Id: I234c3183f93fe0884aadd2ab6dbd6c2b7a07c660
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2113381
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66826}
This commit is contained in:
Clemens Backes 2020-03-23 11:46:51 +01:00 committed by Commit Bot
parent 40c9fa052b
commit bcc055c158
10 changed files with 71 additions and 11 deletions

View File

@ -878,6 +878,7 @@ namespace internal {
TFS(ThrowWasmTrapDataSegmentDropped) \
TFS(ThrowWasmTrapElemSegmentDropped) \
TFS(ThrowWasmTrapTableOutOfBounds) \
TFS(ThrowWasmTrapBrOnExnNullRef) \
\
/* WeakMap */ \
TFJ(WeakMapConstructor, kDontAdaptArgumentsSentinel) \

View File

@ -1596,7 +1596,8 @@ enum class LoadSensitivity {
V(TrapFuncSigMismatch) \
V(TrapDataSegmentDropped) \
V(TrapElemSegmentDropped) \
V(TrapTableOutOfBounds)
V(TrapTableOutOfBounds) \
V(TrapBrOnExnNullRef)
enum KeyedAccessLoadMode {
STANDARD_LOAD,

View File

@ -551,6 +551,7 @@ namespace internal {
T(WasmTrapDataSegmentDropped, "data segment has been dropped") \
T(WasmTrapElemSegmentDropped, "element segment has been dropped") \
T(WasmTrapTableOutOfBounds, "table access out of bounds") \
T(WasmTrapBrOnExnNullRef, "br_on_exn on nullref value") \
T(WasmExceptionError, "wasm exception") \
/* Asm.js validation related */ \
T(AsmJsInvalid, "Invalid asm.js: %") \

View File

@ -2150,8 +2150,10 @@ Node* WasmGraphBuilder::LoadExceptionTagFromTable(uint32_t exception_index) {
return tag;
}
Node* WasmGraphBuilder::GetExceptionTag(Node* except_obj) {
needs_stack_check_ = true;
Node* WasmGraphBuilder::GetExceptionTag(Node* except_obj,
wasm::WasmCodePosition position) {
TrapIfTrue(wasm::kTrapBrOnExnNullRef, gasm_->WordEqual(RefNull(), except_obj),
position);
return BuildCallToRuntime(Runtime::kWasmExceptionGetTag, &except_obj, 1);
}

View File

@ -213,7 +213,7 @@ class WasmGraphBuilder {
Node* Rethrow(Node* except_obj);
Node* ExceptionTagEqual(Node* caught_tag, Node* expected_tag);
Node* LoadExceptionTagFromTable(uint32_t exception_index);
Node* GetExceptionTag(Node* except_obj);
Node* GetExceptionTag(Node* except_obj, wasm::WasmCodePosition);
Node* GetExceptionValues(Node* except_obj,
const wasm::WasmException* exception,
Vector<Node*> values_out);

View File

@ -492,7 +492,8 @@ class WasmGraphBuildingInterface {
TFNode* if_no_match = nullptr;
// Get the exception tag and see if it matches the expected one.
TFNode* caught_tag = BUILD(GetExceptionTag, exception.node);
TFNode* caught_tag =
BUILD(GetExceptionTag, exception.node, decoder->position());
TFNode* exception_tag = BUILD(LoadExceptionTagFromTable, imm.index);
TFNode* compare = BUILD(ExceptionTagEqual, caught_tag, exception_tag);
BUILD(BranchNoHint, compare, &if_match, &if_no_match);

View File

@ -3132,6 +3132,7 @@ class ThreadImpl {
HandleScope handle_scope(isolate_); // Avoid leaking handles.
WasmValue ex = Pop();
Handle<Object> exception = ex.to_anyref();
if (exception->IsNull()) return DoTrap(kTrapBrOnExnNullRef, pc);
if (MatchingExceptionTag(exception, imm.index.index)) {
imm.index.exception = &module()->exceptions[imm.index.index];
DoUnpackException(imm.index.exception, exception);

View File

@ -548,13 +548,14 @@ var prettyPrinted;
assertInstanceof = function assertInstanceof(obj, type) {
if (!(obj instanceof type)) {
var actualTypeName = null;
var actualConstructor = Object.getPrototypeOf(obj).constructor;
if (typeof actualConstructor === "function") {
var actualConstructor = obj && Object.getPrototypeOf(obj).constructor;
if (typeof actualConstructor === 'function') {
actualTypeName = actualConstructor.name || String(actualConstructor);
}
failWithMessage("Object <" + prettyPrinted(obj) + "> is not an instance of <" +
(type.name || type) + ">" +
(actualTypeName ? " but of <" + actualTypeName + ">" : ""));
failWithMessage(
'Object <' + prettyPrinted(obj) + '> is not an instance of <' +
(type.name || type) + '>' +
(actualTypeName ? ' but of <' + actualTypeName + '>' : ''));
}
};

View File

@ -146,6 +146,7 @@ load("test/mjsunit/wasm/exceptions-utils.js");
// Test storing and loading to/from an exception type table.
(function TestTableExnRef() {
print(arguments.callee.name);
let kSig_e_i = makeSig([kWasmI32], [kWasmExnRef]);
let kSig_v_ie = makeSig([kWasmI32, kWasmExnRef], []);
let builder = new WasmModuleBuilder();
@ -182,3 +183,52 @@ load("test/mjsunit/wasm/exceptions-utils.js");
assertSame(e0, instance.exports.table.get(0));
assertSame(e1, instance.exports.table.get(1));
})();
// 'br_on_exn' on a null-ref value should trap.
(function TestBrOnExnNullRefSimple() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let except = builder.addException(kSig_v_r);
builder.addFunction('br_on_exn_nullref', kSig_v_v)
.addBody([
kExprRefNull,
kExprBrOnExn, 0, except,
kExprDrop
]).exportFunc();
let instance = builder.instantiate();
assertTraps(kTrapBrOnExnNullRef, () => instance.exports.br_on_exn_nullref());
})();
(function TestBrOnExnNullRefFromJS() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let except = builder.addException(kSig_v_i);
let imp = builder.addImport('imp', 'ort', kSig_i_i);
let kConstant0 = 11;
let kNoMatch = 13;
builder.addFunction('call_import', kSig_i_i)
.addBody([
kExprTry, kWasmI32,
kExprLocalGet, 0,
kExprCallFunction, imp,
kExprCatch,
kExprBrOnExn, 0, except,
kExprDrop,
kExprI32Const, kNoMatch,
kExprEnd
]).exportFunc();
let instance;
function js_import(i) {
if (i == 0) return kConstant0; // Will return kConstant0.
if (i == 1) throw new Error('1'); // Will not match.
if (i == 2) throw null; // Will trap.
throw undefined; // Will not match.
}
instance = builder.instantiate({imp: {ort: js_import}});
assertEquals(kConstant0, instance.exports.call_import(0));
assertEquals(kNoMatch, instance.exports.call_import(1));
assertTraps(kTrapBrOnExnNullRef, () => instance.exports.call_import(2));
assertEquals(kNoMatch, instance.exports.call_import(3));
})();

View File

@ -501,6 +501,7 @@ let kTrapUnalignedAccess = 9;
let kTrapDataSegmentDropped = 10;
let kTrapElemSegmentDropped = 11;
let kTrapTableOutOfBounds = 12;
let kTrapBrOnExnNullRef = 13;
let kTrapMsgs = [
"unreachable",
@ -515,7 +516,8 @@ let kTrapMsgs = [
"operation does not support unaligned accesses",
"data segment has been dropped",
"element segment has been dropped",
"table access out of bounds"
"table access out of bounds",
"br_on_exn on nullref value"
];
function assertTraps(trap, code) {