Commit 9dfa4639 authored by mtrofin's avatar mtrofin Committed by Commit bot

Reland of [wasm] Transferrable modules (patchset #1 id:1 of...

Reland of [wasm] Transferrable modules (patchset #1 id:1 of https://codereview.chromium.org/2762163002/ )

Reason for revert:
Temporarily disabled tests on chromium side (https://codereview.chromium.org/2764933002)

Original issue's description:
> Revert of [wasm] Transferrable modules (patchset #13 id:280001 of https://codereview.chromium.org/2748473004/ )
>
> Reason for revert:
> Breaks layout tests:
> https://build.chromium.org/p/client.v8.fyi/builders/V8-Blink%20Linux%2064/builds/14312
>
> See https://github.com/v8/v8/wiki/Blink-layout-tests
>
> Original issue's description:
> > [wasm] Transferrable modules
> >
> > We want to restrict structured cloning in Chrome to:
> > - postMessage senders and receivers that are co-located
> > in the same process
> > - indexedDB (just https).
> >
> > For context, on the Chrome side, we will achieve the postMessage part
> > by using a mechanism similar to transferrables: the
> > SerializedScriptValue will have a list of wasm modules, separate from
> > the serialized data stream; and this list won't be copied cross
> > process boundaries. The IDB part is achieved by explicitly opting in
> > reading/writing to the serialization stream. To block attack vectors
> > in IPC cases, the default for deserialization will be to expect data
> > in the wasm transfers list.
> >
> > This change is the V8 side necessary to enabling this design. We
> > introduce TransferrableModule, an opaque datatype exposed to the
> > embedder. Internally, TransferrableModules are just serialized data,
> > because we don't have a better mechanism, at the moment, for
> > de-contextualizing/re-contextualizing wasm modules (wrt Isolate and
> > Context).
> >
> > The chrome defaults will be implemented in the
> > serialization/deserialization delegates on that side. For the v8 side
> > of things, in the absence of a serialization delegate, the V8
> > serializer will write to serialization stream. In the absence of a
> > deserialization delegate, the deserializer won't work. This asymmetry
> > is intentional - it communicates to the embedder the need to make a
> > policy decision, otherwise wasm serialization/deserialization won't
> > work "out of the box".
> >
> > BUG=v8:6079
> >
> > Review-Url: https://codereview.chromium.org/2748473004
> > Cr-Commit-Position: refs/heads/master@{#43955}
> > Committed: https://chromium.googlesource.com/v8/v8/+/99743ad460ea5b9795ba9d70a074e75d7362a3d1
>
> TBR=jbroman@chromium.org,bradnelson@chromium.org,mtrofin@chromium.org
> # Skipping CQ checks because original CL landed less than 1 days ago.
> NOPRESUBMIT=true
> NOTREECHECKS=true
> NOTRY=true
> BUG=v8:6079
>
> Review-Url: https://codereview.chromium.org/2762163002
> Cr-Commit-Position: refs/heads/master@{#43981}
> Committed: https://chromium.googlesource.com/v8/v8/+/e538b70e1a45289dfe0fa9789563f023a5e9c22b

TBR=jbroman@chromium.org,bradnelson@chromium.org,machenbach@chromium.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=v8:6079

Review-Url: https://codereview.chromium.org/2762273002
Cr-Commit-Position: refs/heads/master@{#43994}
parent 154369bb
......@@ -108,6 +108,7 @@ class Private;
class Uint32;
class Utils;
class Value;
class WasmCompiledModule;
template <class T> class Local;
template <class T>
class MaybeLocal;
......@@ -1709,6 +1710,8 @@ class V8_EXPORT ValueSerializer {
virtual Maybe<uint32_t> GetSharedArrayBufferId(
Isolate* isolate, Local<SharedArrayBuffer> shared_array_buffer);
virtual Maybe<uint32_t> GetWasmModuleTransferId(
Isolate* isolate, Local<WasmCompiledModule> module);
/*
* Allocates memory for the buffer of at least the size provided. The actual
* size (which may be greater or equal) is written to |actual_size|. If no
......@@ -1819,6 +1822,13 @@ class V8_EXPORT ValueDeserializer {
* MaybeLocal<Object>() returned.
*/
virtual MaybeLocal<Object> ReadHostObject(Isolate* isolate);
/*
* Get a WasmCompiledModule given a transfer_id previously provided
* by ValueSerializer::GetWasmModuleTransferId
*/
virtual MaybeLocal<WasmCompiledModule> GetWasmModuleFromId(
Isolate* isolate, uint32_t transfer_id);
};
ValueDeserializer(Isolate* isolate, const uint8_t* data, size_t size);
......@@ -1861,6 +1871,11 @@ class V8_EXPORT ValueDeserializer {
*/
void SetSupportsLegacyWireFormat(bool supports_legacy_wire_format);
/*
* Expect inline wasm in the data stream (rather than in-memory transfer)
*/
void SetExpectInlineWasm(bool allow_inline_wasm);
/*
* Reads the underlying wire format version. Likely mostly to be useful to
* legacy code reading old wire format versions. Must be called after
......@@ -3903,6 +3918,37 @@ class V8_EXPORT WasmCompiledModule : public Object {
typedef std::pair<std::unique_ptr<const uint8_t[]>, size_t> SerializedModule;
// A buffer that is owned by the caller.
typedef std::pair<const uint8_t*, size_t> CallerOwnedBuffer;
// An opaque, native heap object for transferring wasm modules. It
// supports move semantics, and does not support copy semantics.
class TransferrableModule final {
public:
TransferrableModule(TransferrableModule&& src) = default;
TransferrableModule(const TransferrableModule& src) = delete;
TransferrableModule& operator=(TransferrableModule&& src) = default;
TransferrableModule& operator=(const TransferrableModule& src) = delete;
private:
typedef std::pair<std::unique_ptr<const uint8_t[]>, size_t> OwnedBuffer;
friend class WasmCompiledModule;
TransferrableModule(OwnedBuffer&& code, OwnedBuffer&& bytes)
: compiled_code(std::move(code)), wire_bytes(std::move(bytes)) {}
OwnedBuffer compiled_code = {nullptr, 0};
OwnedBuffer wire_bytes = {nullptr, 0};
};
// Get an in-memory, non-persistable, and context-independent (meaning,
// suitable for transfer to another Isolate and Context) representation
// of this wasm compiled module.
TransferrableModule GetTransferrableModule();
// Efficiently re-create a WasmCompiledModule, without recompiling, from
// a TransferrableModule.
static MaybeLocal<WasmCompiledModule> FromTransferrableModule(
Isolate* isolate, const TransferrableModule&);
// Get the wasm-encoded bytes that were used to compile this module.
Local<String> GetWasmWireBytes();
......@@ -3924,6 +3970,11 @@ class V8_EXPORT WasmCompiledModule : public Object {
static MaybeLocal<WasmCompiledModule> Compile(Isolate* isolate,
const uint8_t* start,
size_t length);
static CallerOwnedBuffer AsCallerOwned(
const TransferrableModule::OwnedBuffer& buff) {
return {buff.first.get(), buff.second};
}
WasmCompiledModule();
static void CheckCast(Value* obj);
};
......
......@@ -3125,6 +3125,11 @@ Maybe<uint32_t> ValueSerializer::Delegate::GetSharedArrayBufferId(
return Nothing<uint32_t>();
}
Maybe<uint32_t> ValueSerializer::Delegate::GetWasmModuleTransferId(
Isolate* v8_isolate, Local<WasmCompiledModule> module) {
return Nothing<uint32_t>();
}
void* ValueSerializer::Delegate::ReallocateBufferMemory(void* old_buffer,
size_t size,
size_t* actual_size) {
......@@ -3213,6 +3218,15 @@ MaybeLocal<Object> ValueDeserializer::Delegate::ReadHostObject(
return MaybeLocal<Object>();
}
MaybeLocal<WasmCompiledModule> ValueDeserializer::Delegate::GetWasmModuleFromId(
Isolate* v8_isolate, uint32_t id) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
isolate->ScheduleThrow(*isolate->factory()->NewError(
isolate->error_function(),
i::MessageTemplate::kDataCloneDeserializationError));
return MaybeLocal<WasmCompiledModule>();
}
struct ValueDeserializer::PrivateData {
PrivateData(i::Isolate* i, i::Vector<const uint8_t> data, Delegate* delegate)
: isolate(i), deserializer(i, data, delegate) {}
......@@ -3275,6 +3289,10 @@ void ValueDeserializer::SetSupportsLegacyWireFormat(
private_->supports_legacy_wire_format = supports_legacy_wire_format;
}
void ValueDeserializer::SetExpectInlineWasm(bool expect_inline_wasm) {
private_->deserializer.set_expect_inline_wasm(expect_inline_wasm);
}
uint32_t ValueDeserializer::GetWireFormatVersion() const {
CHECK(!private_->has_aborted);
return private_->deserializer.GetWireFormatVersion();
......@@ -7506,6 +7524,36 @@ Local<String> WasmCompiledModule::GetWasmWireBytes() {
return Local<String>::Cast(Utils::ToLocal(wire_bytes));
}
// Currently, wasm modules are bound, both to Isolate and to
// the Context they were created in. The currently-supported means to
// decontextualize and then re-contextualize a module is via
// serialization/deserialization.
WasmCompiledModule::TransferrableModule
WasmCompiledModule::GetTransferrableModule() {
i::DisallowHeapAllocation no_gc;
WasmCompiledModule::SerializedModule compiled_part = Serialize();
Local<String> wire_bytes = GetWasmWireBytes();
size_t wire_size = static_cast<size_t>(wire_bytes->Length());
uint8_t* bytes = new uint8_t[wire_size];
wire_bytes->WriteOneByte(bytes, 0, wire_bytes->Length());
return TransferrableModule(
std::move(compiled_part),
std::make_pair(
std::unique_ptr<const uint8_t[]>(const_cast<const uint8_t*>(bytes)),
wire_size));
}
MaybeLocal<WasmCompiledModule> WasmCompiledModule::FromTransferrableModule(
Isolate* isolate,
const WasmCompiledModule::TransferrableModule& transferrable_module) {
MaybeLocal<WasmCompiledModule> ret =
Deserialize(isolate, AsCallerOwned(transferrable_module.compiled_code),
AsCallerOwned(transferrable_module.wire_bytes));
return ret;
}
WasmCompiledModule::SerializedModule WasmCompiledModule::Serialize() {
i::Handle<i::JSObject> obj =
i::Handle<i::JSObject>::cast(Utils::OpenHandle(this));
......
......@@ -126,6 +126,8 @@ enum class SerializationTag : uint8_t {
// wasmWireByteLength:uint32_t, then raw data
// compiledDataLength:uint32_t, then raw data
kWasmModule = 'W',
// A wasm module object transfer. next value is its index.
kWasmModuleTransfer = 'w',
// The delegate is responsible for processing all following data.
// This "escapes" to whatever wire format the delegate chooses.
kHostObject = '\\',
......@@ -803,6 +805,19 @@ Maybe<bool> ValueSerializer::WriteJSArrayBufferView(JSArrayBufferView* view) {
}
Maybe<bool> ValueSerializer::WriteWasmModule(Handle<JSObject> object) {
if (delegate_ != nullptr) {
Maybe<uint32_t> transfer_id = delegate_->GetWasmModuleTransferId(
reinterpret_cast<v8::Isolate*>(isolate_),
v8::Local<v8::WasmCompiledModule>::Cast(Utils::ToLocal(object)));
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate_, Nothing<bool>());
uint32_t id = 0;
if (transfer_id.To(&id)) {
WriteTag(SerializationTag::kWasmModuleTransfer);
WriteVarint<uint32_t>(id);
return Just(true);
}
}
Handle<WasmCompiledModule> compiled_part(
WasmCompiledModule::cast(object->GetEmbedderField(0)), isolate_);
WasmEncodingTag encoding_tag = WasmEncodingTag::kRawBytes;
......@@ -1150,6 +1165,8 @@ MaybeHandle<Object> ValueDeserializer::ReadObjectInternal() {
}
case SerializationTag::kWasmModule:
return ReadWasmModule();
case SerializationTag::kWasmModuleTransfer:
return ReadWasmModuleTransfer();
case SerializationTag::kHostObject:
return ReadHostObject();
default:
......@@ -1595,8 +1612,32 @@ MaybeHandle<JSArrayBufferView> ValueDeserializer::ReadJSArrayBufferView(
return typed_array;
}
MaybeHandle<JSObject> ValueDeserializer::ReadWasmModuleTransfer() {
if (FLAG_wasm_disable_structured_cloning || expect_inline_wasm()) {
return MaybeHandle<JSObject>();
}
uint32_t transfer_id = 0;
Local<Value> module_value;
if (!ReadVarint<uint32_t>().To(&transfer_id) || delegate_ == nullptr ||
!delegate_
->GetWasmModuleFromId(reinterpret_cast<v8::Isolate*>(isolate_),
transfer_id)
.ToLocal(&module_value)) {
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate_, JSObject);
return MaybeHandle<JSObject>();
}
uint32_t id = next_id_++;
Handle<JSObject> module =
Handle<JSObject>::cast(Utils::OpenHandle(*module_value));
AddObjectWithID(id, module);
return module;
}
MaybeHandle<JSObject> ValueDeserializer::ReadWasmModule() {
if (FLAG_wasm_disable_structured_cloning) return MaybeHandle<JSObject>();
if (FLAG_wasm_disable_structured_cloning || !expect_inline_wasm()) {
return MaybeHandle<JSObject>();
}
Vector<const uint8_t> encoding_tag;
if (!ReadRawBytes(sizeof(WasmEncodingTag)).To(&encoding_tag) ||
......@@ -1625,21 +1666,22 @@ MaybeHandle<JSObject> ValueDeserializer::ReadWasmModule() {
// Try to deserialize the compiled module first.
ScriptData script_data(compiled_bytes.start(), compiled_bytes.length());
Handle<FixedArray> compiled_part;
MaybeHandle<JSObject> result;
if (WasmCompiledModuleSerializer::DeserializeWasmModule(
isolate_, &script_data, wire_bytes)
.ToHandle(&compiled_part)) {
return WasmModuleObject::New(
result = WasmModuleObject::New(
isolate_, Handle<WasmCompiledModule>::cast(compiled_part));
}
// If that fails, recompile.
MaybeHandle<JSObject> result;
{
} else {
wasm::ErrorThrower thrower(isolate_, "ValueDeserializer::ReadWasmModule");
result = wasm::SyncCompile(isolate_, &thrower,
wasm::ModuleWireBytes(wire_bytes));
}
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate_, JSObject);
uint32_t id = next_id_++;
if (!result.is_null()) {
AddObjectWithID(id, result.ToHandleChecked());
}
return result;
}
......
......@@ -31,6 +31,7 @@ class JSValue;
class Object;
class Oddball;
class Smi;
class WasmModuleObject;
enum class SerializationTag : uint8_t;
......@@ -218,6 +219,9 @@ class ValueDeserializer {
bool ReadUint64(uint64_t* value) WARN_UNUSED_RESULT;
bool ReadDouble(double* value) WARN_UNUSED_RESULT;
bool ReadRawBytes(size_t length, const void** data) WARN_UNUSED_RESULT;
void set_expect_inline_wasm(bool expect_inline_wasm) {
expect_inline_wasm_ = expect_inline_wasm;
}
private:
// Reading the wire format.
......@@ -230,6 +234,7 @@ class ValueDeserializer {
Maybe<T> ReadZigZag() WARN_UNUSED_RESULT;
Maybe<double> ReadDouble() WARN_UNUSED_RESULT;
Maybe<Vector<const uint8_t>> ReadRawBytes(int size) WARN_UNUSED_RESULT;
bool expect_inline_wasm() const { return expect_inline_wasm_; }
// Reads a string if it matches the one provided.
// Returns true if this was the case. Otherwise, nothing is consumed.
......@@ -263,6 +268,7 @@ class ValueDeserializer {
MaybeHandle<JSArrayBufferView> ReadJSArrayBufferView(
Handle<JSArrayBuffer> buffer) WARN_UNUSED_RESULT;
MaybeHandle<JSObject> ReadWasmModule() WARN_UNUSED_RESULT;
MaybeHandle<JSObject> ReadWasmModuleTransfer() WARN_UNUSED_RESULT;
MaybeHandle<JSObject> ReadHostObject() WARN_UNUSED_RESULT;
/*
......@@ -285,6 +291,7 @@ class ValueDeserializer {
PretenureFlag pretenure_;
uint32_t version_ = 0;
uint32_t next_id_ = 0;
bool expect_inline_wasm_ = false;
// Always global handles.
Handle<FixedArray> id_map_;
......
......@@ -469,6 +469,43 @@ TEST(BlockWasmCodeGenAtDeserialization) {
Cleanup();
}
TEST(TransferrableWasmModules) {
v8::internal::AccountingAllocator allocator;
Zone zone(&allocator, ZONE_NAME);
ZoneBuffer buffer(&zone);
WasmSerializationTest::BuildWireBytes(&zone, &buffer);
Isolate* from_isolate = CcTest::InitIsolateOnce();
ErrorThrower thrower(from_isolate, "");
std::vector<v8::WasmCompiledModule::TransferrableModule> store;
{
HandleScope scope(from_isolate);
testing::SetupIsolateForWasmModule(from_isolate);
MaybeHandle<WasmModuleObject> module_object = SyncCompile(
from_isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()));
v8::Local<v8::WasmCompiledModule> v8_module =
v8::Local<v8::WasmCompiledModule>::Cast(v8::Utils::ToLocal(
Handle<JSObject>::cast(module_object.ToHandleChecked())));
store.push_back(v8_module->GetTransferrableModule());
}
{
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator =
from_isolate->array_buffer_allocator();
v8::Isolate* to_isolate = v8::Isolate::New(create_params);
v8::HandleScope new_scope(to_isolate);
v8::Local<v8::Context> deserialization_context =
v8::Context::New(to_isolate);
deserialization_context->Enter();
v8::MaybeLocal<v8::WasmCompiledModule> mod =
v8::WasmCompiledModule::FromTransferrableModule(to_isolate, store[0]);
CHECK(!mod.IsEmpty());
}
}
TEST(MemorySize) {
{
// Initial memory size is 16, see wasm-module-builder.cc
......
This diff is collapsed.
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