mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-08 00:38:39 +09:00
1316634 - Implement efficient WebAssembly.validate
1316634: Use ModuleEnvironment in the FunctionDecoder. 1316634: Move function validation to WasmBinaryFormat.h/cpp && rename it WasmValidate. 1316634: Implement efficient WebAssembly.validate 1316634: Use CheckedInt before checking against maximum limits
This commit is contained in:
parent
69496939ad
commit
ba8732b25b
13 changed files with 518 additions and 443 deletions
|
|
@ -349,7 +349,6 @@ main_deunified_sources = [
|
|||
'vm/Xdr.cpp',
|
||||
'wasm/AsmJS.cpp',
|
||||
'wasm/WasmBaselineCompile.cpp',
|
||||
'wasm/WasmBinaryFormat.cpp',
|
||||
'wasm/WasmBinaryIterator.cpp',
|
||||
'wasm/WasmBinaryToAST.cpp',
|
||||
'wasm/WasmBinaryToExperimentalText.cpp',
|
||||
|
|
@ -368,7 +367,8 @@ main_deunified_sources = [
|
|||
'wasm/WasmTable.cpp',
|
||||
'wasm/WasmTextToBinary.cpp',
|
||||
'wasm/WasmTextUtils.cpp',
|
||||
'wasm/WasmTypes.cpp'
|
||||
'wasm/WasmTypes.cpp',
|
||||
'wasm/WasmValidate.cpp'
|
||||
]
|
||||
|
||||
# jsarray.cpp and jsatom.cpp cannot be built in unified mode because
|
||||
|
|
|
|||
|
|
@ -38,12 +38,12 @@
|
|||
#include "vm/StringBuffer.h"
|
||||
#include "vm/Time.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
#include "wasm/WasmBinaryFormat.h"
|
||||
#include "wasm/WasmCompile.h"
|
||||
#include "wasm/WasmGenerator.h"
|
||||
#include "wasm/WasmInstance.h"
|
||||
#include "wasm/WasmJS.h"
|
||||
#include "wasm/WasmSerialize.h"
|
||||
#include "wasm/WasmValidate.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -117,10 +117,10 @@
|
|||
# include "jit/x86-shared/Assembler-x86-shared.h"
|
||||
#endif
|
||||
|
||||
#include "wasm/WasmBinaryFormat.h"
|
||||
#include "wasm/WasmBinaryIterator.h"
|
||||
#include "wasm/WasmGenerator.h"
|
||||
#include "wasm/WasmSignalHandlers.h"
|
||||
#include "wasm/WasmValidate.h"
|
||||
|
||||
#include "jit/MacroAssembler-inl.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
#include "jsprf.h"
|
||||
|
||||
#include "jit/AtomicOp.h"
|
||||
#include "wasm/WasmBinaryFormat.h"
|
||||
#include "wasm/WasmValidate.h"
|
||||
|
||||
namespace js {
|
||||
namespace wasm {
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@
|
|||
|
||||
#include "jscntxt.h"
|
||||
|
||||
#include "wasm/WasmBinaryFormat.h"
|
||||
#include "wasm/WasmBinaryIterator.h"
|
||||
#include "wasm/WasmValidate.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::wasm;
|
||||
|
|
|
|||
|
|
@ -17,408 +17,17 @@
|
|||
|
||||
#include "wasm/WasmCompile.h"
|
||||
|
||||
#include "mozilla/CheckedInt.h"
|
||||
|
||||
#include "jsprf.h"
|
||||
|
||||
#include "wasm/WasmBinaryFormat.h"
|
||||
#include "wasm/WasmBinaryIterator.h"
|
||||
#include "wasm/WasmGenerator.h"
|
||||
#include "wasm/WasmSignalHandlers.h"
|
||||
#include "wasm/WasmValidate.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
using namespace js::wasm;
|
||||
|
||||
using mozilla::CheckedInt;
|
||||
using mozilla::IsNaN;
|
||||
|
||||
namespace {
|
||||
|
||||
struct ValidatingPolicy : OpIterPolicy
|
||||
{
|
||||
// Validation is what we're all about here.
|
||||
static const bool Validate = true;
|
||||
};
|
||||
|
||||
typedef OpIter<ValidatingPolicy> ValidatingOpIter;
|
||||
|
||||
class FunctionDecoder
|
||||
{
|
||||
const ModuleGenerator& mg_;
|
||||
const ValTypeVector& locals_;
|
||||
ValidatingOpIter iter_;
|
||||
|
||||
public:
|
||||
FunctionDecoder(const ModuleGenerator& mg, const ValTypeVector& locals, Decoder& d)
|
||||
: mg_(mg), locals_(locals), iter_(d)
|
||||
{}
|
||||
const ModuleGenerator& mg() const { return mg_; }
|
||||
ValidatingOpIter& iter() { return iter_; }
|
||||
const ValTypeVector& locals() const { return locals_; }
|
||||
|
||||
bool checkHasMemory() {
|
||||
if (!mg().usesMemory())
|
||||
return iter().fail("can't touch memory without memory");
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
static bool
|
||||
DecodeCallArgs(FunctionDecoder& f, const Sig& sig)
|
||||
{
|
||||
const ValTypeVector& args = sig.args();
|
||||
uint32_t numArgs = args.length();
|
||||
for (size_t i = 0; i < numArgs; ++i) {
|
||||
ValType argType = args[i];
|
||||
if (!f.iter().readCallArg(argType, numArgs, i, nullptr))
|
||||
return false;
|
||||
}
|
||||
|
||||
return f.iter().readCallArgsEnd(numArgs);
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeCallReturn(FunctionDecoder& f, const Sig& sig)
|
||||
{
|
||||
return f.iter().readCallReturn(sig.ret());
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeCall(FunctionDecoder& f)
|
||||
{
|
||||
uint32_t funcIndex;
|
||||
if (!f.iter().readCall(&funcIndex))
|
||||
return false;
|
||||
|
||||
if (funcIndex >= f.mg().numFuncs())
|
||||
return f.iter().fail("callee index out of range");
|
||||
|
||||
if (!f.iter().inReachableCode())
|
||||
return true;
|
||||
|
||||
const Sig* sig = &f.mg().funcSig(funcIndex);
|
||||
|
||||
return DecodeCallArgs(f, *sig) &&
|
||||
DecodeCallReturn(f, *sig);
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeCallIndirect(FunctionDecoder& f)
|
||||
{
|
||||
if (!f.mg().numTables())
|
||||
return f.iter().fail("can't call_indirect without a table");
|
||||
|
||||
uint32_t sigIndex;
|
||||
if (!f.iter().readCallIndirect(&sigIndex, nullptr))
|
||||
return false;
|
||||
|
||||
if (sigIndex >= f.mg().numSigs())
|
||||
return f.iter().fail("signature index out of range");
|
||||
|
||||
if (!f.iter().inReachableCode())
|
||||
return true;
|
||||
|
||||
const Sig& sig = f.mg().sig(sigIndex);
|
||||
if (!DecodeCallArgs(f, sig))
|
||||
return false;
|
||||
|
||||
return DecodeCallReturn(f, sig);
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeBrTable(FunctionDecoder& f)
|
||||
{
|
||||
uint32_t tableLength;
|
||||
ExprType type = ExprType::Limit;
|
||||
if (!f.iter().readBrTable(&tableLength, &type, nullptr, nullptr))
|
||||
return false;
|
||||
|
||||
uint32_t depth;
|
||||
for (size_t i = 0, e = tableLength; i < e; ++i) {
|
||||
if (!f.iter().readBrTableEntry(&type, nullptr, &depth))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the default label.
|
||||
return f.iter().readBrTableDefault(&type, nullptr, &depth);
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeFunctionBodyExprs(FunctionDecoder& f)
|
||||
{
|
||||
#define CHECK(c) if (!(c)) return false; break
|
||||
|
||||
while (true) {
|
||||
uint16_t op;
|
||||
if (!f.iter().readOp(&op))
|
||||
return false;
|
||||
|
||||
switch (op) {
|
||||
case uint16_t(Op::End):
|
||||
if (!f.iter().readEnd(nullptr, nullptr, nullptr))
|
||||
return false;
|
||||
if (f.iter().controlStackEmpty())
|
||||
return true;
|
||||
break;
|
||||
case uint16_t(Op::Nop):
|
||||
CHECK(f.iter().readNop());
|
||||
case uint16_t(Op::Drop):
|
||||
CHECK(f.iter().readDrop());
|
||||
case uint16_t(Op::Call):
|
||||
CHECK(DecodeCall(f));
|
||||
case uint16_t(Op::CallIndirect):
|
||||
CHECK(DecodeCallIndirect(f));
|
||||
case uint16_t(Op::I32Const):
|
||||
CHECK(f.iter().readI32Const(nullptr));
|
||||
case uint16_t(Op::I64Const):
|
||||
CHECK(f.iter().readI64Const(nullptr));
|
||||
case uint16_t(Op::F32Const):
|
||||
CHECK(f.iter().readF32Const(nullptr));
|
||||
case uint16_t(Op::F64Const):
|
||||
CHECK(f.iter().readF64Const(nullptr));
|
||||
case uint16_t(Op::GetLocal):
|
||||
CHECK(f.iter().readGetLocal(f.locals(), nullptr));
|
||||
case uint16_t(Op::SetLocal):
|
||||
CHECK(f.iter().readSetLocal(f.locals(), nullptr, nullptr));
|
||||
case uint16_t(Op::TeeLocal):
|
||||
CHECK(f.iter().readTeeLocal(f.locals(), nullptr, nullptr));
|
||||
case uint16_t(Op::GetGlobal):
|
||||
CHECK(f.iter().readGetGlobal(f.mg().globals(), nullptr));
|
||||
case uint16_t(Op::SetGlobal):
|
||||
CHECK(f.iter().readSetGlobal(f.mg().globals(), nullptr, nullptr));
|
||||
case uint16_t(Op::Select):
|
||||
CHECK(f.iter().readSelect(nullptr, nullptr, nullptr, nullptr));
|
||||
case uint16_t(Op::Block):
|
||||
CHECK(f.iter().readBlock());
|
||||
case uint16_t(Op::Loop):
|
||||
CHECK(f.iter().readLoop());
|
||||
case uint16_t(Op::If):
|
||||
CHECK(f.iter().readIf(nullptr));
|
||||
case uint16_t(Op::Else):
|
||||
CHECK(f.iter().readElse(nullptr, nullptr));
|
||||
case uint16_t(Op::I32Clz):
|
||||
case uint16_t(Op::I32Ctz):
|
||||
case uint16_t(Op::I32Popcnt):
|
||||
CHECK(f.iter().readUnary(ValType::I32, nullptr));
|
||||
case uint16_t(Op::I64Clz):
|
||||
case uint16_t(Op::I64Ctz):
|
||||
case uint16_t(Op::I64Popcnt):
|
||||
CHECK(f.iter().readUnary(ValType::I64, nullptr));
|
||||
case uint16_t(Op::F32Abs):
|
||||
case uint16_t(Op::F32Neg):
|
||||
case uint16_t(Op::F32Ceil):
|
||||
case uint16_t(Op::F32Floor):
|
||||
case uint16_t(Op::F32Sqrt):
|
||||
case uint16_t(Op::F32Trunc):
|
||||
case uint16_t(Op::F32Nearest):
|
||||
CHECK(f.iter().readUnary(ValType::F32, nullptr));
|
||||
case uint16_t(Op::F64Abs):
|
||||
case uint16_t(Op::F64Neg):
|
||||
case uint16_t(Op::F64Ceil):
|
||||
case uint16_t(Op::F64Floor):
|
||||
case uint16_t(Op::F64Sqrt):
|
||||
case uint16_t(Op::F64Trunc):
|
||||
case uint16_t(Op::F64Nearest):
|
||||
CHECK(f.iter().readUnary(ValType::F64, nullptr));
|
||||
case uint16_t(Op::I32Add):
|
||||
case uint16_t(Op::I32Sub):
|
||||
case uint16_t(Op::I32Mul):
|
||||
case uint16_t(Op::I32DivS):
|
||||
case uint16_t(Op::I32DivU):
|
||||
case uint16_t(Op::I32RemS):
|
||||
case uint16_t(Op::I32RemU):
|
||||
case uint16_t(Op::I32And):
|
||||
case uint16_t(Op::I32Or):
|
||||
case uint16_t(Op::I32Xor):
|
||||
case uint16_t(Op::I32Shl):
|
||||
case uint16_t(Op::I32ShrS):
|
||||
case uint16_t(Op::I32ShrU):
|
||||
case uint16_t(Op::I32Rotl):
|
||||
case uint16_t(Op::I32Rotr):
|
||||
CHECK(f.iter().readBinary(ValType::I32, nullptr, nullptr));
|
||||
case uint16_t(Op::I64Add):
|
||||
case uint16_t(Op::I64Sub):
|
||||
case uint16_t(Op::I64Mul):
|
||||
case uint16_t(Op::I64DivS):
|
||||
case uint16_t(Op::I64DivU):
|
||||
case uint16_t(Op::I64RemS):
|
||||
case uint16_t(Op::I64RemU):
|
||||
case uint16_t(Op::I64And):
|
||||
case uint16_t(Op::I64Or):
|
||||
case uint16_t(Op::I64Xor):
|
||||
case uint16_t(Op::I64Shl):
|
||||
case uint16_t(Op::I64ShrS):
|
||||
case uint16_t(Op::I64ShrU):
|
||||
case uint16_t(Op::I64Rotl):
|
||||
case uint16_t(Op::I64Rotr):
|
||||
CHECK(f.iter().readBinary(ValType::I64, nullptr, nullptr));
|
||||
case uint16_t(Op::F32Add):
|
||||
case uint16_t(Op::F32Sub):
|
||||
case uint16_t(Op::F32Mul):
|
||||
case uint16_t(Op::F32Div):
|
||||
case uint16_t(Op::F32Min):
|
||||
case uint16_t(Op::F32Max):
|
||||
case uint16_t(Op::F32CopySign):
|
||||
CHECK(f.iter().readBinary(ValType::F32, nullptr, nullptr));
|
||||
case uint16_t(Op::F64Add):
|
||||
case uint16_t(Op::F64Sub):
|
||||
case uint16_t(Op::F64Mul):
|
||||
case uint16_t(Op::F64Div):
|
||||
case uint16_t(Op::F64Min):
|
||||
case uint16_t(Op::F64Max):
|
||||
case uint16_t(Op::F64CopySign):
|
||||
CHECK(f.iter().readBinary(ValType::F64, nullptr, nullptr));
|
||||
case uint16_t(Op::I32Eq):
|
||||
case uint16_t(Op::I32Ne):
|
||||
case uint16_t(Op::I32LtS):
|
||||
case uint16_t(Op::I32LtU):
|
||||
case uint16_t(Op::I32LeS):
|
||||
case uint16_t(Op::I32LeU):
|
||||
case uint16_t(Op::I32GtS):
|
||||
case uint16_t(Op::I32GtU):
|
||||
case uint16_t(Op::I32GeS):
|
||||
case uint16_t(Op::I32GeU):
|
||||
CHECK(f.iter().readComparison(ValType::I32, nullptr, nullptr));
|
||||
case uint16_t(Op::I64Eq):
|
||||
case uint16_t(Op::I64Ne):
|
||||
case uint16_t(Op::I64LtS):
|
||||
case uint16_t(Op::I64LtU):
|
||||
case uint16_t(Op::I64LeS):
|
||||
case uint16_t(Op::I64LeU):
|
||||
case uint16_t(Op::I64GtS):
|
||||
case uint16_t(Op::I64GtU):
|
||||
case uint16_t(Op::I64GeS):
|
||||
case uint16_t(Op::I64GeU):
|
||||
CHECK(f.iter().readComparison(ValType::I64, nullptr, nullptr));
|
||||
case uint16_t(Op::F32Eq):
|
||||
case uint16_t(Op::F32Ne):
|
||||
case uint16_t(Op::F32Lt):
|
||||
case uint16_t(Op::F32Le):
|
||||
case uint16_t(Op::F32Gt):
|
||||
case uint16_t(Op::F32Ge):
|
||||
CHECK(f.iter().readComparison(ValType::F32, nullptr, nullptr));
|
||||
case uint16_t(Op::F64Eq):
|
||||
case uint16_t(Op::F64Ne):
|
||||
case uint16_t(Op::F64Lt):
|
||||
case uint16_t(Op::F64Le):
|
||||
case uint16_t(Op::F64Gt):
|
||||
case uint16_t(Op::F64Ge):
|
||||
CHECK(f.iter().readComparison(ValType::F64, nullptr, nullptr));
|
||||
case uint16_t(Op::I32Eqz):
|
||||
CHECK(f.iter().readConversion(ValType::I32, ValType::I32, nullptr));
|
||||
case uint16_t(Op::I64Eqz):
|
||||
case uint16_t(Op::I32WrapI64):
|
||||
CHECK(f.iter().readConversion(ValType::I64, ValType::I32, nullptr));
|
||||
case uint16_t(Op::I32TruncSF32):
|
||||
case uint16_t(Op::I32TruncUF32):
|
||||
case uint16_t(Op::I32ReinterpretF32):
|
||||
CHECK(f.iter().readConversion(ValType::F32, ValType::I32, nullptr));
|
||||
case uint16_t(Op::I32TruncSF64):
|
||||
case uint16_t(Op::I32TruncUF64):
|
||||
CHECK(f.iter().readConversion(ValType::F64, ValType::I32, nullptr));
|
||||
case uint16_t(Op::I64ExtendSI32):
|
||||
case uint16_t(Op::I64ExtendUI32):
|
||||
CHECK(f.iter().readConversion(ValType::I32, ValType::I64, nullptr));
|
||||
case uint16_t(Op::I64TruncSF32):
|
||||
case uint16_t(Op::I64TruncUF32):
|
||||
CHECK(f.iter().readConversion(ValType::F32, ValType::I64, nullptr));
|
||||
case uint16_t(Op::I64TruncSF64):
|
||||
case uint16_t(Op::I64TruncUF64):
|
||||
case uint16_t(Op::I64ReinterpretF64):
|
||||
CHECK(f.iter().readConversion(ValType::F64, ValType::I64, nullptr));
|
||||
case uint16_t(Op::F32ConvertSI32):
|
||||
case uint16_t(Op::F32ConvertUI32):
|
||||
case uint16_t(Op::F32ReinterpretI32):
|
||||
CHECK(f.iter().readConversion(ValType::I32, ValType::F32, nullptr));
|
||||
case uint16_t(Op::F32ConvertSI64):
|
||||
case uint16_t(Op::F32ConvertUI64):
|
||||
CHECK(f.iter().readConversion(ValType::I64, ValType::F32, nullptr));
|
||||
case uint16_t(Op::F32DemoteF64):
|
||||
CHECK(f.iter().readConversion(ValType::F64, ValType::F32, nullptr));
|
||||
case uint16_t(Op::F64ConvertSI32):
|
||||
case uint16_t(Op::F64ConvertUI32):
|
||||
CHECK(f.iter().readConversion(ValType::I32, ValType::F64, nullptr));
|
||||
case uint16_t(Op::F64ConvertSI64):
|
||||
case uint16_t(Op::F64ConvertUI64):
|
||||
case uint16_t(Op::F64ReinterpretI64):
|
||||
CHECK(f.iter().readConversion(ValType::I64, ValType::F64, nullptr));
|
||||
case uint16_t(Op::F64PromoteF32):
|
||||
CHECK(f.iter().readConversion(ValType::F32, ValType::F64, nullptr));
|
||||
case uint16_t(Op::I32Extend8S):
|
||||
case uint16_t(Op::I32Extend16S):
|
||||
CHECK(f.iter().readConversion(ValType::I32, ValType::I32, nullptr));
|
||||
case uint16_t(Op::I64Extend8S):
|
||||
case uint16_t(Op::I64Extend16S):
|
||||
case uint16_t(Op::I64Extend32S):
|
||||
CHECK(f.iter().readConversion(ValType::I64, ValType::I64, nullptr));
|
||||
case uint16_t(Op::I32Load8S):
|
||||
case uint16_t(Op::I32Load8U):
|
||||
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I32, 1, nullptr));
|
||||
case uint16_t(Op::I32Load16S):
|
||||
case uint16_t(Op::I32Load16U):
|
||||
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I32, 2, nullptr));
|
||||
case uint16_t(Op::I32Load):
|
||||
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I32, 4, nullptr));
|
||||
case uint16_t(Op::I64Load8S):
|
||||
case uint16_t(Op::I64Load8U):
|
||||
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 1, nullptr));
|
||||
case uint16_t(Op::I64Load16S):
|
||||
case uint16_t(Op::I64Load16U):
|
||||
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 2, nullptr));
|
||||
case uint16_t(Op::I64Load32S):
|
||||
case uint16_t(Op::I64Load32U):
|
||||
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 4, nullptr));
|
||||
case uint16_t(Op::I64Load):
|
||||
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 8, nullptr));
|
||||
case uint16_t(Op::F32Load):
|
||||
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::F32, 4, nullptr));
|
||||
case uint16_t(Op::F64Load):
|
||||
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::F64, 8, nullptr));
|
||||
case uint16_t(Op::I32Store8):
|
||||
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I32, 1, nullptr, nullptr));
|
||||
case uint16_t(Op::I32Store16):
|
||||
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I32, 2, nullptr, nullptr));
|
||||
case uint16_t(Op::I32Store):
|
||||
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I32, 4, nullptr, nullptr));
|
||||
case uint16_t(Op::I64Store8):
|
||||
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 1, nullptr, nullptr));
|
||||
case uint16_t(Op::I64Store16):
|
||||
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 2, nullptr, nullptr));
|
||||
case uint16_t(Op::I64Store32):
|
||||
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 4, nullptr, nullptr));
|
||||
case uint16_t(Op::I64Store):
|
||||
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 8, nullptr, nullptr));
|
||||
case uint16_t(Op::F32Store):
|
||||
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::F32, 4, nullptr, nullptr));
|
||||
case uint16_t(Op::F64Store):
|
||||
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::F64, 8, nullptr, nullptr));
|
||||
case uint16_t(Op::GrowMemory):
|
||||
CHECK(f.checkHasMemory() && f.iter().readGrowMemory(nullptr));
|
||||
case uint16_t(Op::CurrentMemory):
|
||||
CHECK(f.checkHasMemory() && f.iter().readCurrentMemory());
|
||||
case uint16_t(Op::Br):
|
||||
CHECK(f.iter().readBr(nullptr, nullptr, nullptr));
|
||||
case uint16_t(Op::BrIf):
|
||||
CHECK(f.iter().readBrIf(nullptr, nullptr, nullptr, nullptr));
|
||||
case uint16_t(Op::BrTable):
|
||||
CHECK(DecodeBrTable(f));
|
||||
case uint16_t(Op::Return):
|
||||
CHECK(f.iter().readReturn(nullptr));
|
||||
case uint16_t(Op::Unreachable):
|
||||
CHECK(f.iter().readUnreachable());
|
||||
default:
|
||||
return f.iter().unrecognizedOpcode(op);
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_CRASH("unreachable");
|
||||
|
||||
#undef CHECK
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeFunctionBody(Decoder& d, ModuleGenerator& mg, uint32_t funcIndex)
|
||||
{
|
||||
|
|
@ -436,23 +45,7 @@ DecodeFunctionBody(Decoder& d, ModuleGenerator& mg, uint32_t funcIndex)
|
|||
if (!mg.startFuncDef(offsetInModule, &fg))
|
||||
return false;
|
||||
|
||||
ValTypeVector locals;
|
||||
const Sig& sig = mg.funcSig(funcIndex);
|
||||
if (!locals.appendAll(sig.args()))
|
||||
return false;
|
||||
|
||||
if (!DecodeLocalEntries(d, ModuleKind::Wasm, &locals))
|
||||
return false;
|
||||
|
||||
FunctionDecoder f(mg, locals, d);
|
||||
|
||||
if (!f.iter().readFunctionStart(sig.ret()))
|
||||
return false;
|
||||
|
||||
if (!DecodeFunctionBodyExprs(f))
|
||||
return false;
|
||||
|
||||
if (!f.iter().readFunctionEnd())
|
||||
if (!ValidateFunctionBody(mg.env(), funcIndex, d))
|
||||
return false;
|
||||
|
||||
if (d.currentPosition() != bodyBegin + bodySize)
|
||||
|
|
|
|||
|
|
@ -801,18 +801,6 @@ ModuleGenerator::numFuncDefs() const
|
|||
return env_->funcSigs.length() - numFuncImports();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ModuleGenerator::numFuncs() const
|
||||
{
|
||||
// asm.js pre-reserves a bunch of function index space which is
|
||||
// incrementally filled in during function-body validation. Thus, there are
|
||||
// a few possible interpretations of numFuncs() (total index space size vs.
|
||||
// exact number of imports/definitions encountered so far) and to simplify
|
||||
// things we simply only define this quantity for wasm.
|
||||
MOZ_ASSERT(!isAsmJS());
|
||||
return env_->funcSigs.length();
|
||||
}
|
||||
|
||||
const SigWithId&
|
||||
ModuleGenerator::funcSig(uint32_t funcIndex) const
|
||||
{
|
||||
|
|
@ -978,7 +966,7 @@ ModuleGenerator::finishFuncDefs()
|
|||
MOZ_ASSERT(funcCodeRange(i).funcIndex() == i);
|
||||
} else {
|
||||
MOZ_ASSERT(numFinishedFuncDefs_ == numFuncDefs());
|
||||
for (uint32_t i = 0; i < numFuncs(); i++)
|
||||
for (uint32_t i = 0; i < env_->numFuncs(); i++)
|
||||
MOZ_ASSERT(funcCodeRange(i).funcIndex() == i);
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@
|
|||
#define wasm_generator_h
|
||||
|
||||
#include "jit/MacroAssembler.h"
|
||||
#include "wasm/WasmBinaryFormat.h"
|
||||
#include "wasm/WasmModule.h"
|
||||
#include "wasm/WasmValidate.h"
|
||||
|
||||
namespace js {
|
||||
namespace wasm {
|
||||
|
|
@ -289,7 +289,6 @@ private:
|
|||
// Functions declarations:
|
||||
uint32_t numFuncImports() const;
|
||||
uint32_t numFuncDefs() const;
|
||||
uint32_t numFuncs() const;
|
||||
|
||||
// Function definitions:
|
||||
MOZ_MUST_USE bool startFuncDefs();
|
||||
|
|
|
|||
|
|
@ -23,10 +23,10 @@
|
|||
#include "jit/CodeGenerator.h"
|
||||
|
||||
#include "wasm/WasmBaselineCompile.h"
|
||||
#include "wasm/WasmBinaryFormat.h"
|
||||
#include "wasm/WasmBinaryIterator.h"
|
||||
#include "wasm/WasmGenerator.h"
|
||||
#include "wasm/WasmSignalHandlers.h"
|
||||
#include "wasm/WasmValidate.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include "wasm/WasmInstance.h"
|
||||
#include "wasm/WasmModule.h"
|
||||
#include "wasm/WasmSignalHandlers.h"
|
||||
#include "wasm/WasmValidate.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
|
|
@ -1927,12 +1928,8 @@ WebAssembly_validate(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (!GetBufferSource(cx, callArgs, "WebAssembly.validate", &bytecode))
|
||||
return false;
|
||||
|
||||
CompileArgs compileArgs;
|
||||
if (!InitCompileArgs(cx, &compileArgs))
|
||||
return false;
|
||||
|
||||
UniqueChars error;
|
||||
bool validated = !!Compile(*bytecode, compileArgs, &error);
|
||||
bool validated = Validate(*bytecode, &error);
|
||||
|
||||
// If the reason for validation failure was OOM (signalled by null error
|
||||
// message), report out-of-memory so that validate's return is always
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@
|
|||
#include "js/CharacterEncoding.h"
|
||||
#include "js/HashTable.h"
|
||||
#include "wasm/WasmAST.h"
|
||||
#include "wasm/WasmBinaryFormat.h"
|
||||
#include "wasm/WasmTypes.h"
|
||||
#include "wasm/WasmValidate.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::wasm;
|
||||
|
|
|
|||
|
|
@ -15,15 +15,17 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "wasm/WasmBinaryFormat.h"
|
||||
#include "wasm/WasmValidate.h"
|
||||
|
||||
#include "mozilla/CheckedInt.h"
|
||||
|
||||
#include "jsprf.h"
|
||||
|
||||
#include "jit/JitOptions.h"
|
||||
#include "wasm/WasmBinaryIterator.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
using namespace js::wasm;
|
||||
|
||||
using mozilla::CheckedInt;
|
||||
|
|
@ -140,6 +142,405 @@ wasm::DecodeLocalEntries(Decoder& d, ModuleKind kind, ValTypeVector* locals)
|
|||
return true;
|
||||
}
|
||||
|
||||
// Function body validation.
|
||||
|
||||
struct ValidatingPolicy : OpIterPolicy
|
||||
{
|
||||
// Validation is what we're all about here.
|
||||
static const bool Validate = true;
|
||||
};
|
||||
|
||||
typedef OpIter<ValidatingPolicy> ValidatingOpIter;
|
||||
|
||||
class FunctionDecoder
|
||||
{
|
||||
const ModuleEnvironment& env_;
|
||||
const ValTypeVector& locals_;
|
||||
ValidatingOpIter iter_;
|
||||
|
||||
public:
|
||||
FunctionDecoder(const ModuleEnvironment& env, const ValTypeVector& locals, Decoder& d)
|
||||
: env_(env), locals_(locals), iter_(d)
|
||||
{}
|
||||
|
||||
const ModuleEnvironment& env() const { return env_; }
|
||||
ValidatingOpIter& iter() { return iter_; }
|
||||
const ValTypeVector& locals() const { return locals_; }
|
||||
|
||||
bool checkHasMemory() {
|
||||
if (!env().usesMemory())
|
||||
return iter().fail("can't touch memory without memory");
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static bool
|
||||
DecodeCallArgs(FunctionDecoder& f, const Sig& sig)
|
||||
{
|
||||
const ValTypeVector& args = sig.args();
|
||||
uint32_t numArgs = args.length();
|
||||
for (size_t i = 0; i < numArgs; ++i) {
|
||||
ValType argType = args[i];
|
||||
if (!f.iter().readCallArg(argType, numArgs, i, nullptr))
|
||||
return false;
|
||||
}
|
||||
|
||||
return f.iter().readCallArgsEnd(numArgs);
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeCallReturn(FunctionDecoder& f, const Sig& sig)
|
||||
{
|
||||
return f.iter().readCallReturn(sig.ret());
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeCall(FunctionDecoder& f)
|
||||
{
|
||||
uint32_t funcIndex;
|
||||
if (!f.iter().readCall(&funcIndex))
|
||||
return false;
|
||||
|
||||
if (funcIndex >= f.env().numFuncs())
|
||||
return f.iter().fail("callee index out of range");
|
||||
|
||||
if (!f.iter().inReachableCode())
|
||||
return true;
|
||||
|
||||
const Sig* sig = f.env().funcSigs[funcIndex];
|
||||
|
||||
return DecodeCallArgs(f, *sig) &&
|
||||
DecodeCallReturn(f, *sig);
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeCallIndirect(FunctionDecoder& f)
|
||||
{
|
||||
if (!f.env().numTables())
|
||||
return f.iter().fail("can't call_indirect without a table");
|
||||
|
||||
uint32_t sigIndex;
|
||||
if (!f.iter().readCallIndirect(&sigIndex, nullptr))
|
||||
return false;
|
||||
|
||||
if (sigIndex >= f.env().numSigs())
|
||||
return f.iter().fail("signature index out of range");
|
||||
|
||||
if (!f.iter().inReachableCode())
|
||||
return true;
|
||||
|
||||
const Sig& sig = f.env().sigs[sigIndex];
|
||||
if (!DecodeCallArgs(f, sig))
|
||||
return false;
|
||||
|
||||
return DecodeCallReturn(f, sig);
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeBrTable(FunctionDecoder& f)
|
||||
{
|
||||
uint32_t tableLength;
|
||||
ExprType type = ExprType::Limit;
|
||||
if (!f.iter().readBrTable(&tableLength, &type, nullptr, nullptr))
|
||||
return false;
|
||||
|
||||
uint32_t depth;
|
||||
for (size_t i = 0, e = tableLength; i < e; ++i) {
|
||||
if (!f.iter().readBrTableEntry(&type, nullptr, &depth))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the default label.
|
||||
return f.iter().readBrTableDefault(&type, nullptr, &depth);
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeFunctionBodyExprs(FunctionDecoder& f)
|
||||
{
|
||||
#define CHECK(c) if (!(c)) return false; break
|
||||
|
||||
while (true) {
|
||||
uint16_t op;
|
||||
if (!f.iter().readOp(&op))
|
||||
return false;
|
||||
|
||||
switch (op) {
|
||||
case uint16_t(Op::End):
|
||||
if (!f.iter().readEnd(nullptr, nullptr, nullptr))
|
||||
return false;
|
||||
if (f.iter().controlStackEmpty())
|
||||
return true;
|
||||
break;
|
||||
case uint16_t(Op::Nop):
|
||||
CHECK(f.iter().readNop());
|
||||
case uint16_t(Op::Drop):
|
||||
CHECK(f.iter().readDrop());
|
||||
case uint16_t(Op::Call):
|
||||
CHECK(DecodeCall(f));
|
||||
case uint16_t(Op::CallIndirect):
|
||||
CHECK(DecodeCallIndirect(f));
|
||||
case uint16_t(Op::I32Const):
|
||||
CHECK(f.iter().readI32Const(nullptr));
|
||||
case uint16_t(Op::I64Const):
|
||||
CHECK(f.iter().readI64Const(nullptr));
|
||||
case uint16_t(Op::F32Const):
|
||||
CHECK(f.iter().readF32Const(nullptr));
|
||||
case uint16_t(Op::F64Const):
|
||||
CHECK(f.iter().readF64Const(nullptr));
|
||||
case uint16_t(Op::GetLocal):
|
||||
CHECK(f.iter().readGetLocal(f.locals(), nullptr));
|
||||
case uint16_t(Op::SetLocal):
|
||||
CHECK(f.iter().readSetLocal(f.locals(), nullptr, nullptr));
|
||||
case uint16_t(Op::TeeLocal):
|
||||
CHECK(f.iter().readTeeLocal(f.locals(), nullptr, nullptr));
|
||||
case uint16_t(Op::GetGlobal):
|
||||
CHECK(f.iter().readGetGlobal(f.env().globals, nullptr));
|
||||
case uint16_t(Op::SetGlobal):
|
||||
CHECK(f.iter().readSetGlobal(f.env().globals, nullptr, nullptr));
|
||||
case uint16_t(Op::Select):
|
||||
CHECK(f.iter().readSelect(nullptr, nullptr, nullptr, nullptr));
|
||||
case uint16_t(Op::Block):
|
||||
CHECK(f.iter().readBlock());
|
||||
case uint16_t(Op::Loop):
|
||||
CHECK(f.iter().readLoop());
|
||||
case uint16_t(Op::If):
|
||||
CHECK(f.iter().readIf(nullptr));
|
||||
case uint16_t(Op::Else):
|
||||
CHECK(f.iter().readElse(nullptr, nullptr));
|
||||
case uint16_t(Op::I32Clz):
|
||||
case uint16_t(Op::I32Ctz):
|
||||
case uint16_t(Op::I32Popcnt):
|
||||
CHECK(f.iter().readUnary(ValType::I32, nullptr));
|
||||
case uint16_t(Op::I64Clz):
|
||||
case uint16_t(Op::I64Ctz):
|
||||
case uint16_t(Op::I64Popcnt):
|
||||
CHECK(f.iter().readUnary(ValType::I64, nullptr));
|
||||
case uint16_t(Op::F32Abs):
|
||||
case uint16_t(Op::F32Neg):
|
||||
case uint16_t(Op::F32Ceil):
|
||||
case uint16_t(Op::F32Floor):
|
||||
case uint16_t(Op::F32Sqrt):
|
||||
case uint16_t(Op::F32Trunc):
|
||||
case uint16_t(Op::F32Nearest):
|
||||
CHECK(f.iter().readUnary(ValType::F32, nullptr));
|
||||
case uint16_t(Op::F64Abs):
|
||||
case uint16_t(Op::F64Neg):
|
||||
case uint16_t(Op::F64Ceil):
|
||||
case uint16_t(Op::F64Floor):
|
||||
case uint16_t(Op::F64Sqrt):
|
||||
case uint16_t(Op::F64Trunc):
|
||||
case uint16_t(Op::F64Nearest):
|
||||
CHECK(f.iter().readUnary(ValType::F64, nullptr));
|
||||
case uint16_t(Op::I32Add):
|
||||
case uint16_t(Op::I32Sub):
|
||||
case uint16_t(Op::I32Mul):
|
||||
case uint16_t(Op::I32DivS):
|
||||
case uint16_t(Op::I32DivU):
|
||||
case uint16_t(Op::I32RemS):
|
||||
case uint16_t(Op::I32RemU):
|
||||
case uint16_t(Op::I32And):
|
||||
case uint16_t(Op::I32Or):
|
||||
case uint16_t(Op::I32Xor):
|
||||
case uint16_t(Op::I32Shl):
|
||||
case uint16_t(Op::I32ShrS):
|
||||
case uint16_t(Op::I32ShrU):
|
||||
case uint16_t(Op::I32Rotl):
|
||||
case uint16_t(Op::I32Rotr):
|
||||
CHECK(f.iter().readBinary(ValType::I32, nullptr, nullptr));
|
||||
case uint16_t(Op::I64Add):
|
||||
case uint16_t(Op::I64Sub):
|
||||
case uint16_t(Op::I64Mul):
|
||||
case uint16_t(Op::I64DivS):
|
||||
case uint16_t(Op::I64DivU):
|
||||
case uint16_t(Op::I64RemS):
|
||||
case uint16_t(Op::I64RemU):
|
||||
case uint16_t(Op::I64And):
|
||||
case uint16_t(Op::I64Or):
|
||||
case uint16_t(Op::I64Xor):
|
||||
case uint16_t(Op::I64Shl):
|
||||
case uint16_t(Op::I64ShrS):
|
||||
case uint16_t(Op::I64ShrU):
|
||||
case uint16_t(Op::I64Rotl):
|
||||
case uint16_t(Op::I64Rotr):
|
||||
CHECK(f.iter().readBinary(ValType::I64, nullptr, nullptr));
|
||||
case uint16_t(Op::F32Add):
|
||||
case uint16_t(Op::F32Sub):
|
||||
case uint16_t(Op::F32Mul):
|
||||
case uint16_t(Op::F32Div):
|
||||
case uint16_t(Op::F32Min):
|
||||
case uint16_t(Op::F32Max):
|
||||
case uint16_t(Op::F32CopySign):
|
||||
CHECK(f.iter().readBinary(ValType::F32, nullptr, nullptr));
|
||||
case uint16_t(Op::F64Add):
|
||||
case uint16_t(Op::F64Sub):
|
||||
case uint16_t(Op::F64Mul):
|
||||
case uint16_t(Op::F64Div):
|
||||
case uint16_t(Op::F64Min):
|
||||
case uint16_t(Op::F64Max):
|
||||
case uint16_t(Op::F64CopySign):
|
||||
CHECK(f.iter().readBinary(ValType::F64, nullptr, nullptr));
|
||||
case uint16_t(Op::I32Eq):
|
||||
case uint16_t(Op::I32Ne):
|
||||
case uint16_t(Op::I32LtS):
|
||||
case uint16_t(Op::I32LtU):
|
||||
case uint16_t(Op::I32LeS):
|
||||
case uint16_t(Op::I32LeU):
|
||||
case uint16_t(Op::I32GtS):
|
||||
case uint16_t(Op::I32GtU):
|
||||
case uint16_t(Op::I32GeS):
|
||||
case uint16_t(Op::I32GeU):
|
||||
CHECK(f.iter().readComparison(ValType::I32, nullptr, nullptr));
|
||||
case uint16_t(Op::I64Eq):
|
||||
case uint16_t(Op::I64Ne):
|
||||
case uint16_t(Op::I64LtS):
|
||||
case uint16_t(Op::I64LtU):
|
||||
case uint16_t(Op::I64LeS):
|
||||
case uint16_t(Op::I64LeU):
|
||||
case uint16_t(Op::I64GtS):
|
||||
case uint16_t(Op::I64GtU):
|
||||
case uint16_t(Op::I64GeS):
|
||||
case uint16_t(Op::I64GeU):
|
||||
CHECK(f.iter().readComparison(ValType::I64, nullptr, nullptr));
|
||||
case uint16_t(Op::F32Eq):
|
||||
case uint16_t(Op::F32Ne):
|
||||
case uint16_t(Op::F32Lt):
|
||||
case uint16_t(Op::F32Le):
|
||||
case uint16_t(Op::F32Gt):
|
||||
case uint16_t(Op::F32Ge):
|
||||
CHECK(f.iter().readComparison(ValType::F32, nullptr, nullptr));
|
||||
case uint16_t(Op::F64Eq):
|
||||
case uint16_t(Op::F64Ne):
|
||||
case uint16_t(Op::F64Lt):
|
||||
case uint16_t(Op::F64Le):
|
||||
case uint16_t(Op::F64Gt):
|
||||
case uint16_t(Op::F64Ge):
|
||||
CHECK(f.iter().readComparison(ValType::F64, nullptr, nullptr));
|
||||
case uint16_t(Op::I32Eqz):
|
||||
CHECK(f.iter().readConversion(ValType::I32, ValType::I32, nullptr));
|
||||
case uint16_t(Op::I64Eqz):
|
||||
case uint16_t(Op::I32WrapI64):
|
||||
CHECK(f.iter().readConversion(ValType::I64, ValType::I32, nullptr));
|
||||
case uint16_t(Op::I32TruncSF32):
|
||||
case uint16_t(Op::I32TruncUF32):
|
||||
case uint16_t(Op::I32ReinterpretF32):
|
||||
CHECK(f.iter().readConversion(ValType::F32, ValType::I32, nullptr));
|
||||
case uint16_t(Op::I32TruncSF64):
|
||||
case uint16_t(Op::I32TruncUF64):
|
||||
CHECK(f.iter().readConversion(ValType::F64, ValType::I32, nullptr));
|
||||
case uint16_t(Op::I64ExtendSI32):
|
||||
case uint16_t(Op::I64ExtendUI32):
|
||||
CHECK(f.iter().readConversion(ValType::I32, ValType::I64, nullptr));
|
||||
case uint16_t(Op::I64TruncSF32):
|
||||
case uint16_t(Op::I64TruncUF32):
|
||||
CHECK(f.iter().readConversion(ValType::F32, ValType::I64, nullptr));
|
||||
case uint16_t(Op::I64TruncSF64):
|
||||
case uint16_t(Op::I64TruncUF64):
|
||||
case uint16_t(Op::I64ReinterpretF64):
|
||||
CHECK(f.iter().readConversion(ValType::F64, ValType::I64, nullptr));
|
||||
case uint16_t(Op::F32ConvertSI32):
|
||||
case uint16_t(Op::F32ConvertUI32):
|
||||
case uint16_t(Op::F32ReinterpretI32):
|
||||
CHECK(f.iter().readConversion(ValType::I32, ValType::F32, nullptr));
|
||||
case uint16_t(Op::F32ConvertSI64):
|
||||
case uint16_t(Op::F32ConvertUI64):
|
||||
CHECK(f.iter().readConversion(ValType::I64, ValType::F32, nullptr));
|
||||
case uint16_t(Op::F32DemoteF64):
|
||||
CHECK(f.iter().readConversion(ValType::F64, ValType::F32, nullptr));
|
||||
case uint16_t(Op::F64ConvertSI32):
|
||||
case uint16_t(Op::F64ConvertUI32):
|
||||
CHECK(f.iter().readConversion(ValType::I32, ValType::F64, nullptr));
|
||||
case uint16_t(Op::F64ConvertSI64):
|
||||
case uint16_t(Op::F64ConvertUI64):
|
||||
case uint16_t(Op::F64ReinterpretI64):
|
||||
CHECK(f.iter().readConversion(ValType::I64, ValType::F64, nullptr));
|
||||
case uint16_t(Op::F64PromoteF32):
|
||||
CHECK(f.iter().readConversion(ValType::F32, ValType::F64, nullptr));
|
||||
case uint16_t(Op::I32Load8S):
|
||||
case uint16_t(Op::I32Load8U):
|
||||
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I32, 1, nullptr));
|
||||
case uint16_t(Op::I32Load16S):
|
||||
case uint16_t(Op::I32Load16U):
|
||||
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I32, 2, nullptr));
|
||||
case uint16_t(Op::I32Load):
|
||||
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I32, 4, nullptr));
|
||||
case uint16_t(Op::I64Load8S):
|
||||
case uint16_t(Op::I64Load8U):
|
||||
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 1, nullptr));
|
||||
case uint16_t(Op::I64Load16S):
|
||||
case uint16_t(Op::I64Load16U):
|
||||
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 2, nullptr));
|
||||
case uint16_t(Op::I64Load32S):
|
||||
case uint16_t(Op::I64Load32U):
|
||||
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 4, nullptr));
|
||||
case uint16_t(Op::I64Load):
|
||||
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 8, nullptr));
|
||||
case uint16_t(Op::F32Load):
|
||||
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::F32, 4, nullptr));
|
||||
case uint16_t(Op::F64Load):
|
||||
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::F64, 8, nullptr));
|
||||
case uint16_t(Op::I32Store8):
|
||||
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I32, 1, nullptr, nullptr));
|
||||
case uint16_t(Op::I32Store16):
|
||||
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I32, 2, nullptr, nullptr));
|
||||
case uint16_t(Op::I32Store):
|
||||
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I32, 4, nullptr, nullptr));
|
||||
case uint16_t(Op::I64Store8):
|
||||
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 1, nullptr, nullptr));
|
||||
case uint16_t(Op::I64Store16):
|
||||
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 2, nullptr, nullptr));
|
||||
case uint16_t(Op::I64Store32):
|
||||
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 4, nullptr, nullptr));
|
||||
case uint16_t(Op::I64Store):
|
||||
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 8, nullptr, nullptr));
|
||||
case uint16_t(Op::F32Store):
|
||||
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::F32, 4, nullptr, nullptr));
|
||||
case uint16_t(Op::F64Store):
|
||||
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::F64, 8, nullptr, nullptr));
|
||||
case uint16_t(Op::GrowMemory):
|
||||
CHECK(f.checkHasMemory() && f.iter().readGrowMemory(nullptr));
|
||||
case uint16_t(Op::CurrentMemory):
|
||||
CHECK(f.checkHasMemory() && f.iter().readCurrentMemory());
|
||||
case uint16_t(Op::Br):
|
||||
CHECK(f.iter().readBr(nullptr, nullptr, nullptr));
|
||||
case uint16_t(Op::BrIf):
|
||||
CHECK(f.iter().readBrIf(nullptr, nullptr, nullptr, nullptr));
|
||||
case uint16_t(Op::BrTable):
|
||||
CHECK(DecodeBrTable(f));
|
||||
case uint16_t(Op::Return):
|
||||
CHECK(f.iter().readReturn(nullptr));
|
||||
case uint16_t(Op::Unreachable):
|
||||
CHECK(f.iter().readUnreachable());
|
||||
default:
|
||||
return f.iter().unrecognizedOpcode(op);
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_CRASH("unreachable");
|
||||
|
||||
#undef CHECK
|
||||
}
|
||||
|
||||
bool
|
||||
wasm::ValidateFunctionBody(const ModuleEnvironment& env, uint32_t funcIndex, Decoder& d)
|
||||
{
|
||||
ValTypeVector locals;
|
||||
const Sig& sig = *env.funcSigs[funcIndex];
|
||||
if (!locals.appendAll(sig.args()))
|
||||
return false;
|
||||
|
||||
if (!DecodeLocalEntries(d, ModuleKind::Wasm, &locals))
|
||||
return false;
|
||||
|
||||
FunctionDecoder f(env, locals, d);
|
||||
|
||||
if (!f.iter().readFunctionStart(sig.ret()))
|
||||
return false;
|
||||
|
||||
if (!DecodeFunctionBodyExprs(f))
|
||||
return false;
|
||||
|
||||
return f.iter().readFunctionEnd();
|
||||
}
|
||||
// Section macros.
|
||||
|
||||
static bool
|
||||
|
|
@ -625,11 +1026,12 @@ DecodeGlobalSection(Decoder& d, GlobalDescVector* globals)
|
|||
if (!d.readVarU32(&numDefs))
|
||||
return d.fail("expected number of globals");
|
||||
|
||||
uint32_t numGlobals = globals->length() + numDefs;
|
||||
if (numGlobals > MaxGlobals)
|
||||
CheckedInt<uint32_t> numGlobals = globals->length();
|
||||
numGlobals += numDefs;
|
||||
if (!numGlobals.isValid() || numGlobals.value() > MaxGlobals)
|
||||
return d.fail("too many globals");
|
||||
|
||||
if (!globals->reserve(numGlobals))
|
||||
if (!globals->reserve(numGlobals.value()))
|
||||
return false;
|
||||
|
||||
for (uint32_t i = 0; i < numDefs; i++) {
|
||||
|
|
@ -894,6 +1296,59 @@ wasm::DecodeModuleEnvironment(Decoder& d, ModuleEnvironment* env)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeFunctionBody(Decoder& d, const ModuleEnvironment& env, uint32_t funcIndex)
|
||||
{
|
||||
uint32_t bodySize;
|
||||
if (!d.readVarU32(&bodySize))
|
||||
return d.fail("expected number of function body bytes");
|
||||
|
||||
if (d.bytesRemain() < bodySize)
|
||||
return d.fail("function body length too big");
|
||||
|
||||
const uint8_t* bodyBegin = d.currentPosition();
|
||||
|
||||
if (!ValidateFunctionBody(env, funcIndex, d))
|
||||
return false;
|
||||
|
||||
if (d.currentPosition() != bodyBegin + bodySize)
|
||||
return d.fail("function body length mismatch");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeCodeSection(Decoder& d, const ModuleEnvironment& env)
|
||||
{
|
||||
uint32_t sectionStart, sectionSize;
|
||||
if (!d.startSection(SectionId::Code, §ionStart, §ionSize, "code"))
|
||||
return false;
|
||||
|
||||
if (sectionStart == Decoder::NotStarted) {
|
||||
if (env.numFuncDefs() != 0)
|
||||
return d.fail("expected function bodies");
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t numFuncDefs;
|
||||
if (!d.readVarU32(&numFuncDefs))
|
||||
return d.fail("expected function body count");
|
||||
|
||||
if (numFuncDefs != env.numFuncDefs())
|
||||
return d.fail("function body count does not match function signature count");
|
||||
|
||||
for (uint32_t funcDefIndex = 0; funcDefIndex < numFuncDefs; funcDefIndex++) {
|
||||
if (!DecodeFunctionBody(d, env, env.numFuncImports() + funcDefIndex))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!d.finishSection(sectionStart, sectionSize, "code"))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
wasm::DecodeDataSection(Decoder& d, const ModuleEnvironment& env, DataSegmentVector* segments)
|
||||
{
|
||||
|
|
@ -951,5 +1406,30 @@ wasm::DecodeUnknownSections(Decoder& d)
|
|||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Validate algorithm.
|
||||
|
||||
bool
|
||||
wasm::Validate(const ShareableBytes& bytecode, UniqueChars* error)
|
||||
{
|
||||
Decoder d(bytecode.begin(), bytecode.end(), error);
|
||||
|
||||
ModuleEnvironment env;
|
||||
if (!DecodeModuleEnvironment(d, &env))
|
||||
return false;
|
||||
|
||||
if (!DecodeCodeSection(d, env))
|
||||
return false;
|
||||
|
||||
DataSegmentVector dataSegments;
|
||||
if (!DecodeDataSection(d, env, &dataSegments))
|
||||
return false;
|
||||
|
||||
if (!DecodeUnknownSections(d))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -16,10 +16,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef wasm_binary_format_h
|
||||
#define wasm_binary_format_h
|
||||
#ifndef wasm_validate_h
|
||||
#define wasm_validate_h
|
||||
|
||||
#include "wasm/WasmCode.h"
|
||||
#include "wasm/WasmTypes.h"
|
||||
|
||||
namespace js {
|
||||
namespace wasm {
|
||||
|
|
@ -622,6 +623,13 @@ struct ModuleEnvironment
|
|||
minMemoryLength(0)
|
||||
{}
|
||||
|
||||
size_t numTables() const {
|
||||
return tables.length();
|
||||
}
|
||||
size_t numSigs() const {
|
||||
return sigs.length();
|
||||
}
|
||||
|
||||
size_t numFuncs() const {
|
||||
// asm.js pre-reserves a bunch of function index space which is
|
||||
// incrementally filled in during function-body validation. Thus, there
|
||||
|
|
@ -637,6 +645,10 @@ struct ModuleEnvironment
|
|||
MOZ_ASSERT(!isAsmJS());
|
||||
return funcSigs.length() - funcImportGlobalDataOffsets.length();
|
||||
}
|
||||
size_t numFuncImports() const {
|
||||
MOZ_ASSERT(!isAsmJS());
|
||||
return funcImportGlobalDataOffsets.length();
|
||||
}
|
||||
bool usesMemory() const {
|
||||
return UsesMemory(memoryUsage);
|
||||
}
|
||||
|
|
@ -665,6 +677,12 @@ DecodeDataSection(Decoder& d, const ModuleEnvironment& env, DataSegmentVector* s
|
|||
MOZ_MUST_USE bool
|
||||
DecodeUnknownSections(Decoder& d);
|
||||
|
||||
[[nodiscard]] bool
|
||||
ValidateFunctionBody(const ModuleEnvironment& env, uint32_t funcIndex, Decoder& d);
|
||||
|
||||
[[nodiscard]] bool
|
||||
Validate(const ShareableBytes& bytecode, UniqueChars* error);
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue