WebAssembly: ModuleInformation should be a ref counted thing that can be shared acros...
[WebKit-https.git] / Source / JavaScriptCore / wasm / WasmValidate.cpp
1 /*
2  * Copyright (C) 2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "WasmValidate.h"
28
29 #if ENABLE(WEBASSEMBLY)
30
31 #include "WasmFunctionParser.h"
32 #include <wtf/CommaPrinter.h>
33 #include <wtf/text/StringBuilder.h>
34
35 namespace JSC { namespace Wasm {
36
37 class Validate {
38 public:
39     class ControlData {
40     public:
41         ControlData(BlockType type, Type signature)
42             : m_blockType(type)
43             , m_signature(signature)
44         {
45         }
46
47         ControlData()
48         {
49         }
50
51         void dump(PrintStream& out) const
52         {
53             switch (type()) {
54             case BlockType::If:
55                 out.print("If:       ");
56                 break;
57             case BlockType::Block:
58                 out.print("Block:    ");
59                 break;
60             case BlockType::Loop:
61                 out.print("Loop:     ");
62                 break;
63             case BlockType::TopLevel:
64                 out.print("TopLevel: ");
65                 break;
66             }
67             out.print(makeString(signature()));
68         }
69
70         bool hasNonVoidSignature() const { return m_signature != Void; }
71
72         BlockType type() const { return m_blockType; }
73         Type signature() const { return m_signature; }
74     private:
75         BlockType m_blockType;
76         Type m_signature;
77     };
78     typedef String ErrorType;
79     typedef UnexpectedType<ErrorType> UnexpectedResult;
80     typedef Expected<void, ErrorType> Result;
81     typedef Type ExpressionType;
82     typedef ControlData ControlType;
83     typedef Vector<ExpressionType, 1> ExpressionList;
84     typedef FunctionParser<Validate>::ControlEntry ControlEntry;
85
86     static const ExpressionType emptyExpression = Void;
87
88     template <typename ...Args>
89     NEVER_INLINE UnexpectedResult WARN_UNUSED_RETURN fail(Args... args) const
90     {
91         using namespace FailureHelper; // See ADL comment in WasmParser.h.
92         return UnexpectedResult(makeString(ASCIILiteral("WebAssembly.Module doesn't validate: "), makeString(args)...));
93     }
94 #define WASM_VALIDATOR_FAIL_IF(condition, ...) do { \
95         if (UNLIKELY(condition))                    \
96         return fail(__VA_ARGS__);                   \
97     } while (0)
98
99     Result WARN_UNUSED_RETURN addArguments(const Signature&);
100     Result WARN_UNUSED_RETURN addLocal(Type, uint32_t);
101     ExpressionType addConstant(Type type, uint64_t) { return type; }
102
103     // Locals
104     Result WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result);
105     Result WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value);
106
107     // Globals
108     Result WARN_UNUSED_RETURN getGlobal(uint32_t index, ExpressionType& result);
109     Result WARN_UNUSED_RETURN setGlobal(uint32_t index, ExpressionType value);
110
111     // Memory
112     Result WARN_UNUSED_RETURN load(LoadOpType, ExpressionType pointer, ExpressionType& result, uint32_t offset);
113     Result WARN_UNUSED_RETURN store(StoreOpType, ExpressionType pointer, ExpressionType value, uint32_t offset);
114
115     // Basic operators
116     template<OpType>
117     Result WARN_UNUSED_RETURN addOp(ExpressionType arg, ExpressionType& result);
118     template<OpType>
119     Result WARN_UNUSED_RETURN addOp(ExpressionType left, ExpressionType right, ExpressionType& result);
120     Result WARN_UNUSED_RETURN addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result);
121
122     // Control flow
123     ControlData WARN_UNUSED_RETURN addTopLevel(Type signature);
124     ControlData WARN_UNUSED_RETURN addBlock(Type signature);
125     ControlData WARN_UNUSED_RETURN addLoop(Type signature);
126     Result WARN_UNUSED_RETURN addIf(ExpressionType condition, Type signature, ControlData& result);
127     Result WARN_UNUSED_RETURN addElse(ControlData&, const ExpressionList&);
128     Result WARN_UNUSED_RETURN addElseToUnreachable(ControlData&);
129
130     Result WARN_UNUSED_RETURN addReturn(ControlData& topLevel, const ExpressionList& returnValues);
131     Result WARN_UNUSED_RETURN addBranch(ControlData&, ExpressionType condition, const ExpressionList& expressionStack);
132     Result WARN_UNUSED_RETURN addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const ExpressionList& expressionStack);
133     Result WARN_UNUSED_RETURN endBlock(ControlEntry&, ExpressionList& expressionStack);
134     Result WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&);
135     Result WARN_UNUSED_RETURN addGrowMemory(ExpressionType delta, ExpressionType& result);
136     Result WARN_UNUSED_RETURN addCurrentMemory(ExpressionType& result);
137
138     Result WARN_UNUSED_RETURN addUnreachable() { return { }; }
139
140     // Calls
141     Result WARN_UNUSED_RETURN addCall(unsigned calleeIndex, const Signature&, const Vector<ExpressionType>& args, ExpressionType& result);
142     Result WARN_UNUSED_RETURN addCallIndirect(const Signature&, const Vector<ExpressionType>& args, ExpressionType& result);
143
144     bool hasMemory() const { return !!m_module.memory; }
145
146     Validate(const ModuleInformation& module)
147         : m_module(module)
148     {
149     }
150
151     void dump(const Vector<ControlEntry>&, const ExpressionList*);
152     void setParser(FunctionParser<Validate>*) { }
153
154 private:
155     Result WARN_UNUSED_RETURN unify(const ExpressionList&, const ControlData&);
156
157     Result WARN_UNUSED_RETURN checkBranchTarget(ControlData& target, const ExpressionList& expressionStack);
158
159     Vector<Type> m_locals;
160     const ModuleInformation& m_module;
161 };
162
163 auto Validate::addArguments(const Signature& signature) -> Result
164 {
165     for (size_t i = 0; i < signature.argumentCount(); ++i)
166         WASM_FAIL_IF_HELPER_FAILS(addLocal(signature.argument(i), 1));
167     return { };
168 }
169
170 auto Validate::addLocal(Type type, uint32_t count) -> Result
171 {
172     size_t size = m_locals.size() + count;
173     WASM_VALIDATOR_FAIL_IF(!m_locals.tryReserveCapacity(size), "can't allocate memory for ", size, " locals");
174
175     for (uint32_t i = 0; i < count; ++i)
176         m_locals.uncheckedAppend(type);
177     return { };
178 }
179
180 auto Validate::getLocal(uint32_t index, ExpressionType& result) -> Result
181 {
182     WASM_VALIDATOR_FAIL_IF(index >= m_locals.size(), "attempt to use unknown local ", index, " last one is ", m_locals.size());
183     result = m_locals[index];
184     return { };
185 }
186
187 auto Validate::setLocal(uint32_t index, ExpressionType value) -> Result
188 {
189     ExpressionType localType;
190     WASM_FAIL_IF_HELPER_FAILS(getLocal(index, localType));
191     WASM_VALIDATOR_FAIL_IF(localType != value, "set_local to type ", value, " expected ", localType);
192     return { };
193 }
194
195 auto Validate::getGlobal(uint32_t index, ExpressionType& result) -> Result
196 {
197     WASM_VALIDATOR_FAIL_IF(index >= m_module.globals.size(), "get_global ", index, " of unknown global, limit is ", m_module.globals.size());
198     result = m_module.globals[index].type;
199     ASSERT(isValueType(result));
200     return { };
201 }
202
203 auto Validate::setGlobal(uint32_t index, ExpressionType value) -> Result
204 {
205     WASM_VALIDATOR_FAIL_IF(index >= m_module.globals.size(), "set_global ", index, " of unknown global, limit is ", m_module.globals.size());
206     WASM_VALIDATOR_FAIL_IF(m_module.globals[index].mutability == Global::Immutable, "set_global ", index, " is immutable");
207
208     ExpressionType globalType = m_module.globals[index].type;
209     ASSERT(isValueType(globalType));
210     WASM_VALIDATOR_FAIL_IF(globalType != value, "set_global ", index, " with type ", globalType, " with a variable of type ", value);
211     return { };
212 }
213
214 Validate::ControlType Validate::addTopLevel(Type signature)
215 {
216     return ControlData(BlockType::TopLevel, signature);
217 }
218
219 Validate::ControlType Validate::addBlock(Type signature)
220 {
221     return ControlData(BlockType::Block, signature);
222 }
223
224 Validate::ControlType Validate::addLoop(Type signature)
225 {
226     return ControlData(BlockType::Loop, signature);
227 }
228
229 auto Validate::addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result) -> Result
230 {
231     WASM_VALIDATOR_FAIL_IF(condition != I32, "select condition must be i32, got ", condition);
232     WASM_VALIDATOR_FAIL_IF(nonZero != zero, "select result types must match, got ", nonZero, " and ", zero);
233     result = zero;
234     return { };
235 }
236
237 auto Validate::addIf(ExpressionType condition, Type signature, ControlType& result) -> Result
238 {
239     WASM_VALIDATOR_FAIL_IF(condition != I32, "if condition must be i32, got ", condition);
240     result = ControlData(BlockType::If, signature);
241     return { };
242 }
243
244 auto Validate::addElse(ControlType& current, const ExpressionList& values) -> Result
245 {
246     WASM_FAIL_IF_HELPER_FAILS(unify(values, current));
247     return addElseToUnreachable(current);
248 }
249
250 auto Validate::addElseToUnreachable(ControlType& current) -> Result
251 {
252     WASM_VALIDATOR_FAIL_IF(current.type() != BlockType::If, "else block isn't associated to an if");
253     current = ControlData(BlockType::Block, current.signature());
254     return { };
255 }
256
257 auto Validate::addReturn(ControlType& topLevel, const ExpressionList& returnValues) -> Result
258 {
259     ASSERT(topLevel.type() == BlockType::TopLevel);
260     if (topLevel.signature() == Void)
261         return { };
262     ASSERT(returnValues.size() == 1);
263     WASM_VALIDATOR_FAIL_IF(topLevel.signature() != returnValues[0], "return type ", returnValues[0], " doesn't match function's return type ", topLevel.signature());
264     return { };
265 }
266
267 auto Validate::checkBranchTarget(ControlType& target, const ExpressionList& expressionStack) -> Result
268     {
269         if (target.type() == BlockType::Loop)
270             return { };
271
272         if (target.signature() == Void)
273             return { };
274
275         WASM_VALIDATOR_FAIL_IF(expressionStack.isEmpty(), target.type() == BlockType::TopLevel ? "branch out of function" : "branch to block", " on empty expression stack, but expected ", target.signature());
276         WASM_VALIDATOR_FAIL_IF(target.signature() != expressionStack.last(), "branch's stack type doesn't match block's type");
277
278         return { };
279     }
280
281 auto Validate::addBranch(ControlType& target, ExpressionType condition, const ExpressionList& stack) -> Result
282 {
283     // Void means this is an unconditional branch.
284     WASM_VALIDATOR_FAIL_IF(condition != Void && condition != I32, "conditional branch with non-i32 condition ", condition);
285     return checkBranchTarget(target, stack);
286 }
287
288 auto Validate::addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const ExpressionList& expressionStack) -> Result
289 {
290     WASM_VALIDATOR_FAIL_IF(condition != I32, "br_table with non-i32 condition ", condition);
291
292     for (auto target : targets)
293         WASM_VALIDATOR_FAIL_IF(defaultTarget.signature() != target->signature(), "br_table target type mismatch");
294
295     return checkBranchTarget(defaultTarget, expressionStack);
296 }
297
298 auto Validate::addGrowMemory(ExpressionType delta, ExpressionType& result) -> Result
299 {
300     WASM_VALIDATOR_FAIL_IF(delta != I32, "grow_memory with non-i32 delta");
301     result = I32;
302     return { };
303 }
304
305 auto Validate::addCurrentMemory(ExpressionType& result) -> Result
306 {
307     result = I32;
308     return { };
309 }
310
311 auto Validate::endBlock(ControlEntry& entry, ExpressionList& stack) -> Result
312 {
313     WASM_FAIL_IF_HELPER_FAILS(unify(stack, entry.controlData));
314     return addEndToUnreachable(entry);
315 }
316
317 auto Validate::addEndToUnreachable(ControlEntry& entry) -> Result
318 {
319     auto block = entry.controlData;
320     if (block.signature() != Void) {
321         WASM_VALIDATOR_FAIL_IF(block.type() == BlockType::If, "If-block had a non-void result type: ", block.signature(), " but had no else-block");
322         entry.enclosedExpressionStack.append(block.signature());
323     }
324     return { };
325 }
326
327 auto Validate::addCall(unsigned, const Signature& signature, const Vector<ExpressionType>& args, ExpressionType& result) -> Result
328 {
329     WASM_VALIDATOR_FAIL_IF(signature.argumentCount() != args.size(), "arity mismatch in call, got ", args.size(), " arguments, expected ", signature.argumentCount());
330
331     for (unsigned i = 0; i < args.size(); ++i)
332         WASM_VALIDATOR_FAIL_IF(args[i] != signature.argument(i), "argument type mismatch in call, got ", args[i], ", expected ", signature.argument(i));
333
334     result = signature.returnType();
335     return { };
336 }
337
338 auto Validate::addCallIndirect(const Signature& signature, const Vector<ExpressionType>& args, ExpressionType& result) -> Result
339 {
340     const auto argumentCount = signature.argumentCount();
341     WASM_VALIDATOR_FAIL_IF(argumentCount != args.size() - 1, "arity mismatch in call_indirect, got ", args.size() - 1, " arguments, expected ", argumentCount);
342
343     for (unsigned i = 0; i < argumentCount; ++i)
344         WASM_VALIDATOR_FAIL_IF(args[i] != signature.argument(i), "argument type mismatch in call_indirect, got ", args[i], ", expected ", signature.argument(i));
345
346     WASM_VALIDATOR_FAIL_IF(args.last() != I32, "non-i32 call_indirect index ", args.last());
347
348     result = signature.returnType();
349     return { };
350 }
351
352 auto Validate::unify(const ExpressionList& values, const ControlType& block) -> Result
353 {
354     if (block.signature() == Void) {
355         WASM_VALIDATOR_FAIL_IF(!values.isEmpty(), "void block should end with an empty stack");
356         return { };
357     }
358
359     WASM_VALIDATOR_FAIL_IF(values.size() != 1, "block with type: ", block.signature(), " ends with a stack containing more than one value");
360     WASM_VALIDATOR_FAIL_IF(values[0] != block.signature(), "control flow returns with unexpected type");
361     return { };
362 }
363
364 static void dumpExpressionStack(const CommaPrinter& comma, const Validate::ExpressionList& expressionStack)
365 {
366     dataLog(comma, " ExpressionStack:");
367     for (const auto& expression : expressionStack)
368         dataLog(comma, makeString(expression));
369 }
370
371 void Validate::dump(const Vector<ControlEntry>& controlStack, const ExpressionList* expressionStack)
372 {
373     for (size_t i = controlStack.size(); i--;) {
374         dataLog("  ", controlStack[i].controlData);
375         CommaPrinter comma(", ", "");
376         dumpExpressionStack(comma, *expressionStack);
377         expressionStack = &controlStack[i].enclosedExpressionStack;
378         dataLogLn();
379     }
380     dataLogLn();
381 }
382
383 Expected<void, String> validateFunction(const uint8_t* source, size_t length, const Signature& signature, const ModuleInformation& module)
384 {
385     Validate context(module);
386     FunctionParser<Validate> validator(context, source, length, signature, module);
387     WASM_FAIL_IF_HELPER_FAILS(validator.parse());
388     return { };
389 }
390
391 } } // namespace JSC::Wasm
392
393 #include "WasmValidateInlines.h"
394
395 #endif // ENABLE(WEBASSEMBLY)