Skip to content

Commit 8d76f0e

Browse files
titzerCommit bot
authored andcommitted
[wasm] Enforce memory and table limits during instantiation.
[email protected] BUG=chromium:575167 Review-Url: https://codereview.chromium.org/2636173002 Cr-Commit-Position: refs/heads/master@{#42426}
1 parent cfa6ce3 commit 8d76f0e

13 files changed

+182
-77
lines changed

src/wasm/module-decoder.cc

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -319,11 +319,10 @@ class ModuleDecoder : public Decoder {
319319
}
320320
case kExternalMemory: {
321321
// ===== Imported memory =========================================
322-
bool has_max = false;
323-
consume_resizable_limits("memory", "pages", kV8MaxWasmMemoryPages,
324-
&module->min_mem_pages, &has_max,
325-
kSpecMaxWasmMemoryPages,
326-
&module->max_mem_pages);
322+
consume_resizable_limits(
323+
"memory", "pages", kV8MaxWasmMemoryPages,
324+
&module->min_mem_pages, &module->has_max_mem,
325+
kSpecMaxWasmMemoryPages, &module->max_mem_pages);
327326
SetHasMemory(module);
328327
break;
329328
}
@@ -394,10 +393,10 @@ class ModuleDecoder : public Decoder {
394393
uint32_t memory_count = consume_count("memory count", kV8MaxWasmMemories);
395394

396395
for (uint32_t i = 0; ok() && i < memory_count; i++) {
397-
bool has_max = false;
398-
consume_resizable_limits(
399-
"memory", "pages", kV8MaxWasmMemoryPages, &module->min_mem_pages,
400-
&has_max, kSpecMaxWasmMemoryPages, &module->max_mem_pages);
396+
consume_resizable_limits("memory", "pages", kV8MaxWasmMemoryPages,
397+
&module->min_mem_pages, &module->has_max_mem,
398+
kSpecMaxWasmMemoryPages,
399+
&module->max_mem_pages);
401400
}
402401
SetHasMemory(module);
403402
section_iter.advance();

src/wasm/wasm-js.cc

Lines changed: 27 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -354,22 +354,22 @@ void WebAssemblyInstantiate(const v8::FunctionCallbackInfo<v8::Value>& args) {
354354

355355
bool GetIntegerProperty(v8::Isolate* isolate, ErrorThrower* thrower,
356356
Local<Context> context, Local<v8::Object> object,
357-
Local<String> property, int* result, int lower_bound,
358-
int upper_bound) {
357+
Local<String> property, int* result,
358+
int64_t lower_bound, uint64_t upper_bound) {
359359
v8::MaybeLocal<v8::Value> maybe = object->Get(context, property);
360360
v8::Local<v8::Value> value;
361361
if (maybe.ToLocal(&value)) {
362362
int64_t number;
363363
if (!value->IntegerValue(context).To(&number)) return false;
364-
if (number < static_cast<int64_t>(lower_bound)) {
364+
if (number < lower_bound) {
365365
thrower->RangeError("Property value %" PRId64
366-
" is below the lower bound %d",
366+
" is below the lower bound %" PRIx64,
367367
number, lower_bound);
368368
return false;
369369
}
370370
if (number > static_cast<int64_t>(upper_bound)) {
371371
thrower->RangeError("Property value %" PRId64
372-
" is above the upper bound %d",
372+
" is above the upper bound %" PRIu64,
373373
number, upper_bound);
374374
return false;
375375
}
@@ -379,8 +379,6 @@ bool GetIntegerProperty(v8::Isolate* isolate, ErrorThrower* thrower,
379379
return false;
380380
}
381381

382-
const int max_table_size = 1 << 26;
383-
384382
void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
385383
v8::Isolate* isolate = args.GetIsolate();
386384
HandleScope scope(isolate);
@@ -408,28 +406,23 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
408406
}
409407
}
410408
// The descriptor's 'initial'.
411-
int initial;
409+
int initial = 0;
412410
if (!GetIntegerProperty(isolate, &thrower, context, descriptor,
413411
v8_str(isolate, "initial"), &initial, 0,
414-
max_table_size)) {
412+
i::wasm::kV8MaxWasmTableSize)) {
415413
return;
416414
}
417415
// The descriptor's 'maximum'.
418-
int maximum = 0;
416+
int maximum = -1;
419417
Local<String> maximum_key = v8_str(isolate, "maximum");
420418
Maybe<bool> has_maximum = descriptor->Has(context, maximum_key);
421419

422-
if (has_maximum.IsNothing()) {
423-
// There has been an exception, just return.
424-
return;
425-
}
426-
if (has_maximum.FromJust()) {
420+
if (!has_maximum.IsNothing() && has_maximum.FromJust()) {
427421
if (!GetIntegerProperty(isolate, &thrower, context, descriptor, maximum_key,
428-
&maximum, initial, max_table_size)) {
422+
&maximum, initial,
423+
i::wasm::kSpecMaxWasmTableSize)) {
429424
return;
430425
}
431-
} else {
432-
maximum = static_cast<int>(i::wasm::kV8MaxWasmTableSize);
433426
}
434427

435428
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
@@ -452,23 +445,21 @@ void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
452445
Local<Context> context = isolate->GetCurrentContext();
453446
Local<v8::Object> descriptor = args[0]->ToObject(context).ToLocalChecked();
454447
// The descriptor's 'initial'.
455-
int initial;
448+
int initial = 0;
456449
if (!GetIntegerProperty(isolate, &thrower, context, descriptor,
457-
v8_str(isolate, "initial"), &initial, 0, 65536)) {
450+
v8_str(isolate, "initial"), &initial, 0,
451+
i::wasm::kV8MaxWasmMemoryPages)) {
458452
return;
459453
}
460454
// The descriptor's 'maximum'.
461-
int maximum = 0;
455+
int maximum = -1;
462456
Local<String> maximum_key = v8_str(isolate, "maximum");
463457
Maybe<bool> has_maximum = descriptor->Has(context, maximum_key);
464458

465-
if (has_maximum.IsNothing()) {
466-
// There has been an exception, just return.
467-
return;
468-
}
469-
if (has_maximum.FromJust()) {
459+
if (!has_maximum.IsNothing() && has_maximum.FromJust()) {
470460
if (!GetIntegerProperty(isolate, &thrower, context, descriptor, maximum_key,
471-
&maximum, initial, 65536)) {
461+
&maximum, initial,
462+
i::wasm::kSpecMaxWasmMemoryPages)) {
472463
return;
473464
}
474465
}
@@ -481,8 +472,8 @@ void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
481472
thrower.RangeError("could not allocate memory");
482473
return;
483474
}
484-
i::Handle<i::JSObject> memory_obj = i::WasmMemoryObject::New(
485-
i_isolate, buffer, has_maximum.FromJust() ? maximum : -1);
475+
i::Handle<i::JSObject> memory_obj =
476+
i::WasmMemoryObject::New(i_isolate, buffer, maximum);
486477
args.GetReturnValue().Set(Utils::ToLocal(memory_obj));
487478
}
488479

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

526-
if (new_size64 < old_size || new_size64 > receiver->maximum_length()) {
517+
int64_t max_size64 = receiver->maximum_length();
518+
if (max_size64 < 0 ||
519+
max_size64 > static_cast<int64_t>(i::wasm::kV8MaxWasmTableSize)) {
520+
max_size64 = i::wasm::kV8MaxWasmTableSize;
521+
}
522+
523+
if (new_size64 < old_size || new_size64 > max_size64) {
527524
v8::Local<v8::Value> e = v8::Exception::RangeError(
528525
v8_str(isolate, new_size64 < old_size ? "trying to shrink table"
529526
: "maximum table size exceeded"));

src/wasm/wasm-limits.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const size_t kV8MaxWasmTables = 1;
3131
const size_t kV8MaxWasmMemories = 1;
3232

3333
const size_t kSpecMaxWasmMemoryPages = 65536;
34+
const size_t kSpecMaxWasmTableSize = 0xFFFFFFFFu;
3435

3536
const uint64_t kWasmMaxHeapOffset =
3637
static_cast<uint64_t>(

src/wasm/wasm-module.cc

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1669,16 +1669,34 @@ class WasmInstanceBuilder {
16691669
table_instance.js_wrappers = Handle<FixedArray>(
16701670
table_instance.table_object->functions(), isolate_);
16711671

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

1680+
if (table.has_max) {
1681+
int64_t imported_max_size =
1682+
table_instance.table_object->maximum_length();
1683+
if (imported_max_size < 0) {
1684+
thrower_->LinkError(
1685+
"table import %d has no maximum length, expected %d", index,
1686+
table.max_size);
1687+
return -1;
1688+
}
1689+
if (imported_max_size > table.max_size) {
1690+
thrower_->LinkError(
1691+
"table import %d has maximum larger than maximum %d, "
1692+
"got %" PRIx64,
1693+
index, table.max_size, imported_max_size);
1694+
return -1;
1695+
}
1696+
}
1697+
16811698
// Allocate a new dispatch table and signature table.
1699+
int table_size = imported_cur_size;
16821700
table_instance.function_table =
16831701
isolate_->factory()->NewFixedArray(table_size);
16841702
table_instance.signature_table =
@@ -1720,6 +1738,29 @@ class WasmInstanceBuilder {
17201738
DCHECK(WasmJs::IsWasmMemoryObject(isolate_, memory));
17211739
instance->set_memory_object(*memory);
17221740
memory_ = Handle<JSArrayBuffer>(memory->buffer(), isolate_);
1741+
uint32_t imported_cur_pages = static_cast<uint32_t>(
1742+
memory_->byte_length()->Number() / WasmModule::kPageSize);
1743+
if (imported_cur_pages < module_->min_mem_pages) {
1744+
thrower_->LinkError(
1745+
"memory import %d is smaller than maximum %u, got %u", index,
1746+
module_->min_mem_pages, imported_cur_pages);
1747+
}
1748+
int32_t imported_max_pages = memory->maximum_pages();
1749+
if (module_->has_max_mem) {
1750+
if (imported_max_pages < 0) {
1751+
thrower_->LinkError(
1752+
"memory import %d has no maximum limit, expected at most %u",
1753+
index, imported_max_pages);
1754+
return -1;
1755+
}
1756+
if (static_cast<uint32_t>(imported_max_pages) >
1757+
module_->max_mem_pages) {
1758+
thrower_->LinkError(
1759+
"memory import %d has larger maximum than maximum %u, got %d",
1760+
index, module_->max_mem_pages, imported_max_pages);
1761+
return -1;
1762+
}
1763+
}
17231764
break;
17241765
}
17251766
case kExternalGlobal: {
@@ -2212,13 +2253,14 @@ int32_t wasm::GetInstanceMemorySize(Isolate* isolate,
22122253
}
22132254
}
22142255

2215-
uint32_t GetMaxInstanceMemorySize(Isolate* isolate,
2216-
Handle<WasmInstanceObject> instance) {
2256+
uint32_t GetMaxInstanceMemoryPages(Isolate* isolate,
2257+
Handle<WasmInstanceObject> instance) {
22172258
if (instance->has_memory_object()) {
22182259
Handle<WasmMemoryObject> memory_object(instance->memory_object(), isolate);
2219-
2220-
int maximum = memory_object->maximum_pages();
2221-
if (maximum > 0) return static_cast<uint32_t>(maximum);
2260+
if (memory_object->has_maximum_pages()) {
2261+
uint32_t maximum = static_cast<uint32_t>(memory_object->maximum_pages());
2262+
if (maximum < kV8MaxWasmMemoryPages) return maximum;
2263+
}
22222264
}
22232265
uint32_t compiled_max_pages = instance->compiled_module()->max_mem_pages();
22242266
isolate->counters()->wasm_max_mem_pages_count()->AddSample(
@@ -2294,7 +2336,7 @@ int32_t wasm::GrowWebAssemblyMemory(Isolate* isolate, Handle<Object> receiver,
22942336
Handle<WasmInstanceObject> instance = instance_wrapper->instance_object();
22952337
DCHECK(IsWasmInstance(*instance));
22962338
if (pages == 0) return GetInstanceMemorySize(isolate, instance);
2297-
uint32_t max_pages = GetMaxInstanceMemorySize(isolate, instance);
2339+
uint32_t max_pages = GetMaxInstanceMemoryPages(isolate, instance);
22982340

22992341
// Grow memory object buffer and update instances associated with it.
23002342
MaybeHandle<JSArrayBuffer> memory_buffer = handle(memory_object->buffer());
@@ -2342,7 +2384,7 @@ int32_t wasm::GrowMemory(Isolate* isolate, Handle<WasmInstanceObject> instance,
23422384
old_size = old_buffer->byte_length()->Number();
23432385
old_mem_start = static_cast<Address>(old_buffer->backing_store());
23442386
}
2345-
uint32_t max_pages = GetMaxInstanceMemorySize(isolate, instance_obj);
2387+
uint32_t max_pages = GetMaxInstanceMemoryPages(isolate, instance_obj);
23462388
Handle<JSArrayBuffer> buffer =
23472389
GrowMemoryBuffer(isolate, instance_buffer, pages, max_pages);
23482390
if (buffer.is_null()) return -1;

src/wasm/wasm-module.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ struct V8_EXPORT_PRIVATE WasmModule {
179179
Zone* owned_zone;
180180
uint32_t min_mem_pages = 0; // minimum size of the memory in 64k pages
181181
uint32_t max_mem_pages = 0; // maximum size of the memory in 64k pages
182+
bool has_max_mem = false; // try if a maximum memory size exists
182183
bool has_memory = false; // true if the memory was defined or imported
183184
bool mem_export = false; // true if the memory is exported
184185
// TODO(wasm): reconcile start function index being an int with

src/wasm/wasm-objects.cc

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ DEFINE_OBJ_GETTER(WasmModuleObject, compiled_module, kCompiledModule,
122122
WasmCompiledModule)
123123

124124
Handle<WasmTableObject> WasmTableObject::New(Isolate* isolate, uint32_t initial,
125-
uint32_t maximum,
125+
int64_t maximum,
126126
Handle<FixedArray>* js_functions) {
127127
Handle<JSFunction> table_ctor(
128128
isolate->native_context()->wasm_table_constructor());
@@ -133,8 +133,8 @@ Handle<WasmTableObject> WasmTableObject::New(Isolate* isolate, uint32_t initial,
133133
(*js_functions)->set(i, null);
134134
}
135135
table_obj->SetInternalField(kFunctions, *(*js_functions));
136-
table_obj->SetInternalField(kMaximum,
137-
static_cast<Object*>(Smi::FromInt(maximum)));
136+
Handle<Object> max = isolate->factory()->NewNumber(maximum);
137+
table_obj->SetInternalField(kMaximum, *max);
138138

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

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

179-
uint32_t WasmTableObject::maximum_length() {
180-
return SafeUint32(GetInternalField(kMaximum));
179+
bool WasmTableObject::has_maximum_length() {
180+
return GetInternalField(kMaximum)->Number() >= 0;
181+
}
182+
183+
int64_t WasmTableObject::maximum_length() {
184+
return static_cast<int64_t>(GetInternalField(kMaximum)->Number());
181185
}
182186

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

196200
Handle<WasmMemoryObject> WasmMemoryObject::New(Isolate* isolate,
197201
Handle<JSArrayBuffer> buffer,
198-
int maximum) {
202+
int32_t maximum) {
199203
Handle<JSFunction> memory_ctor(
200204
isolate->native_context()->wasm_memory_constructor());
201205
Handle<JSObject> memory_obj =
202206
isolate->factory()->NewJSObject(memory_ctor, TENURED);
203207
memory_obj->SetInternalField(kArrayBuffer, *buffer);
204-
memory_obj->SetInternalField(kMaximum,
205-
static_cast<Object*>(Smi::FromInt(maximum)));
208+
Handle<Object> max = isolate->factory()->NewNumber(maximum);
209+
memory_obj->SetInternalField(kMaximum, *max);
206210
Handle<Symbol> memory_sym(isolate->native_context()->wasm_memory_sym());
207211
Object::SetProperty(memory_obj, memory_sym, memory_obj, STRICT).Check();
208212
return Handle<WasmMemoryObject>::cast(memory_obj);
@@ -216,8 +220,12 @@ uint32_t WasmMemoryObject::current_pages() {
216220
return SafeUint32(buffer()->byte_length()) / wasm::WasmModule::kPageSize;
217221
}
218222

223+
bool WasmMemoryObject::has_maximum_pages() {
224+
return GetInternalField(kMaximum)->Number() >= 0;
225+
}
226+
219227
int32_t WasmMemoryObject::maximum_pages() {
220-
return SafeInt32(GetInternalField(kMaximum));
228+
return static_cast<int32_t>(GetInternalField(kMaximum)->Number());
221229
}
222230

223231
WasmMemoryObject* WasmMemoryObject::cast(Object* object) {

src/wasm/wasm-objects.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "src/objects-inl.h"
1010
#include "src/trap-handler/trap-handler.h"
1111
#include "src/wasm/managed.h"
12+
#include "src/wasm/wasm-limits.h"
1213

1314
namespace v8 {
1415
namespace internal {
@@ -60,10 +61,11 @@ class WasmTableObject : public JSObject {
6061

6162
FixedArray* dispatch_tables();
6263
uint32_t current_length();
63-
uint32_t maximum_length();
64+
bool has_maximum_length();
65+
int64_t maximum_length(); // Returns < 0 if no maximum.
6466

6567
static Handle<WasmTableObject> New(Isolate* isolate, uint32_t initial,
66-
uint32_t maximum,
68+
int64_t maximum,
6769
Handle<FixedArray>* js_functions);
6870
static void Grow(Isolate* isolate, Handle<WasmTableObject> table,
6971
uint32_t count);
@@ -86,11 +88,12 @@ class WasmMemoryObject : public JSObject {
8688
void AddInstance(Isolate* isolate, Handle<WasmInstanceObject> object);
8789
void ResetInstancesLink(Isolate* isolate);
8890
uint32_t current_pages();
89-
int32_t maximum_pages(); // returns < 0 if there is no maximum
91+
bool has_maximum_pages();
92+
int32_t maximum_pages(); // Returns < 0 if there is no maximum.
9093

9194
static Handle<WasmMemoryObject> New(Isolate* isolate,
9295
Handle<JSArrayBuffer> buffer,
93-
int maximum);
96+
int32_t maximum);
9497

9598
static bool Grow(Isolate* isolate, Handle<WasmMemoryObject> memory,
9699
uint32_t count);

test/mjsunit/wasm/compiled-module-serialization.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
99

1010
(function SerializeAndDeserializeModule() {
1111
var builder = new WasmModuleBuilder();
12-
builder.addImportedMemory("", "memory", 1,1);
12+
builder.addImportedMemory("", "memory", 1);
1313
var kSig_v_i = makeSig([kWasmI32], []);
1414
var signature = builder.addType(kSig_v_i);
1515
builder.addImport("", "some_value", kSig_i_v);

0 commit comments

Comments
 (0)