blob: f9c0a3aeba59f03aa470d1b591bba39f7e9cd488 [file] [log] [blame]
/*
* Copyright (C) 2016-2019 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 "JSCJSValueInlines.h"
#include "WasmFunctionParser.h"
#include "WasmSignature.h"
#include <wtf/CommaPrinter.h>
namespace JSC { namespace Wasm {
class Validate {
public:
class ControlData {
public:
ControlData() = default;
ControlData(BlockType type, BlockSignature signature)
: m_blockType(type)
, m_signature(signature)
{
}
static bool isIf(const ControlData& control) { return control.blockType() == BlockType::If; }
static bool isTopLevel(const ControlData& control) { return control.blockType() == BlockType::TopLevel; }
BlockType blockType() const { return m_blockType; }
BlockSignature signature() const { return m_signature; }
unsigned branchTargetArity() const { return blockType() == BlockType::Loop ? signature()->argumentCount() : signature()->returnCount(); }
Type branchTargetType(unsigned i) const
{
ASSERT(i < branchTargetArity());
return blockType() == BlockType::Loop ? signature()->argument(i) : signature()->returnType(i);
}
void dump(PrintStream& out) const
{
switch (blockType()) {
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;
}
const Signature& sig = *signature();
out.print(sig);
}
private:
BlockType m_blockType;
BlockSignature m_signature;
};
enum ExpressionType { EmptyExpression };
using ErrorType = String;
using UnexpectedResult = Unexpected<ErrorType>;
using Result = Expected<void, ErrorType>;
using ControlType = ControlData;
using ControlEntry = FunctionParser<Validate>::ControlEntry;
using ControlStack = FunctionParser<Validate>::ControlStack;
using ResultList = FunctionParser<Validate>::ResultList;
using Stack = FunctionParser<Validate>::Stack;
static ExpressionType emptyExpression() { return EmptyExpression; };
template <typename ...Args>
NEVER_INLINE UnexpectedResult WARN_UNUSED_RETURN fail(const Args&... args) const
{
using namespace FailureHelper; // See ADL comment in WasmParser.h.
if (UNLIKELY(!ASSERT_DISABLED && Options::crashOnFailedWebAssemblyValidate()))
WTFBreakpointTrap();
StringPrintStream out;
out.print("WebAssembly.Module doesn't validate: "_s, args...);
return UnexpectedResult(out.toString());
}
#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, uint64_t) { return EmptyExpression; }
// References
Result WARN_UNUSED_RETURN addRefIsNull(ExpressionType value, ExpressionType& result);
Result WARN_UNUSED_RETURN addRefFunc(uint32_t index, ExpressionType& result);
// Tables
Result WARN_UNUSED_RETURN addTableGet(unsigned, ExpressionType index, ExpressionType& result);
Result WARN_UNUSED_RETURN addTableSet(unsigned, ExpressionType index, ExpressionType value);
Result WARN_UNUSED_RETURN addTableSize(unsigned, ExpressionType& result);
Result WARN_UNUSED_RETURN addTableGrow(unsigned, ExpressionType fill, ExpressionType delta, ExpressionType& result);
Result WARN_UNUSED_RETURN addTableFill(unsigned, ExpressionType offset, ExpressionType fill, ExpressionType count);
// 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(BlockSignature);
Result WARN_UNUSED_RETURN addBlock(BlockSignature, Stack& enclosingStack, ControlType& newBlock, Stack& newStack);
Result WARN_UNUSED_RETURN addLoop(BlockSignature, Stack& enclosingStack, ControlType& block, Stack& newStack, uint32_t loopIndex);
Result WARN_UNUSED_RETURN addIf(ExpressionType condition, BlockSignature, Stack& enclosingStack, ControlType& result, Stack& newStack);
Result WARN_UNUSED_RETURN addElse(ControlData&, const Stack&);
Result WARN_UNUSED_RETURN addElseToUnreachable(ControlData&);
Result WARN_UNUSED_RETURN addReturn(ControlData& topLevel, const Stack& returnValues);
Result WARN_UNUSED_RETURN addBranch(ControlData&, ExpressionType condition, const Stack& expressionStack);
Result WARN_UNUSED_RETURN addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const Stack& expressionStack);
Result WARN_UNUSED_RETURN endBlock(ControlEntry&, Stack& 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 { }; }
Result WARN_UNUSED_RETURN endTopLevel(BlockSignature, const Stack&) { return { }; }
// Calls
Result WARN_UNUSED_RETURN addCall(unsigned calleeIndex, const Signature&, const Vector<ExpressionType>& args, ResultList&);
Result WARN_UNUSED_RETURN addCallIndirect(unsigned tableIndex, const Signature&, const Vector<ExpressionType>& args, ResultList&);
Validate()
{
}
void dump(const ControlStack&, const Stack*);
void setParser(FunctionParser<Validate>*) { }
void didFinishParsingLocals() { }
void didPopValueFromStack() { }
private:
Result WARN_UNUSED_RETURN unify(const Stack&, const ControlData&);
};
auto Validate::addArguments(const Signature&) -> Result
{
return { };
}
auto Validate::addTableGet(unsigned, ExpressionType, ExpressionType&) -> Result
{
return { };
}
auto Validate::addTableSet(unsigned, ExpressionType, ExpressionType) -> Result
{
return { };
}
auto Validate::addTableSize(unsigned, ExpressionType&) -> Result
{
return { };
}
auto Validate::addTableGrow(unsigned, ExpressionType, ExpressionType, ExpressionType&) -> Result
{
return { };
}
auto Validate::addTableFill(unsigned, ExpressionType, ExpressionType, ExpressionType) -> Result
{
return { };
}
auto Validate::addRefIsNull(ExpressionType, ExpressionType&) -> Result
{
return { };
}
auto Validate::addRefFunc(uint32_t, ExpressionType&) -> Result
{
return { };
}
auto Validate::addLocal(Type, uint32_t) -> Result
{
return { };
}
auto Validate::getLocal(uint32_t, ExpressionType&) -> Result
{
return { };
}
auto Validate::setLocal(uint32_t, ExpressionType) -> Result
{
return { };
}
auto Validate::getGlobal(uint32_t, ExpressionType&) -> Result
{
return { };
}
auto Validate::setGlobal(uint32_t, ExpressionType) -> Result
{
return { };
}
Validate::ControlType Validate::addTopLevel(BlockSignature signature)
{
return ControlData(BlockType::TopLevel, signature);
}
auto Validate::addBlock(BlockSignature signature, Stack& enclosingStack, ControlType& newBlock, Stack& newStack) -> Result
{
splitStack(signature, enclosingStack, newStack);
newBlock = ControlData(BlockType::Block, signature);
return { };
}
auto Validate::addLoop(BlockSignature signature, Stack& enclosingStack, ControlType& loop, Stack& newStack, uint32_t) -> Result
{
splitStack(signature, enclosingStack, newStack);
loop = ControlData(BlockType::Loop, signature);
return { };
}
auto Validate::addSelect(ExpressionType, ExpressionType, ExpressionType, ExpressionType&) -> Result
{
return { };
}
auto Validate::addIf(ExpressionType, BlockSignature signature, Stack& enclosingStack, ControlType& result, Stack& newStack) -> Result
{
splitStack(signature, enclosingStack, newStack);
result = ControlData(BlockType::If, signature);
return { };
}
auto Validate::addElse(ControlType& current, const Stack&) -> Result
{
return addElseToUnreachable(current);
}
auto Validate::addElseToUnreachable(ControlType& current) -> Result
{
current = ControlData(BlockType::Block, current.signature());
return { };
}
auto Validate::addReturn(ControlType&, const Stack&) -> Result
{
return { };
}
auto Validate::addBranch(ControlType&, ExpressionType, const Stack&) -> Result
{
return { };
}
auto Validate::addSwitch(ExpressionType, const Vector<ControlData*>&, ControlData&, const Stack&) -> Result
{
return { };
}
auto Validate::addGrowMemory(ExpressionType, ExpressionType&) -> Result
{
return { };
}
auto Validate::addCurrentMemory(ExpressionType&) -> Result
{
return { };
}
auto Validate::endBlock(ControlEntry& entry, Stack&) -> Result
{
return addEndToUnreachable(entry);
}
auto Validate::addEndToUnreachable(ControlEntry& entry) -> Result
{
auto block = entry.controlData;
for (unsigned i = 0; i < block.signature()->returnCount(); ++i)
entry.enclosedExpressionStack.constructAndAppend(block.signature()->returnType(i), EmptyExpression);
return { };
}
auto Validate::addCall(unsigned, const Signature& signature, const Vector<ExpressionType>&, ResultList& results) -> Result
{
for (unsigned i = 0; i < signature.returnCount(); ++i)
results.append(EmptyExpression);
return { };
}
auto Validate::addCallIndirect(unsigned, const Signature& signature, const Vector<ExpressionType>&, ResultList& results) -> Result
{
for (unsigned i = 0; i < signature.returnCount(); ++i)
results.append(EmptyExpression);
return { };
}
auto Validate::load(LoadOpType, ExpressionType, ExpressionType&, uint32_t) -> Result
{
return { };
}
auto Validate::store(StoreOpType, ExpressionType, ExpressionType, uint32_t) -> Result
{
return { };
}
template<OpType>
auto Validate::addOp(ExpressionType, ExpressionType&) -> Result
{
return { };
}
template<OpType>
auto Validate::addOp(ExpressionType, ExpressionType, ExpressionType&) -> Result
{
return { };
}
static void dumpExpressionStack(const CommaPrinter& comma, const Validate::Stack& expressionStack)
{
dataLog(comma, " ExpressionStack:");
for (const auto& expression : expressionStack)
dataLog(comma, makeString(expression.type()));
}
void Validate::dump(const ControlStack& controlStack, const Stack* 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(const FunctionData& function, const Signature& signature, const ModuleInformation& module)
{
Validate context;
FunctionParser<Validate> validator(context, function.data.data(), function.data.size(), signature, module);
WASM_FAIL_IF_HELPER_FAILS(validator.parse());
return { };
}
} } // namespace JSC::Wasm
#endif // ENABLE(WEBASSEMBLY)