Skip to content

Commit

Permalink
[wasm] Enforce memory and table limits during instantiation.
Browse files Browse the repository at this point in the history
[email protected]
BUG=chromium:575167

Review-Url: https://codereview.chromium.org/2636173002
Cr-Commit-Position: refs/heads/master@{#42426}
  • Loading branch information
titzer authored and Commit bot committed Jan 17, 2017
1 parent cfa6ce3 commit 8d76f0e
Show file tree
Hide file tree
Showing 13 changed files with 182 additions and 77 deletions.
17 changes: 8 additions & 9 deletions src/wasm/module-decoder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -319,11 +319,10 @@ class ModuleDecoder : public Decoder {
}
case kExternalMemory: {
// ===== Imported memory =========================================
bool has_max = false;
consume_resizable_limits("memory", "pages", kV8MaxWasmMemoryPages,
&module->min_mem_pages, &has_max,
kSpecMaxWasmMemoryPages,
&module->max_mem_pages);
consume_resizable_limits(
"memory", "pages", kV8MaxWasmMemoryPages,
&module->min_mem_pages, &module->has_max_mem,
kSpecMaxWasmMemoryPages, &module->max_mem_pages);
SetHasMemory(module);
break;
}
Expand Down Expand Up @@ -394,10 +393,10 @@ class ModuleDecoder : public Decoder {
uint32_t memory_count = consume_count("memory count", kV8MaxWasmMemories);

for (uint32_t i = 0; ok() && i < memory_count; i++) {
bool has_max = false;
consume_resizable_limits(
"memory", "pages", kV8MaxWasmMemoryPages, &module->min_mem_pages,
&has_max, kSpecMaxWasmMemoryPages, &module->max_mem_pages);
consume_resizable_limits("memory", "pages", kV8MaxWasmMemoryPages,
&module->min_mem_pages, &module->has_max_mem,
kSpecMaxWasmMemoryPages,
&module->max_mem_pages);
}
SetHasMemory(module);
section_iter.advance();
Expand Down
57 changes: 27 additions & 30 deletions src/wasm/wasm-js.cc
Original file line number Diff line number Diff line change
Expand Up @@ -354,22 +354,22 @@ void WebAssemblyInstantiate(const v8::FunctionCallbackInfo<v8::Value>& args) {

bool GetIntegerProperty(v8::Isolate* isolate, ErrorThrower* thrower,
Local<Context> context, Local<v8::Object> object,
Local<String> property, int* result, int lower_bound,
int upper_bound) {
Local<String> property, int* result,
int64_t lower_bound, uint64_t upper_bound) {
v8::MaybeLocal<v8::Value> maybe = object->Get(context, property);
v8::Local<v8::Value> value;
if (maybe.ToLocal(&value)) {
int64_t number;
if (!value->IntegerValue(context).To(&number)) return false;
if (number < static_cast<int64_t>(lower_bound)) {
if (number < lower_bound) {
thrower->RangeError("Property value %" PRId64
" is below the lower bound %d",
" is below the lower bound %" PRIx64,
number, lower_bound);
return false;
}
if (number > static_cast<int64_t>(upper_bound)) {
thrower->RangeError("Property value %" PRId64
" is above the upper bound %d",
" is above the upper bound %" PRIu64,
number, upper_bound);
return false;
}
Expand All @@ -379,8 +379,6 @@ bool GetIntegerProperty(v8::Isolate* isolate, ErrorThrower* thrower,
return false;
}

const int max_table_size = 1 << 26;

void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate);
Expand Down Expand Up @@ -408,28 +406,23 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
}
}
// The descriptor's 'initial'.
int initial;
int initial = 0;
if (!GetIntegerProperty(isolate, &thrower, context, descriptor,
v8_str(isolate, "initial"), &initial, 0,
max_table_size)) {
i::wasm::kV8MaxWasmTableSize)) {
return;
}
// The descriptor's 'maximum'.
int maximum = 0;
int maximum = -1;
Local<String> maximum_key = v8_str(isolate, "maximum");
Maybe<bool> has_maximum = descriptor->Has(context, maximum_key);

if (has_maximum.IsNothing()) {
// There has been an exception, just return.
return;
}
if (has_maximum.FromJust()) {
if (!has_maximum.IsNothing() && has_maximum.FromJust()) {
if (!GetIntegerProperty(isolate, &thrower, context, descriptor, maximum_key,
&maximum, initial, max_table_size)) {
&maximum, initial,
i::wasm::kSpecMaxWasmTableSize)) {
return;
}
} else {
maximum = static_cast<int>(i::wasm::kV8MaxWasmTableSize);
}

i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
Expand All @@ -452,23 +445,21 @@ void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
Local<Context> context = isolate->GetCurrentContext();
Local<v8::Object> descriptor = args[0]->ToObject(context).ToLocalChecked();
// The descriptor's 'initial'.
int initial;
int initial = 0;
if (!GetIntegerProperty(isolate, &thrower, context, descriptor,
v8_str(isolate, "initial"), &initial, 0, 65536)) {
v8_str(isolate, "initial"), &initial, 0,
i::wasm::kV8MaxWasmMemoryPages)) {
return;
}
// The descriptor's 'maximum'.
int maximum = 0;
int maximum = -1;
Local<String> maximum_key = v8_str(isolate, "maximum");
Maybe<bool> has_maximum = descriptor->Has(context, maximum_key);

if (has_maximum.IsNothing()) {
// There has been an exception, just return.
return;
}
if (has_maximum.FromJust()) {
if (!has_maximum.IsNothing() && has_maximum.FromJust()) {
if (!GetIntegerProperty(isolate, &thrower, context, descriptor, maximum_key,
&maximum, initial, 65536)) {
&maximum, initial,
i::wasm::kSpecMaxWasmMemoryPages)) {
return;
}
}
Expand All @@ -481,8 +472,8 @@ void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
thrower.RangeError("could not allocate memory");
return;
}
i::Handle<i::JSObject> memory_obj = i::WasmMemoryObject::New(
i_isolate, buffer, has_maximum.FromJust() ? maximum : -1);
i::Handle<i::JSObject> memory_obj =
i::WasmMemoryObject::New(i_isolate, buffer, maximum);
args.GetReturnValue().Set(Utils::ToLocal(memory_obj));
}

