Bug 1310949: Tighten Global parsing/decoding; r=luke

MozReview-Commit-ID: 4AVFOFWlgub

--HG--
rename : js/src/asmjs/WasmBinary.cpp => js/src/asmjs/WasmBinaryFormat.cpp
extra : rebase_source : ac85ca85277beb0fb07f18fb3676da30d11b49e2
This commit is contained in:
Benjamin Bouvier 2016-10-21 19:10:00 +02:00
Родитель a738b37621
Коммит 7d330e2dd1
12 изменённых файлов: 326 добавлений и 262 удалений

Просмотреть файл

@ -29,6 +29,7 @@
#include "jswrapper.h"
#include "asmjs/WasmBinaryFormat.h"
#include "asmjs/WasmGenerator.h"
#include "asmjs/WasmInstance.h"
#include "asmjs/WasmJS.h"

Просмотреть файл

@ -595,21 +595,21 @@ class AstFunc : public AstNode
class AstGlobal : public AstNode
{
AstName name_;
uint32_t flags_;
bool isMutable_;
ValType type_;
Maybe<AstExpr*> init_;
public:
AstGlobal() : flags_(0), type_(ValType::Limit)
AstGlobal() : isMutable_(false), type_(ValType::Limit)
{}
explicit AstGlobal(AstName name, ValType type, uint32_t flags,
explicit AstGlobal(AstName name, ValType type, bool isMutable,
Maybe<AstExpr*> init = Maybe<AstExpr*>())
: name_(name), flags_(flags), type_(type), init_(init)
: name_(name), isMutable_(isMutable), type_(type), init_(init)
{}
AstName name() const { return name_; }
uint32_t flags() const { return flags_; }
bool isMutable() const { return isMutable_; }
ValType type() const { return type_; }
bool hasInit() const { return !!init_; }

Просмотреть файл

@ -94,6 +94,7 @@
#include "mozilla/MathAlgorithms.h"
#include "asmjs/WasmBinaryFormat.h"
#include "asmjs/WasmBinaryIterator.h"
#include "asmjs/WasmGenerator.h"
#include "asmjs/WasmSignalHandlers.h"

Просмотреть файл

@ -41,146 +41,10 @@ Decoder::fail(const char* msg, ...) {
bool
Decoder::fail(UniqueChars msg) {
MOZ_ASSERT(error_);
UniqueChars strWithOffset(JS_smprintf("at offset %" PRIuSIZE ": %s",
currentOffset(), msg.get()));
UniqueChars strWithOffset(JS_smprintf("at offset %" PRIuSIZE ": %s", currentOffset(), msg.get()));
if (!strWithOffset)
return false;
*error_ = Move(strWithOffset);
return false;
}
bool
wasm::DecodePreamble(Decoder& d)
{
uint32_t u32;
if (!d.readFixedU32(&u32) || u32 != MagicNumber)
return d.fail("failed to match magic number");
if (!d.readFixedU32(&u32) || u32 != EncodingVersion)
return d.fail("binary version 0x%" PRIx32 " does not match expected version 0x%" PRIx32,
u32, EncodingVersion);
return true;
}
bool
wasm::EncodeLocalEntries(Encoder& e, const ValTypeVector& locals)
{
uint32_t numLocalEntries = 0;
ValType prev = ValType::Limit;
for (ValType t : locals) {
if (t != prev) {
numLocalEntries++;
prev = t;
}
}
if (!e.writeVarU32(numLocalEntries))
return false;
if (numLocalEntries) {
prev = locals[0];
uint32_t count = 1;
for (uint32_t i = 1; i < locals.length(); i++, count++) {
if (prev != locals[i]) {
if (!e.writeVarU32(count))
return false;
if (!e.writeValType(prev))
return false;
prev = locals[i];
count = 0;
}
}
if (!e.writeVarU32(count))
return false;
if (!e.writeValType(prev))
return false;
}
return true;
}
bool
wasm::DecodeLocalEntries(Decoder& d, ValTypeVector* locals)
{
uint32_t numLocalEntries;
if (!d.readVarU32(&numLocalEntries))
return false;
for (uint32_t i = 0; i < numLocalEntries; i++) {
uint32_t count;
if (!d.readVarU32(&count))
return false;
if (MaxLocals - locals->length() < count)
return false;
ValType type;
if (!d.readValType(&type))
return false;
if (!locals->appendN(type, count))
return false;
}
return true;
}
bool
wasm::DecodeGlobalType(Decoder& d, ValType* type, uint32_t* flags)
{
if (!d.readValType(type))
return d.fail("bad global type");
if (!d.readVarU32(flags))
return d.fail("expected global flags");
if (*flags & ~uint32_t(GlobalFlags::AllowedMask))
return d.fail("unexpected bits set in global flags");
return true;
}
bool
wasm::DecodeLimits(Decoder& d, Limits* limits)
{
uint32_t flags;
if (!d.readVarU32(&flags))
return d.fail("expected flags");
// TODO (bug 1310149): tighten this check (s/3/1) when the AngryBots demo
// gets updated.
if (flags & ~uint32_t(0x3))
return d.fail("unexpected bits set in flags: %" PRIu32, (flags & ~uint32_t(0x3)));
if (!d.readVarU32(&limits->initial))
return d.fail("expected initial length");
if (flags & 0x1) {
uint32_t maximum;
if (!d.readVarU32(&maximum))
return d.fail("expected maximum length");
if (limits->initial > maximum) {
return d.fail("memory size minimum must not be greater than maximum; "
"maximum length %" PRIu32 " is less than initial length %" PRIu32 ,
maximum, limits->initial);
}
limits->maximum.emplace(maximum);
}
return true;
}
bool
wasm::DecodeUnknownSections(Decoder& d)
{
while (!d.done()) {
if (!d.skipUserDefinedSection())
return false;
}
return true;
}

Просмотреть файл

@ -109,6 +109,8 @@ enum class ValType
Limit
};
typedef Vector<ValType, 8, SystemAllocPolicy> ValTypeVector;
enum class TypeConstructor
{
AnyFunc = 0x20,
@ -1072,31 +1074,6 @@ class Decoder
}
};
// Reusable macro encoding/decoding functions reused by both the two
// encoders (AsmJS/WasmText) and decoders (Wasm/WasmIonCompile).
typedef Vector<ValType, 8, SystemAllocPolicy> ValTypeVector;
MOZ_MUST_USE bool
DecodePreamble(Decoder& d);
MOZ_MUST_USE bool
EncodeLocalEntries(Encoder& d, const ValTypeVector& locals);
MOZ_MUST_USE bool
DecodeLocalEntries(Decoder& d, ValTypeVector* locals);
MOZ_MUST_USE bool
DecodeGlobalType(Decoder& d, ValType* type, uint32_t* flags);
struct Limits;
MOZ_MUST_USE bool
DecodeLimits(Decoder& d, Limits* limits);
MOZ_MUST_USE bool
DecodeUnknownSections(Decoder& d);
} // namespace wasm
} // namespace js

