[wasm] Fix crash serializing modules w/ big frames
When a wasm function has a large stack frame, the x64 code generator performs the stack overflow check before constructing the frame. This requires using the `address_of_real_stack_limit` external reference, as well as the `ThrowWasmStackOverflow` runtime function. `ThrowWasmStackOverflow` is called via a generated trampoline, but it is not a builtin, so the serializer adds it to the `stub_lookup_` map. This map is encoded by using a monotonically increasing `stub_id` that starts at 0. When the function is serialized, a stub is differentiated from a builtin by which half of the `i32` bits is used, upper or lower. A stub only uses the lower 16 bits and a builtin only uses the upper 16 bits. The deserializer checks whether the lower 16 bits are 0; if so, it is determined to be a builtin. But if the `stub_id` is 0, then it will be confused with builtin 0 (`RecordWrite`). Calling the builtin instead of the stub causes a crash. This CL starts all `stub_id`s at 1, which prevents the builtin/stub confusion. There is an additional bug that is not fixed by this CL: `ThrowWasmStackOverflow` shouldn't be called at all. Currently it is called because `address_of_real_stack_limit` is a thread-local value that is not properly relocated. Bug: chromium:808848 Change-Id: I06b3e650ea58ad717dcc47a3716443e16582e711 Reviewed-on: https://chromium-review.googlesource.com/981687 Reviewed-by: Michael Starzinger <mstarzinger@chromium.org> Commit-Queue: Ben Smith <binji@chromium.org> Cr-Commit-Position: refs/heads/master@{#52252}
This commit is contained in:
parent
56f7c23f85
commit
fae1ab03a9
@ -100,6 +100,9 @@ class Reader {
|
||||
|
||||
constexpr size_t kVersionSize = 4 * sizeof(uint32_t);
|
||||
|
||||
// Start from 1 so an encoded stub id is not confused with an encoded builtin.
|
||||
constexpr int kFirstStubId = 1;
|
||||
|
||||
void WriteVersion(Isolate* isolate, Vector<byte> buffer) {
|
||||
DCHECK_GE(buffer.size(), kVersionSize);
|
||||
Writer writer(buffer);
|
||||
@ -304,7 +307,7 @@ void NativeModuleSerializer::BufferCopiedStubs() {
|
||||
Writer writer(remaining_);
|
||||
writer.Write(
|
||||
static_cast<uint32_t>((buff_size - sizeof(uint32_t)) / sizeof(uint32_t)));
|
||||
uint32_t stub_id = 0;
|
||||
uint32_t stub_id = kFirstStubId;
|
||||
|
||||
for (auto pair : native_module_->stubs_) {
|
||||
uint32_t key = pair.first;
|
||||
@ -615,7 +618,7 @@ Address NativeModuleDeserializer::GetTrampolineOrStubFromTag(uint32_t tag) {
|
||||
return native_module_->GetLocalAddressFor(handle(builtin));
|
||||
} else {
|
||||
DCHECK_EQ(tag & 0xFFFF0000, 0);
|
||||
return stubs_[tag];
|
||||
return stubs_[tag - kFirstStubId];
|
||||
}
|
||||
}
|
||||
|
||||
|
71
test/mjsunit/regress/wasm/regress-808848.js
Normal file
71
test/mjsunit/regress/wasm/regress-808848.js
Normal file
@ -0,0 +1,71 @@
|
||||
// Copyright 2018 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.
|
||||
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
load('test/mjsunit/wasm/wasm-constants.js');
|
||||
load('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
|
||||
// The number of locals must be greater than the constant defined here:
|
||||
// https://cs.chromium.org/chromium/src/v8/src/compiler/x64/code-generator-x64.cc?l=3146
|
||||
const kNumLocals = 128;
|
||||
|
||||
function varuint32(val) {
|
||||
let bytes = [];
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
bytes.push(0x80 | ((val >> (7 * i)) & 0x7f));
|
||||
}
|
||||
bytes.push((val >> (7 * 4)) & 0x7f);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
// Generate a function that calls the "get" import `kNumLocals` times, and
|
||||
// stores each result in a local, then calls the "call" import `kNumLocals`
|
||||
// times with the stored local values.
|
||||
//
|
||||
// The intention is to create a function that has a large stack frame.
|
||||
let body = [];
|
||||
|
||||
for (let i = 0; i < kNumLocals; ++i) {
|
||||
body.push(kExprCallFunction, 0, kExprSetLocal, ...varuint32(i));
|
||||
}
|
||||
|
||||
for (let i = 0; i < kNumLocals; ++i) {
|
||||
body.push(kExprGetLocal, ...varuint32(i), kExprCallFunction, 1);
|
||||
}
|
||||
|
||||
let builder = new WasmModuleBuilder();
|
||||
builder.addImport('mod', 'get', kSig_i_v);
|
||||
builder.addImport('mod', 'call', kSig_v_i);
|
||||
builder.
|
||||
addFunction('main', kSig_v_v).
|
||||
addLocals({i32_count: kNumLocals}).
|
||||
addBody(body).
|
||||
exportAs('main');
|
||||
let m1_bytes = builder.toBuffer();
|
||||
let m1 = new WebAssembly.Module(m1_bytes);
|
||||
|
||||
// Serialize the module and postMessage it to another thread.
|
||||
let serialized_m1 = %SerializeWasmModule(m1);
|
||||
|
||||
let workerScript =
|
||||
`onmessage = function(msg) {
|
||||
let {serialized_m1, m1_bytes} = msg;
|
||||
|
||||
try {
|
||||
let m1_clone = %DeserializeWasmModule(serialized_m1, m1_bytes);
|
||||
let imports = {mod: {get: () => 3, call: () => {}}};
|
||||
let i2 = new WebAssembly.Instance(m1_clone, imports);
|
||||
i2.exports.main();
|
||||
postMessage('done');
|
||||
} catch(e) {
|
||||
postMessage('done w/ exception');
|
||||
}
|
||||
}`;
|
||||
|
||||
let worker = new Worker(workerScript);
|
||||
worker.postMessage({serialized_m1, m1_bytes});
|
||||
|
||||
// Wait for worker to finish.
|
||||
print(worker.getMessage());
|
Loading…
Reference in New Issue
Block a user