Expand Down Expand Up @@ -523,7 +514,13 @@ void WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
}
new_size64 += old_size;

if (new_size64 < old_size || new_size64 > receiver->maximum_length()) {
int64_t max_size64 = receiver->maximum_length();
if (max_size64 < 0 ||
max_size64 > static_cast<int64_t>(i::wasm::kV8MaxWasmTableSize)) {
max_size64 = i::wasm::kV8MaxWasmTableSize;
}

if (new_size64 < old_size || new_size64 > max_size64) {
v8::Local<v8::Value> e = v8::Exception::RangeError(
v8_str(isolate, new_size64 < old_size ? "trying to shrink table"
: "maximum table size exceeded"));
Expand Down
1 change: 1 addition & 0 deletions src/wasm/wasm-limits.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const size_t kV8MaxWasmTables = 1;
const size_t kV8MaxWasmMemories = 1;

const size_t kSpecMaxWasmMemoryPages = 65536;
const size_t kSpecMaxWasmTableSize = 0xFFFFFFFFu;

const uint64_t kWasmMaxHeapOffset =
static_cast<uint64_t>(
Expand Down
66 changes: 54 additions & 12 deletions src/wasm/wasm-module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1669,16 +1669,34 @@ class WasmInstanceBuilder {
table_instance.js_wrappers = Handle<FixedArray>(
table_instance.table_object->functions(), isolate_);

// TODO(titzer): import table size must match exactly for now.
int table_size = table_instance.js_wrappers->length();
if (table_size != static_cast<int>(table.min_size)) {
int imported_cur_size = table_instance.js_wrappers->length();
if (imported_cur_size < static_cast<int>(table.min_size)) {
thrower_->LinkError(
"table import %d is wrong size (%d), expected %u", index,
table_size, table.min_size);
"table import %d is smaller than minimum %d, got %u", index,
table.min_size, imported_cur_size);
return -1;
}

if (table.has_max) {
int64_t imported_max_size =
table_instance.table_object->maximum_length();
if (imported_max_size < 0) {
thrower_->LinkError(
"table import %d has no maximum length, expected %d", index,
table.max_size);
return -1;
}
if (imported_max_size > table.max_size) {
thrower_->LinkError(
"table import %d has maximum larger than maximum %d, "
"got %" PRIx64,
index, table.max_size, imported_max_size);
return -1;
}
}

// Allocate a new dispatch table and signature table.
int table_size = imported_cur_size;
table_instance.function_table =
isolate_->factory()->NewFixedArray(table_size);
table_instance.signature_table =
Expand Down Expand Up @@ -1720,6 +1738,29 @@ class WasmInstanceBuilder {
DCHECK(WasmJs::IsWasmMemoryObject(isolate_, memory));
instance->set_memory_object(*memory);
memory_ = Handle<JSArrayBuffer>(memory->buffer(), isolate_);
uint32_t imported_cur_pages = static_cast<uint32_t>(
memory_->byte_length()->Number() / WasmModule::kPageSize);
if (imported_cur_pages < module_->min_mem_pages) {
thrower_->LinkError(
"memory import %d is smaller than maximum %u, got %u", index,
module_->min_mem_pages, imported_cur_pages);
}
int32_t imported_max_pages = memory->maximum_pages();
if (module_->has_max_mem) {
if (imported_max_pages < 0) {
thrower_->LinkError(
"memory import %d has no maximum limit, expected at most %u",
index, imported_max_pages);
return -1;
}
if (static_cast<uint32_t>(imported_max_pages) >
module_->max_mem_pages) {
thrower_->LinkError(
"memory import %d has larger maximum than maximum %u, got %d",
index, module_->max_mem_pages, imported_max_pages);
return -1;
}
}
break;
}
case kExternalGlobal: {
Expand Down Expand Up @@ -2212,13 +2253,14 @@ int32_t wasm::GetInstanceMemorySize(Isolate* isolate,
}
}

uint32_t GetMaxInstanceMemorySize(Isolate* isolate,
Handle<WasmInstanceObject> instance) {
uint32_t GetMaxInstanceMemoryPages(Isolate* isolate,
Handle<WasmInstanceObject> instance) {
if (instance->has_memory_object()) {
Handle<WasmMemoryObject> memory_object(instance->memory_object(), isolate);

int maximum = memory_object->maximum_pages();
if (maximum > 0) return static_cast<uint32_t>(maximum);
if (memory_object->has_maximum_pages()) {
uint32_t maximum = static_cast<uint32_t>(memory_object->maximum_pages());
if (maximum < kV8MaxWasmMemoryPages) return maximum;
}
}
uint32_t compiled_max_pages = instance->compiled_module()->max_mem_pages();
isolate->counters()->wasm_max_mem_pages_count()->AddSample(
Expand Down Expand Up @@ -2294,7 +2336,7 @@ int32_t wasm::GrowWebAssemblyMemory(Isolate* isolate, Handle<Object> receiver,
Handle<WasmInstanceObject> instance = instance_wrapper->instance_object();
DCHECK(IsWasmInstance(*instance));
if (pages == 0) return GetInstanceMemorySize(isolate, instance);
uint32_t max_pages = GetMaxInstanceMemorySize(isolate, instance);
uint32_t max_pages = GetMaxInstanceMemoryPages(isolate, instance);

// Grow memory object buffer and update instances associated with it.
MaybeHandle<JSArrayBuffer> memory_buffer = handle(memory_object->buffer());
Expand Down Expand Up @@ -2342,7 +2384,7 @@ int32_t wasm::GrowMemory(Isolate* isolate, Handle<WasmInstanceObject> instance,
old_size = old_buffer->byte_length()->Number();
old_mem_start = static_cast<Address>(old_buffer->backing_store());
}
uint32_t max_pages = GetMaxInstanceMemorySize(isolate, instance_obj);
uint32_t max_pages = GetMaxInstanceMemoryPages(isolate, instance_obj);
Handle<JSArrayBuffer> buffer =
GrowMemoryBuffer(isolate, instance_buffer, pages, max_pages);
if (buffer.is_null()) return -1;
Expand Down
1 change: 1 addition & 0 deletions src/wasm/wasm-module.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ struct V8_EXPORT_PRIVATE WasmModule {
Zone* owned_zone;
uint32_t min_mem_pages = 0; // minimum size of the memory in 64k pages
uint32_t max_mem_pages = 0; // maximum size of the memory in 64k pages
bool has_max_mem = false; // try if a maximum memory size exists
bool has_memory = false; // true if the memory was defined or imported
bool mem_export = false; // true if the memory is exported
// TODO(wasm): reconcile start function index being an int with
Expand Down
26 changes: 17 additions & 9 deletions src/wasm/wasm-objects.cc
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ DEFINE_OBJ_GETTER(WasmModuleObject, compiled_module, kCompiledModule,
WasmCompiledModule)

Handle<WasmTableObject> WasmTableObject::New(Isolate* isolate, uint32_t initial,
uint32_t maximum,
int64_t maximum,
Handle<FixedArray>* js_functions) {
Handle<JSFunction> table_ctor(
isolate->native_context()->wasm_table_constructor());
Expand All @@ -133,8 +133,8 @@ Handle<WasmTableObject> WasmTableObject::New(Isolate* isolate, uint32_t initial,
(*js_functions)->set(i, null);
}
table_obj->SetInternalField(kFunctions, *(*js_functions));
table_obj->SetInternalField(kMaximum,
static_cast<Object*>(Smi::FromInt(maximum)));
Handle<Object> max = isolate->factory()->NewNumber(maximum);
table_obj->SetInternalField(kMaximum, *max);

Handle<FixedArray> dispatch_tables = isolate->factory()->NewFixedArray(0);
table_obj->SetInternalField(kDispatchTables, *dispatch_tables);
Expand Down Expand Up @@ -176,8 +176,12 @@ DEFINE_OBJ_ACCESSORS(WasmTableObject, functions, kFunctions, FixedArray)

uint32_t WasmTableObject::current_length() { return functions()->length(); }

uint32_t WasmTableObject::maximum_length() {
return SafeUint32(GetInternalField(kMaximum));
bool WasmTableObject::has_maximum_length() {
return GetInternalField(kMaximum)->Number() >= 0;
}

int64_t WasmTableObject::maximum_length() {
return static_cast<int64_t>(GetInternalField(kMaximum)->Number());
}

WasmTableObject* WasmTableObject::cast(Object* object) {
Expand All @@ -195,14 +199,14 @@ void WasmTableObject::Grow(Isolate* isolate, Handle<WasmTableObject> table,

Handle<WasmMemoryObject> WasmMemoryObject::New(Isolate* isolate,
Handle<JSArrayBuffer> buffer,
int maximum) {
int32_t maximum) {
Handle<JSFunction> memory_ctor(
isolate->native_context()->wasm_memory_constructor());
Handle<JSObject> memory_obj =
isolate->factory()->NewJSObject(memory_ctor, TENURED);
memory_obj->SetInternalField(kArrayBuffer, *buffer);
memory_obj->SetInternalField(kMaximum,
static_cast<Object*>(Smi::FromInt(maximum)));
Handle<Object> max = isolate->factory()->NewNumber(maximum);
memory_obj->SetInternalField(kMaximum, *max);
Handle<Symbol> memory_sym(isolate->native_context()->wasm_memory_sym());
Object::SetProperty(memory_obj, memory_sym, memory_obj, STRICT).Check();
return Handle<WasmMemoryObject>::cast(memory_obj);
Expand All @@ -216,8 +220,12 @@ uint32_t WasmMemoryObject::current_pages() {
return SafeUint32(buffer()->byte_length()) / wasm::WasmModule::kPageSize;
}

bool WasmMemoryObject::has_maximum_pages() {
return GetInternalField(kMaximum)->Number() >= 0;
}

int32_t WasmMemoryObject::maximum_pages() {
return SafeInt32(GetInternalField(kMaximum));
return static_cast<int32_t>(GetInternalField(kMaximum)->Number());
}

WasmMemoryObject* WasmMemoryObject::cast(Object* object) {
Expand Down
11 changes: 7 additions & 4 deletions src/wasm/wasm-objects.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "src/objects-inl.h"
#include "src/trap-handler/trap-handler.h"
#include "src/wasm/managed.h"
#include "src/wasm/wasm-limits.h"

namespace v8 {
namespace internal {
Expand Down Expand Up @@ -60,10 +61,11 @@ class WasmTableObject : public JSObject {

FixedArray* dispatch_tables();
uint32_t current_length();
uint32_t maximum_length();
bool has_maximum_length();
int64_t maximum_length(); // Returns < 0 if no maximum.

static Handle<WasmTableObject> New(Isolate* isolate, uint32_t initial,
uint32_t maximum,
int64_t maximum,
Handle<FixedArray>* js_functions);
static void Grow(Isolate* isolate, Handle<WasmTableObject> table,
uint32_t count);
Expand All @@ -86,11 +88,12 @@ class WasmMemoryObject : public JSObject {
void AddInstance(Isolate* isolate, Handle<WasmInstanceObject> object);
void ResetInstancesLink(Isolate* isolate);
uint32_t current_pages();
int32_t maximum_pages(); // returns < 0 if there is no maximum
bool has_maximum_pages();
int32_t maximum_pages(); // Returns < 0 if there is no maximum.

static Handle<WasmMemoryObject> New(Isolate* isolate,
Handle<JSArrayBuffer> buffer,
int maximum);
int32_t maximum);

static bool Grow(Isolate* isolate, Handle<WasmMemoryObject> memory,
uint32_t count);
Expand Down
2 changes: 1 addition & 1 deletion test/mjsunit/wasm/compiled-module-serialization.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");

(function SerializeAndDeserializeModule() {
var builder = new WasmModuleBuilder();
builder.addImportedMemory("", "memory", 1,1);
builder.addImportedMemory("", "memory", 1);
var kSig_v_i = makeSig([kWasmI32], []);
var signature = builder.addType(kSig_v_i);
builder.addImport("", "some_value", kSig_i_v);
Expand Down
Loading

0 comments on commit 8d76f0e

Please sign in to comment.