Просмотреть файл

@ -0,0 +1,222 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
*
* Copyright 2016 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "asmjs/WasmBinaryFormat.h"
using namespace js;
using namespace js::wasm;
bool
wasm::DecodePreamble(Decoder& d)
{
uint32_t u32;
if (!d.readFixedU32(&u32) || u32 != MagicNumber)
return d.fail("failed to match magic number");
if (!d.readFixedU32(&u32) || u32 != EncodingVersion)
return d.fail("binary version 0x%" PRIx32 " does not match expected version 0x%" PRIx32,
u32, EncodingVersion);
return true;
}
bool
wasm::EncodeLocalEntries(Encoder& e, const ValTypeVector& locals)
{
uint32_t numLocalEntries = 0;
ValType prev = ValType::Limit;
for (ValType t : locals) {
if (t != prev) {
numLocalEntries++;
prev = t;
}
}
if (!e.writeVarU32(numLocalEntries))
return false;
if (numLocalEntries) {
prev = locals[0];
uint32_t count = 1;
for (uint32_t i = 1; i < locals.length(); i++, count++) {
if (prev != locals[i]) {
if (!e.writeVarU32(count))
return false;
if (!e.writeValType(prev))
return false;
prev = locals[i];
count = 0;
}
}
if (!e.writeVarU32(count))
return false;
if (!e.writeValType(prev))
return false;
}
return true;
}
bool
wasm::DecodeLocalEntries(Decoder& d, ValTypeVector* locals)
{
uint32_t numLocalEntries;
if (!d.readVarU32(&numLocalEntries))
return false;
for (uint32_t i = 0; i < numLocalEntries; i++) {
uint32_t count;
if (!d.readVarU32(&count))
return false;
if (MaxLocals - locals->length() < count)
return false;
ValType type;
if (!d.readValType(&type))
return false;
if (!locals->appendN(type, count))
return false;
}
return true;
}
bool
wasm::DecodeGlobalType(Decoder& d, ValType* type, bool* isMutable)
{
if (!d.readValType(type))
return d.fail("bad global type");
uint32_t flags;
if (!d.readVarU32(&flags))
return d.fail("expected global flags");
if (flags & ~uint32_t(GlobalFlags::AllowedMask))
return d.fail("unexpected bits set in global flags");
*isMutable = flags & uint32_t(GlobalFlags::IsMutable);
return true;
}
bool
wasm::DecodeInitializerExpression(Decoder& d, const GlobalDescVector& globals, ValType expected,
InitExpr* init)
{
Expr expr;
if (!d.readExpr(&expr))
return d.fail("failed to read initializer type");
switch (expr) {
case Expr::I32Const: {
int32_t i32;
if (!d.readVarS32(&i32))
return d.fail("failed to read initializer i32 expression");
*init = InitExpr(Val(uint32_t(i32)));
break;
}
case Expr::I64Const: {
int64_t i64;
if (!d.readVarS64(&i64))
return d.fail("failed to read initializer i64 expression");
*init = InitExpr(Val(uint64_t(i64)));
break;
}
case Expr::F32Const: {
RawF32 f32;
if (!d.readFixedF32(&f32))
return d.fail("failed to read initializer f32 expression");
*init = InitExpr(Val(f32));
break;
}
case Expr::F64Const: {
RawF64 f64;
if (!d.readFixedF64(&f64))
return d.fail("failed to read initializer f64 expression");
*init = InitExpr(Val(f64));
break;
}
case Expr::GetGlobal: {
uint32_t i;
if (!d.readVarU32(&i))
return d.fail("failed to read get_global index in initializer expression");
if (i >= globals.length())
return d.fail("global index out of range in initializer expression");
if (!globals[i].isImport() || globals[i].isMutable())
return d.fail("initializer expression must reference a global immutable import");
*init = InitExpr(i, globals[i].type());
break;
}
default: {
return d.fail("unexpected initializer expression");
}
}
if (expected != init->type())
return d.fail("type mismatch: initializer type and expected type don't match");
Expr end;
if (!d.readExpr(&end) || end != Expr::End)
return d.fail("failed to read end of initializer expression");
return true;
}
bool
wasm::DecodeLimits(Decoder& d, Limits* limits)
{
uint32_t flags;
if (!d.readVarU32(&flags))
return d.fail("expected flags");
// TODO (bug 1310149): tighten this check (s/3/1) when the AngryBots demo
// gets updated.
if (flags & ~uint32_t(0x3))
return d.fail("unexpected bits set in flags: %" PRIu32, (flags & ~uint32_t(0x3)));
if (!d.readVarU32(&limits->initial))
return d.fail("expected initial length");
if (flags & 0x1) {
uint32_t maximum;
if (!d.readVarU32(&maximum))
return d.fail("expected maximum length");
if (limits->initial > maximum) {
return d.fail("memory size minimum must not be greater than maximum; "
"maximum length %" PRIu32 " is less than initial length %" PRIu32,
maximum, limits->initial);
}
limits->maximum.emplace(maximum);
}
return true;
}
bool
wasm::DecodeUnknownSections(Decoder& d)
{
while (!d.done()) {
if (!d.skipUserDefinedSection())
return false;
}
return true;
}

