Commit 2071ce3b authored by Thibaud Michaud's avatar Thibaud Michaud Committed by V8 LUCI CQ

[wasm][stack-switching] Throw on re-entrant suspender

Throw a wasm trap when trying to re-enter a suspender that is active or
suspended.

R=ahaas@chromium.org

Bug: v8:12191
Change-Id: Ic448a15db29de14fb8d6bb8408af8fbaae82a2b4
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3716481Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Commit-Queue: Thibaud Michaud <thibaudm@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81326}
parent e35039e7
...@@ -3017,7 +3017,10 @@ void AllocateContinuation(MacroAssembler* masm, Register function_data, ...@@ -3017,7 +3017,10 @@ void AllocateContinuation(MacroAssembler* masm, Register function_data,
__ Push(wasm_instance); __ Push(wasm_instance);
__ Push(function_data); __ Push(function_data);
__ Push(suspender); // Argument. __ Push(suspender); // Argument.
__ Move(kContextRegister, Smi::zero()); __ LoadAnyTaggedField(
kContextRegister,
MemOperand(wasm_instance, wasm::ObjectAccess::ToTagged(
WasmInstanceObject::kNativeContextOffset)));
__ CallRuntime(Runtime::kWasmAllocateContinuation); __ CallRuntime(Runtime::kWasmAllocateContinuation);
__ Pop(function_data); __ Pop(function_data);
__ Pop(wasm_instance); __ Pop(wasm_instance);
...@@ -3228,7 +3231,7 @@ void GenericJSToWasmWrapperHelper(MacroAssembler* masm, bool stack_switch) { ...@@ -3228,7 +3231,7 @@ void GenericJSToWasmWrapperHelper(MacroAssembler* masm, bool stack_switch) {
__ LoadRoot(active_continuation, RootIndex::kActiveContinuation); __ LoadRoot(active_continuation, RootIndex::kActiveContinuation);
SaveState(masm, active_continuation, rcx, &suspend); SaveState(masm, active_continuation, rcx, &suspend);
AllocateContinuation(masm, function_data, wasm_instance); AllocateContinuation(masm, function_data, wasm_instance);
Register target_continuation = rax; /* fixed */ Register target_continuation = rax; // fixed
// Save the old stack's rbp in r9, and use it to access the parameters in // Save the old stack's rbp in r9, and use it to access the parameters in
// the parent frame. // the parent frame.
// We also distribute the spill slots across the two stacks as needed by // We also distribute the spill slots across the two stacks as needed by
......
...@@ -650,6 +650,7 @@ namespace internal { ...@@ -650,6 +650,7 @@ namespace internal {
T(WasmTrapStringOffsetOutOfBounds, "string offset out of bounds") \ T(WasmTrapStringOffsetOutOfBounds, "string offset out of bounds") \
T(WasmTrapStringIsolatedSurrogate, \ T(WasmTrapStringIsolatedSurrogate, \
"Failed to encode string as UTF-8: contains unpaired surrogate") \ "Failed to encode string as UTF-8: contains unpaired surrogate") \
T(WasmTrapReentrantSuspender, "re-entering an active/suspended suspender") \
T(WasmExceptionError, "wasm exception") \ T(WasmExceptionError, "wasm exception") \
/* Asm.js validation related */ \ /* Asm.js validation related */ \
T(AsmJsInvalid, "Invalid asm.js: %") \ T(AsmJsInvalid, "Invalid asm.js: %") \
......
...@@ -798,6 +798,11 @@ RUNTIME_FUNCTION(Runtime_WasmAllocateContinuation) { ...@@ -798,6 +798,11 @@ RUNTIME_FUNCTION(Runtime_WasmAllocateContinuation) {
HandleScope scope(isolate); HandleScope scope(isolate);
Handle<WasmSuspenderObject> suspender = args.at<WasmSuspenderObject>(0); Handle<WasmSuspenderObject> suspender = args.at<WasmSuspenderObject>(0);
if (suspender->state() != WasmSuspenderObject::kInactive) {
return ThrowWasmError(isolate,
MessageTemplate::kWasmTrapReentrantSuspender);
}
// Update the continuation state. // Update the continuation state.
auto parent = handle(WasmContinuationObject::cast( auto parent = handle(WasmContinuationObject::cast(
isolate->root(RootIndex::kActiveContinuation)), isolate->root(RootIndex::kActiveContinuation)),
......
...@@ -323,3 +323,44 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); ...@@ -323,3 +323,44 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
let combined_promise = wrapped_export(); let combined_promise = wrapped_export();
assertPromiseResult(combined_promise, v => assertEquals(v, 42)); assertPromiseResult(combined_promise, v => assertEquals(v, 42));
})(); })();
(function TestReenterActiveSuspenderFails() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let import_index = builder.addImport("m", "i", kSig_v_v);
builder.addFunction("test", kSig_i_v)
.addBody([
kExprCallFunction, import_index,
kExprI32Const, 0
]).exportFunc();
let wrapped_export;
function js_import() {
wrapped_export(); // Re-enter the same wrapped export.
}
let instance = builder.instantiate({m: {i: js_import}});
let suspender = new WebAssembly.Suspender();
wrapped_export = suspender.returnPromiseOnSuspend(instance.exports.test);
assertThrows(wrapped_export, WebAssembly.RuntimeError,
/re-entering an active\/suspended suspender/);
})();
(function TestReenterSuspendedSuspenderFails() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let import_index = builder.addImport("m", "i", kSig_v_v);
builder.addFunction("test", kSig_i_v)
.addBody([
kExprCallFunction, import_index,
kExprI32Const, 0
]).exportFunc();
function js_import() {
return Promise.resolve(0);
}
let instance = builder.instantiate({m: {i: js_import}});
let suspender = new WebAssembly.Suspender();
let wrapped_export = suspender.returnPromiseOnSuspend(instance.exports.test);
let promise1 = wrapped_export();
// Re-enter the suspender before resolving the promise.
assertThrows(wrapped_export, WebAssembly.RuntimeError,
/re-entering an active\/suspended suspender/);
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment