diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/wasm/WasmValidate.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/JavaScriptCore/wasm/WasmValidate.cpp')
-rw-r--r-- | Source/JavaScriptCore/wasm/WasmValidate.cpp | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/wasm/WasmValidate.cpp b/Source/JavaScriptCore/wasm/WasmValidate.cpp new file mode 100644 index 000000000..75d3826ca --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmValidate.cpp @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WasmValidate.h" + +#if ENABLE(WEBASSEMBLY) + +#include "WasmFunctionParser.h" +#include <wtf/CommaPrinter.h> +#include <wtf/text/StringBuilder.h> + +namespace JSC { namespace Wasm { + +class Validate { +public: + class ControlData { + public: + ControlData(BlockType type, Type signature) + : m_blockType(type) + , m_signature(signature) + { + } + + ControlData() + { + } + + void dump(PrintStream& out) const + { + switch (type()) { + case BlockType::If: + out.print("If: "); + break; + case BlockType::Block: + out.print("Block: "); + break; + case BlockType::Loop: + out.print("Loop: "); + break; + case BlockType::TopLevel: + out.print("TopLevel: "); + break; + } + out.print(makeString(signature())); + } + + bool hasNonVoidSignature() const { return m_signature != Void; } + + BlockType type() const { return m_blockType; } + Type signature() const { return m_signature; } + private: + BlockType m_blockType; + Type m_signature; + }; + typedef String ErrorType; + typedef UnexpectedType<ErrorType> UnexpectedResult; + typedef Expected<void, ErrorType> Result; + typedef Type ExpressionType; + typedef ControlData ControlType; + typedef Vector<ExpressionType, 1> ExpressionList; + typedef FunctionParser<Validate>::ControlEntry ControlEntry; + + static const ExpressionType emptyExpression = Void; + + template <typename ...Args> + NEVER_INLINE UnexpectedResult WARN_UNUSED_RETURN fail(Args... args) const + { + using namespace FailureHelper; // See ADL comment in WasmParser.h. + return UnexpectedResult(makeString(ASCIILiteral("WebAssembly.Module doesn't validate: "), makeString(args)...)); + } +#define WASM_VALIDATOR_FAIL_IF(condition, ...) do { \ + if (UNLIKELY(condition)) \ + return fail(__VA_ARGS__); \ + } while (0) + + Result WARN_UNUSED_RETURN addArguments(const Signature*); + Result WARN_UNUSED_RETURN addLocal(Type, uint32_t); + ExpressionType addConstant(Type type, uint64_t) { return type; } + + // Locals + Result WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result); + Result WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value); + + // Globals + Result WARN_UNUSED_RETURN getGlobal(uint32_t index, ExpressionType& result); + Result WARN_UNUSED_RETURN setGlobal(uint32_t index, ExpressionType value); + + // Memory + Result WARN_UNUSED_RETURN load(LoadOpType, ExpressionType pointer, ExpressionType& result, uint32_t offset); + Result WARN_UNUSED_RETURN store(StoreOpType, ExpressionType pointer, ExpressionType value, uint32_t offset); + + // Basic operators + template<OpType> + Result WARN_UNUSED_RETURN addOp(ExpressionType arg, ExpressionType& result); + template<OpType> + Result WARN_UNUSED_RETURN addOp(ExpressionType left, ExpressionType right, ExpressionType& result); + Result WARN_UNUSED_RETURN addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result); + + // Control flow + ControlData WARN_UNUSED_RETURN addTopLevel(Type signature); + ControlData WARN_UNUSED_RETURN addBlock(Type signature); + ControlData WARN_UNUSED_RETURN addLoop(Type signature); + Result WARN_UNUSED_RETURN addIf(ExpressionType condition, Type signature, ControlData& result); + Result WARN_UNUSED_RETURN addElse(ControlData&, const ExpressionList&); + Result WARN_UNUSED_RETURN addElseToUnreachable(ControlData&); + + Result WARN_UNUSED_RETURN addReturn(ControlData& topLevel, const ExpressionList& returnValues); + Result WARN_UNUSED_RETURN addBranch(ControlData&, ExpressionType condition, const ExpressionList& expressionStack); + Result WARN_UNUSED_RETURN addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const ExpressionList& expressionStack); + Result WARN_UNUSED_RETURN endBlock(ControlEntry&, ExpressionList& expressionStack); + Result WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&); + Result WARN_UNUSED_RETURN addGrowMemory(ExpressionType delta, ExpressionType& result); + Result WARN_UNUSED_RETURN addCurrentMemory(ExpressionType& result); + + Result WARN_UNUSED_RETURN addUnreachable() { return { }; } + + // Calls + Result WARN_UNUSED_RETURN addCall(unsigned calleeIndex, const Signature*, const Vector<ExpressionType>& args, ExpressionType& result); + Result WARN_UNUSED_RETURN addCallIndirect(const Signature*, SignatureIndex, const Vector<ExpressionType>& args, ExpressionType& result); + + bool hasMemory() const { return !!m_module.memory; } + + Validate(const ModuleInformation& module) + : m_module(module) + { + } + + void dump(const Vector<ControlEntry>&, const ExpressionList*); + +private: + Result WARN_UNUSED_RETURN unify(const ExpressionList&, const ControlData&); + + Result WARN_UNUSED_RETURN checkBranchTarget(ControlData& target, const ExpressionList& expressionStack); + + Vector<Type> m_locals; + const ModuleInformation& m_module; +}; + +auto Validate::addArguments(const Signature* signature) -> Result +{ + for (size_t i = 0; i < signature->argumentCount(); ++i) + WASM_FAIL_IF_HELPER_FAILS(addLocal(signature->argument(i), 1)); + return { }; +} + +auto Validate::addLocal(Type type, uint32_t count) -> Result +{ + size_t size = m_locals.size() + count; + WASM_VALIDATOR_FAIL_IF(!m_locals.tryReserveCapacity(size), "can't allocate memory for ", size, " locals"); + + for (uint32_t i = 0; i < count; ++i) + m_locals.uncheckedAppend(type); + return { }; +} + +auto Validate::getLocal(uint32_t index, ExpressionType& result) -> Result +{ + WASM_VALIDATOR_FAIL_IF(index >= m_locals.size(), "attempt to use unknown local ", index, " last one is ", m_locals.size()); + result = m_locals[index]; + return { }; +} + +auto Validate::setLocal(uint32_t index, ExpressionType value) -> Result +{ + ExpressionType localType; + WASM_FAIL_IF_HELPER_FAILS(getLocal(index, localType)); + WASM_VALIDATOR_FAIL_IF(localType != value, "set_local to type ", value, " expected ", localType); + return { }; +} + +auto Validate::getGlobal(uint32_t index, ExpressionType& result) -> Result +{ + WASM_VALIDATOR_FAIL_IF(index >= m_module.globals.size(), "get_global ", index, " of unknown global, limit is ", m_module.globals.size()); + result = m_module.globals[index].type; + ASSERT(isValueType(result)); + return { }; +} + +auto Validate::setGlobal(uint32_t index, ExpressionType value) -> Result +{ + WASM_VALIDATOR_FAIL_IF(index >= m_module.globals.size(), "set_global ", index, " of unknown global, limit is ", m_module.globals.size()); + WASM_VALIDATOR_FAIL_IF(m_module.globals[index].mutability == Global::Immutable, "set_global ", index, " is immutable"); + + ExpressionType globalType = m_module.globals[index].type; + ASSERT(isValueType(globalType)); + WASM_VALIDATOR_FAIL_IF(globalType != value, "set_global ", index, " with type ", globalType, " with a variable of type ", value); + return { }; +} + +Validate::ControlType Validate::addTopLevel(Type signature) +{ + return ControlData(BlockType::TopLevel, signature); +} + +Validate::ControlType Validate::addBlock(Type signature) +{ + return ControlData(BlockType::Block, signature); +} + +Validate::ControlType Validate::addLoop(Type signature) +{ + return ControlData(BlockType::Loop, signature); +} + +auto Validate::addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result) -> Result +{ + WASM_VALIDATOR_FAIL_IF(condition != I32, "select condition must be i32, got ", condition); + WASM_VALIDATOR_FAIL_IF(nonZero != zero, "select result types must match, got ", nonZero, " and ", zero); + result = zero; + return { }; +} + +auto Validate::addIf(ExpressionType condition, Type signature, ControlType& result) -> Result +{ + WASM_VALIDATOR_FAIL_IF(condition != I32, "if condition must be i32, got ", condition); + result = ControlData(BlockType::If, signature); + return { }; +} + +auto Validate::addElse(ControlType& current, const ExpressionList& values) -> Result +{ + WASM_FAIL_IF_HELPER_FAILS(unify(values, current)); + return addElseToUnreachable(current); +} + +auto Validate::addElseToUnreachable(ControlType& current) -> Result +{ + WASM_VALIDATOR_FAIL_IF(current.type() != BlockType::If, "else block isn't associated to an if"); + current = ControlData(BlockType::Block, current.signature()); + return { }; +} + +auto Validate::addReturn(ControlType& topLevel, const ExpressionList& returnValues) -> Result +{ + ASSERT(topLevel.type() == BlockType::TopLevel); + if (topLevel.signature() == Void) + return { }; + ASSERT(returnValues.size() == 1); + WASM_VALIDATOR_FAIL_IF(topLevel.signature() != returnValues[0], "return type ", returnValues[0], " doesn't match function's return type ", topLevel.signature()); + return { }; +} + +auto Validate::checkBranchTarget(ControlType& target, const ExpressionList& expressionStack) -> Result + { + if (target.type() == BlockType::Loop) + return { }; + + if (target.signature() == Void) + return { }; + + WASM_VALIDATOR_FAIL_IF(expressionStack.isEmpty(), "branch to block on empty expression stack"); + WASM_VALIDATOR_FAIL_IF(target.signature() != expressionStack.last(), "branch's stack type doesn't match block's type"); + + return { }; + } + +auto Validate::addBranch(ControlType& target, ExpressionType condition, const ExpressionList& stack) -> Result +{ + // Void means this is an unconditional branch. + WASM_VALIDATOR_FAIL_IF(condition != Void && condition != I32, "conditional branch with non-i32 condition ", condition); + return checkBranchTarget(target, stack); +} + +auto Validate::addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const ExpressionList& expressionStack) -> Result +{ + WASM_VALIDATOR_FAIL_IF(condition != I32, "br_table with non-i32 condition ", condition); + + for (auto target : targets) + WASM_VALIDATOR_FAIL_IF(defaultTarget.signature() != target->signature(), "br_table target type mismatch"); + + return checkBranchTarget(defaultTarget, expressionStack); +} + +auto Validate::addGrowMemory(ExpressionType delta, ExpressionType& result) -> Result +{ + WASM_VALIDATOR_FAIL_IF(delta != I32, "grow_memory with non-i32 delta"); + result = I32; + return { }; +} + +auto Validate::addCurrentMemory(ExpressionType& result) -> Result +{ + result = I32; + return { }; +} + +auto Validate::endBlock(ControlEntry& entry, ExpressionList& stack) -> Result +{ + WASM_FAIL_IF_HELPER_FAILS(unify(stack, entry.controlData)); + return addEndToUnreachable(entry); +} + +auto Validate::addEndToUnreachable(ControlEntry& entry) -> Result +{ + auto block = entry.controlData; + if (block.signature() != Void) { + WASM_VALIDATOR_FAIL_IF(block.type() == BlockType::If, "If-block had a non-void result type: ", block.signature(), " but had no else-block"); + entry.enclosedExpressionStack.append(block.signature()); + } + return { }; +} + +auto Validate::addCall(unsigned, const Signature* signature, const Vector<ExpressionType>& args, ExpressionType& result) -> Result +{ + WASM_VALIDATOR_FAIL_IF(signature->argumentCount() != args.size(), "arity mismatch in call, got ", args.size(), " arguments, expected ", signature->argumentCount()); + + for (unsigned i = 0; i < args.size(); ++i) + WASM_VALIDATOR_FAIL_IF(args[i] != signature->argument(i), "argument type mismatch in call, got ", args[i], ", expected ", signature->argument(i)); + + result = signature->returnType(); + return { }; +} + +auto Validate::addCallIndirect(const Signature* signature, SignatureIndex signatureIndex, const Vector<ExpressionType>& args, ExpressionType& result) -> Result +{ + UNUSED_PARAM(signatureIndex); + ASSERT(signatureIndex != Signature::invalidIndex); + const auto argumentCount = signature->argumentCount(); + WASM_VALIDATOR_FAIL_IF(argumentCount != args.size() - 1, "arity mismatch in call_indirect, got ", args.size() - 1, " arguments, expected ", argumentCount); + + for (unsigned i = 0; i < argumentCount; ++i) + WASM_VALIDATOR_FAIL_IF(args[i] != signature->argument(i), "argument type mismatch in call_indirect, got ", args[i], ", expected ", signature->argument(i)); + + WASM_VALIDATOR_FAIL_IF(args.last() != I32, "non-i32 call_indirect index ", args.last()); + + result = signature->returnType(); + return { }; +} + +auto Validate::unify(const ExpressionList& values, const ControlType& block) -> Result +{ + if (block.signature() == Void) { + WASM_VALIDATOR_FAIL_IF(!values.isEmpty(), "void block should end with an empty stack"); + return { }; + } + + WASM_VALIDATOR_FAIL_IF(values.size() != 1, "block with type: ", block.signature(), " ends with a stack containing more than one value"); + WASM_VALIDATOR_FAIL_IF(values[0] != block.signature(), "control flow returns with unexpected type"); + return { }; +} + +static void dumpExpressionStack(const CommaPrinter& comma, const Validate::ExpressionList& expressionStack) +{ + dataLog(comma, " ExpressionStack:"); + for (const auto& expression : expressionStack) + dataLog(comma, makeString(expression)); +} + +void Validate::dump(const Vector<ControlEntry>& controlStack, const ExpressionList* expressionStack) +{ + for (size_t i = controlStack.size(); i--;) { + dataLog(" ", controlStack[i].controlData); + CommaPrinter comma(", ", ""); + dumpExpressionStack(comma, *expressionStack); + expressionStack = &controlStack[i].enclosedExpressionStack; + dataLogLn(); + } + dataLogLn(); +} + +Expected<void, String> validateFunction(VM* vm, const uint8_t* source, size_t length, const Signature* signature, const ModuleInformation& module, const Vector<SignatureIndex>& moduleSignatureIndicesToUniquedSignatureIndices) +{ + Validate context(module); + FunctionParser<Validate> validator(vm, context, source, length, signature, module, moduleSignatureIndicesToUniquedSignatureIndices); + WASM_FAIL_IF_HELPER_FAILS(validator.parse()); + return { }; +} + +} } // namespace JSC::Wasm + +#include "WasmValidateInlines.h" + +#endif // ENABLE(WEBASSEMBLY) |