Просмотреть файл

@ -0,0 +1,57 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
*
* Copyright 2016 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef wasm_binary_format_h
#define wasm_binary_format_h
#include "asmjs/WasmBinary.h"
#include "asmjs/WasmTypes.h"
namespace js {
namespace wasm {
// Reusable macro encoding/decoding functions reused by both the two
// encoders (AsmJS/WasmTextToBinary) and all the decoders
// (WasmCompile/WasmIonCompile/WasmBaselineCompile/WasmBinaryToText).
MOZ_MUST_USE bool
DecodePreamble(Decoder& d);
MOZ_MUST_USE bool
EncodeLocalEntries(Encoder& d, const ValTypeVector& locals);
MOZ_MUST_USE bool
DecodeLocalEntries(Decoder& d, ValTypeVector* locals);
MOZ_MUST_USE bool
DecodeGlobalType(Decoder& d, ValType* type, bool* isMutable);
MOZ_MUST_USE bool
DecodeInitializerExpression(Decoder& d, const GlobalDescVector& globals, ValType expected,
InitExpr* init);
MOZ_MUST_USE bool
DecodeLimits(Decoder& d, Limits* limits);
MOZ_MUST_USE bool
DecodeUnknownSections(Decoder& d);
} // namespace wasm
} // namespace js
#endif // wasm_binary_format_h

Просмотреть файл

@ -24,6 +24,7 @@
#include "jscntxt.h"
#include "asmjs/WasmBinaryFormat.h"
#include "asmjs/WasmBinaryIterator.h"
using namespace js;
@ -1599,15 +1600,12 @@ AstDecodeImport(AstDecodeContext& c, uint32_t importIndex, AstImport** import)
}
case uint32_t(DefinitionKind::Global): {
ValType type;
if (!c.d.readValType(&type))
return false;
uint32_t flags;
if (!c.d.readVarU32(&flags))
bool isMutable;
if (!DecodeGlobalType(c.d, &type, &isMutable))
return false;
*import = new(c.lifo) AstImport(importName, moduleName, fieldName,
AstGlobal(importName, type, flags));
AstGlobal(importName, type, isMutable));
break;
}
case uint32_t(DefinitionKind::Table): {
@ -1768,15 +1766,15 @@ AstDecodeGlobal(AstDecodeContext& c, uint32_t i, AstGlobal* global)
return false;
ValType type;
uint32_t flags;
if (!DecodeGlobalType(c.d, &type, &flags))
bool isMutable;
if (!DecodeGlobalType(c.d, &type, &isMutable))
return false;
AstExpr* init;
if (!AstDecodeInitializerExpression(c, &init, type))
return c.d.fail("missing initializer expression");
*global = AstGlobal(name, type, flags, Some(init));
*global = AstGlobal(name, type, isMutable, Some(init));
return true;
}

Просмотреть файл

@ -22,6 +22,7 @@
#include "jsprf.h"
#include "asmjs/WasmBinaryFormat.h"
#include "asmjs/WasmBinaryIterator.h"
#include "asmjs/WasmGenerator.h"
#include "asmjs/WasmSignalHandlers.h"
@ -712,10 +713,9 @@ DecodeImport(Decoder& d, ModuleGeneratorData* init, ImportVector* imports)
}
case DefinitionKind::Global: {
ValType type;
uint32_t flags;
if (!DecodeGlobalType(d, &type, &flags))
bool isMutable;
if (!DecodeGlobalType(d, &type, &isMutable))
return false;
bool isMutable = flags & uint32_t(GlobalFlags::IsMutable);
if (!GlobalIsJSCompatible(d, type, isMutable))
return false;
if (!init->globals.append(GlobalDesc(type, isMutable, init->globals.length())))
@ -806,69 +806,6 @@ DecodeMemorySection(Decoder& d, ModuleGeneratorData* init, bool* exported)
return true;
}
static bool
DecodeInitializerExpression(Decoder& d, const GlobalDescVector& globals, ValType expected,
InitExpr* init)
{
Expr expr;
if (!d.readExpr(&expr))
return d.fail("failed to read initializer type");
switch (expr) {
case Expr::I32Const: {
int32_t i32;
if (!d.readVarS32(&i32))
return d.fail("failed to read initializer i32 expression");
*init = InitExpr(Val(uint32_t(i32)));
break;
}
case Expr::I64Const: {
int64_t i64;
if (!d.readVarS64(&i64))
return d.fail("failed to read initializer i64 expression");
*init = InitExpr(Val(uint64_t(i64)));
break;
}
case Expr::F32Const: {
RawF32 f32;
if (!d.readFixedF32(&f32))
return d.fail("failed to read initializer f32 expression");
*init = InitExpr(Val(f32));
break;
}
case Expr::F64Const: {
RawF64 f64;
if (!d.readFixedF64(&f64))
return d.fail("failed to read initializer f64 expression");
*init = InitExpr(Val(f64));
break;
}
case Expr::GetGlobal: {
uint32_t i;
if (!d.readVarU32(&i))
return d.fail("failed to read get_global index in initializer expression");
if (i >= globals.length())
return d.fail("global index out of range in initializer expression");
if (!globals[i].isImport() || globals[i].isMutable())
return d.fail("initializer expression must reference a global immutable import");
*init = InitExpr(i, globals[i].type());
break;
}
default: {
return d.fail("unexpected initializer expression");
}
}
if (expected != init->type())
return d.fail("type mismatch: initializer type and expected type don't match");
Expr end;
if (!d.readExpr(&end) || end != Expr::End)
return d.fail("failed to read end of initializer expression");
return true;
}
static bool
DecodeGlobalSection(Decoder& d, ModuleGeneratorData* init)
{
@ -887,15 +824,14 @@ DecodeGlobalSection(Decoder& d, ModuleGeneratorData* init)
for (uint32_t i = 0; i < numGlobals; i++) {
ValType type;
uint32_t flags;
if (!DecodeGlobalType(d, &type, &flags))
bool isMutable;
if (!DecodeGlobalType(d, &type, &isMutable))
return false;
InitExpr initializer;
if (!DecodeInitializerExpression(d, init->globals, type, &initializer))
return false;
bool isMutable = flags & uint32_t(GlobalFlags::IsMutable);
if (!init->globals.append(GlobalDesc(initializer, isMutable)))
return false;
}

Просмотреть файл

@ -21,6 +21,7 @@
#include "mozilla/MathAlgorithms.h"
#include "asmjs/WasmBaselineCompile.h"
#include "asmjs/WasmBinaryFormat.h"
#include "asmjs/WasmBinaryIterator.h"
#include "asmjs/WasmGenerator.h"
#include "asmjs/WasmSignalHandlers.h"

Просмотреть файл

@ -28,7 +28,7 @@
#include "jsstr.h"
#include "asmjs/WasmAST.h"
#include "asmjs/WasmBinary.h"
#include "asmjs/WasmBinaryFormat.h"
#include "asmjs/WasmTypes.h"
#include "ds/LifoAlloc.h"
#include "js/CharacterEncoding.h"
@ -2844,12 +2844,14 @@ ParseStartFunc(WasmParseContext& c, WasmToken token, AstModule* module)
}
static bool
ParseGlobalType(WasmParseContext& c, WasmToken* typeToken, uint32_t* flags)
ParseGlobalType(WasmParseContext& c, WasmToken* typeToken, bool* isMutable)
{
*isMutable = false;
// Either (mut i32) or i32.
if (c.ts.getIf(WasmToken::OpenParen)) {
// Immutable by default.
*flags = c.ts.getIf(WasmToken::Mutable) ? 0x1 : 0x0;
*isMutable = c.ts.getIf(WasmToken::Mutable);
if (!c.ts.match(WasmToken::ValueType, typeToken, c.error))
return false;
if (!c.ts.match(WasmToken::CloseParen, c.error))
@ -2919,14 +2921,14 @@ ParseImport(WasmParseContext& c, AstModule* module)
name = c.ts.getIfName();
WasmToken typeToken;
uint32_t flags = 0;
if (!ParseGlobalType(c, &typeToken, &flags))
bool isMutable;
if (!ParseGlobalType(c, &typeToken, &isMutable))
return nullptr;
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
return new(c.lifo) AstImport(name, moduleName.text(), fieldName.text(),
AstGlobal(AstName(), typeToken.valueType(), flags));
AstGlobal(AstName(), typeToken.valueType(), isMutable));
}
if (c.ts.getIf(WasmToken::Func)) {
if (name.empty())
@ -3156,7 +3158,7 @@ ParseGlobal(WasmParseContext& c, AstModule* module)
AstName name = c.ts.getIfName();
WasmToken typeToken;
uint32_t flags = 0;
bool isMutable;
WasmToken openParen;
if (c.ts.getIf(WasmToken::OpenParen, &openParen)) {
@ -3172,11 +3174,12 @@ ParseGlobal(WasmParseContext& c, AstModule* module)
if (!c.ts.match(WasmToken::CloseParen, c.error))
return false;
if (!ParseGlobalType(c, &typeToken, &flags))
if (!ParseGlobalType(c, &typeToken, &isMutable))
return false;
auto* imp = new(c.lifo) AstImport(name, names.module.text(), names.field.text(),
AstGlobal(AstName(), typeToken.valueType(), flags));
AstGlobal(AstName(), typeToken.valueType(),
isMutable));
return imp && module->append(imp);
}
@ -3193,14 +3196,14 @@ ParseGlobal(WasmParseContext& c, AstModule* module)
}
}
if (!ParseGlobalType(c, &typeToken, &flags))
if (!ParseGlobalType(c, &typeToken, &isMutable))
return false;
AstExpr* init = ParseExpr(c, true);
if (!init)
return false;
auto* glob = new(c.lifo) AstGlobal(name, typeToken.valueType(), flags, Some(init));
auto* glob = new(c.lifo) AstGlobal(name, typeToken.valueType(), isMutable, Some(init));
return glob && module->append(glob);
}
@ -4310,6 +4313,13 @@ EncodeTableLimits(Encoder& e, const Limits& limits)
return EncodeLimits(e, limits);
}
static bool
EncodeGlobalType(Encoder& e, const AstGlobal* global)
{
return e.writeValType(global->type()) &&
e.writeVarU32(global->isMutable() ? uint32_t(GlobalFlags::IsMutable) : 0);
}
static bool
EncodeImport(Encoder& e, AstImport& imp)
{
@ -4329,9 +4339,7 @@ EncodeImport(Encoder& e, AstImport& imp)
break;
case DefinitionKind::Global:
MOZ_ASSERT(!imp.global().hasInit());
if (!e.writeValType(imp.global().type()))
return false;
if (!e.writeVarU32(imp.global().flags()))
if (!EncodeGlobalType(e, &imp.global()))
return false;
break;
case DefinitionKind::Table:
@ -4406,9 +4414,7 @@ EncodeGlobalSection(Encoder& e, AstModule& module)
for (const AstGlobal* global : globals) {
MOZ_ASSERT(global->hasInit());
if (!e.writeValType(global->type()))
return false;
if (!e.writeVarU32(global->flags()))
if (!EncodeGlobalType(e, global))
return false;
if (!EncodeExpr(e, global->init()))
return false;

Просмотреть файл

@ -157,6 +157,7 @@ UNIFIED_SOURCES += [
'asmjs/AsmJS.cpp',
'asmjs/WasmBaselineCompile.cpp',
'asmjs/WasmBinary.cpp',
'asmjs/WasmBinaryFormat.cpp',
'asmjs/WasmBinaryIterator.cpp',
'asmjs/WasmBinaryToAST.cpp',
'asmjs/WasmBinaryToExperimentalText.cpp',