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 "JSCJSValueInlines.h"
32#include "WasmFunctionParser.h"
33#include <wtf/CommaPrinter.h>
34
35namespace JSC { namespace Wasm {
36
37class Validate {
38public:
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 Type branchTargetSignature() const { return type() == BlockType::Loop ? Void : signature(); }
75 private:
76 BlockType m_blockType;
77 Type m_signature;
78 };
79 typedef String ErrorType;
80 typedef Unexpected<ErrorType> UnexpectedResult;
81 typedef Expected<void, ErrorType> Result;
82 typedef Type ExpressionType;
83 typedef ControlData ControlType;
84 typedef Vector<ExpressionType, 1> ExpressionList;
85 typedef FunctionParser<Validate>::ControlEntry ControlEntry;
86
87 static constexpr ExpressionType emptyExpression() { return Void; }
88
89 template <typename ...Args>
90 NEVER_INLINE UnexpectedResult WARN_UNUSED_RETURN fail(Args... args) const
91 {
92 using namespace FailureHelper; // See ADL comment in WasmParser.h.
93 return UnexpectedResult(makeString("WebAssembly.Module doesn't validate: "_s, makeString(args)...));
94 }
95#define WASM_VALIDATOR_FAIL_IF(condition, ...) do { \
96 if (UNLIKELY(condition)) \
97 return fail(__VA_ARGS__); \
98 } while (0)
99
100 Result WARN_UNUSED_RETURN addArguments(const Signature&);
101 Result WARN_UNUSED_RETURN addLocal(Type, uint32_t);
102 ExpressionType addConstant(Type type, uint64_t) { return type; }
103
104 // References
105 Result WARN_UNUSED_RETURN addRefIsNull(ExpressionType& value, ExpressionType& result);
106 Result WARN_UNUSED_RETURN addRefFunc(uint32_t index, ExpressionType& result);
107
108 // Tables
109 Result WARN_UNUSED_RETURN addTableGet(unsigned, ExpressionType& index, ExpressionType& result);
110 Result WARN_UNUSED_RETURN addTableSet(unsigned, ExpressionType& index, ExpressionType& value);
111 Result WARN_UNUSED_RETURN addTableSize(unsigned, ExpressionType& result);
112 Result WARN_UNUSED_RETURN addTableGrow(unsigned, ExpressionType& fill, ExpressionType& delta, ExpressionType& result);
113 Result WARN_UNUSED_RETURN addTableFill(unsigned, ExpressionType& offset, ExpressionType& fill, ExpressionType& count);
114 // Locals
115 Result WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result);
116 Result WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value);
117
118 // Globals
119 Result WARN_UNUSED_RETURN getGlobal(uint32_t index, ExpressionType& result);
120 Result WARN_UNUSED_RETURN setGlobal(uint32_t index, ExpressionType value);
121
122 // Memory
123 Result WARN_UNUSED_RETURN load(LoadOpType, ExpressionType pointer, ExpressionType& result, uint32_t offset);
124 Result WARN_UNUSED_RETURN store(StoreOpType, ExpressionType pointer, ExpressionType value, uint32_t offset);
125
126 // Basic operators
127 template<OpType>
128 Result WARN_UNUSED_RETURN addOp(ExpressionType arg, ExpressionType& result);
129 template<OpType>
130 Result WARN_UNUSED_RETURN addOp(ExpressionType left, ExpressionType right, ExpressionType& result);
131 Result WARN_UNUSED_RETURN addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result);
132
133 // Control flow
134 ControlData WARN_UNUSED_RETURN addTopLevel(Type signature);
135 ControlData WARN_UNUSED_RETURN addBlock(Type signature);
136 ControlData WARN_UNUSED_RETURN addLoop(Type signature);
137 Result WARN_UNUSED_RETURN addIf(ExpressionType condition, Type signature, ControlData& result);
138 Result WARN_UNUSED_RETURN addElse(ControlData&, const ExpressionList&);
139 Result WARN_UNUSED_RETURN addElseToUnreachable(ControlData&);
140
141 Result WARN_UNUSED_RETURN addReturn(ControlData& topLevel, const ExpressionList& returnValues);
142 Result WARN_UNUSED_RETURN addBranch(ControlData&, ExpressionType condition, const ExpressionList& expressionStack);
143 Result WARN_UNUSED_RETURN addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const ExpressionList& expressionStack);
144 Result WARN_UNUSED_RETURN endBlock(ControlEntry&, ExpressionList& expressionStack);
145 Result WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&);
146 Result WARN_UNUSED_RETURN addGrowMemory(ExpressionType delta, ExpressionType& result);
147 Result WARN_UNUSED_RETURN addCurrentMemory(ExpressionType& result);
148
149 Result WARN_UNUSED_RETURN addUnreachable() { return { }; }
150
151 // Calls
152 Result WARN_UNUSED_RETURN addCall(unsigned calleeIndex, const Signature&, const Vector<ExpressionType>& args, ExpressionType& result);
153 Result WARN_UNUSED_RETURN addCallIndirect(unsigned tableIndex, const Signature&, const Vector<ExpressionType>& args, ExpressionType& result);
154
155 ALWAYS_INLINE void didKill(ExpressionType) { }
156
157 bool hasMemory() const { return !!m_module.memory; }
158
159 Validate(const ModuleInformation& module)
160 : m_module(module)
161 {
162 }
163
164 void dump(const Vector<ControlEntry>&, const ExpressionList*);
165 void setParser(FunctionParser<Validate>*) { }
166
167private:
168 Result WARN_UNUSED_RETURN unify(const ExpressionList&, const ControlData&);
169
170 Result WARN_UNUSED_RETURN checkBranchTarget(ControlData& target, const ExpressionList& expressionStack);
171
172 Vector<Type> m_locals;
173 const ModuleInformation& m_module;
174};
175
176auto Validate::addArguments(const Signature& signature) -> Result
177{
178 for (size_t i = 0; i < signature.argumentCount(); ++i)
179 WASM_FAIL_IF_HELPER_FAILS(addLocal(signature.argument(i), 1));
180 return { };
181}
182
183auto Validate::addTableGet(unsigned tableIndex, ExpressionType& index, ExpressionType& result) -> Result
184{
185 WASM_VALIDATOR_FAIL_IF(tableIndex >= m_module.tableCount(), "table index ", tableIndex, " is invalid, limit is ", m_module.tableCount());
186 result = m_module.tables[tableIndex].wasmType();
187 WASM_VALIDATOR_FAIL_IF(Type::I32 != index, "table.get index to type ", index, " expected ", Type::I32);
188
189 return { };
190}
191
192auto Validate::addTableSet(unsigned tableIndex, ExpressionType& index, ExpressionType& value) -> Result
193{
194 WASM_VALIDATOR_FAIL_IF(tableIndex >= m_module.tableCount(), "table index ", tableIndex, " is invalid, limit is ", m_module.tableCount());
195 auto type = m_module.tables[tableIndex].wasmType();
196 WASM_VALIDATOR_FAIL_IF(Type::I32 != index, "table.set index to type ", index, " expected ", Type::I32);
197 WASM_VALIDATOR_FAIL_IF(!isSubtype(value, type), "table.set value to type ", value, " expected ", type);
198 RELEASE_ASSERT(m_module.tables[tableIndex].type() == TableElementType::Anyref || m_module.tables[tableIndex].type() == TableElementType::Funcref);
199
200 return { };
201}
202
203auto Validate::addTableSize(unsigned tableIndex, ExpressionType& result) -> Result
204{
205 result = Type::I32;
206 WASM_VALIDATOR_FAIL_IF(tableIndex >= m_module.tableCount(), "table index ", tableIndex, " is invalid, limit is ", m_module.tableCount());
207
208 return { };
209}
210
211auto Validate::addTableGrow(unsigned tableIndex, ExpressionType& fill, ExpressionType& delta, ExpressionType& result) -> Result
212{
213 result = Type::I32;
214 WASM_VALIDATOR_FAIL_IF(tableIndex >= m_module.tableCount(), "table index ", tableIndex, " is invalid, limit is ", m_module.tableCount());
215 WASM_VALIDATOR_FAIL_IF(!isSubtype(fill, m_module.tables[tableIndex].wasmType()), "table.grow expects fill value of type ", m_module.tables[tableIndex].wasmType(), " got ", fill);
216 WASM_VALIDATOR_FAIL_IF(Type::I32 != delta, "table.grow expects an i32 delta value, got ", delta);
217
218 return { };
219}
220
221auto Validate::addTableFill(unsigned tableIndex, ExpressionType& offset, ExpressionType& fill, ExpressionType& count) -> Result
222{
223 WASM_VALIDATOR_FAIL_IF(tableIndex >= m_module.tableCount(), "table index ", tableIndex, " is invalid, limit is ", m_module.tableCount());
224 WASM_VALIDATOR_FAIL_IF(!isSubtype(fill, m_module.tables[tableIndex].wasmType()), "table.fill expects fill value of type ", m_module.tables[tableIndex].wasmType(), " got ", fill);
225 WASM_VALIDATOR_FAIL_IF(Type::I32 != offset, "table.fill expects an i32 offset value, got ", offset);
226 WASM_VALIDATOR_FAIL_IF(Type::I32 != count, "table.fill expects an i32 count value, got ", count);
227
228 return { };
229}
230
231auto Validate::addRefIsNull(ExpressionType& value, ExpressionType& result) -> Result
232{
233 result = Type::I32;
234 WASM_VALIDATOR_FAIL_IF(!isSubtype(value, Type::Anyref), "ref.is_null to type ", value, " expected ", Type::Anyref);
235
236 return { };
237}
238
239auto Validate::addRefFunc(uint32_t index, ExpressionType& result) -> Result
240{
241 result = Type::Funcref;
242 WASM_VALIDATOR_FAIL_IF(index >= m_module.functionIndexSpaceSize(), "ref.func index ", index, " is too large, max is ", m_module.functionIndexSpaceSize());
243 m_module.addReferencedFunction(index);
244
245 return { };
246}
247
248auto Validate::addLocal(Type type, uint32_t count) -> Result
249{
250 size_t size = m_locals.size() + count;
251 WASM_VALIDATOR_FAIL_IF(!m_locals.tryReserveCapacity(size), "can't allocate memory for ", size, " locals");
252
253 for (uint32_t i = 0; i < count; ++i)
254 m_locals.uncheckedAppend(type);
255 return { };
256}
257
258auto Validate::getLocal(uint32_t index, ExpressionType& result) -> Result
259{
260 WASM_VALIDATOR_FAIL_IF(index >= m_locals.size(), "attempt to use unknown local ", index, " last one is ", m_locals.size());
261 result = m_locals[index];
262 return { };
263}
264
265auto Validate::setLocal(uint32_t index, ExpressionType value) -> Result
266{
267 ExpressionType localType;
268 WASM_FAIL_IF_HELPER_FAILS(getLocal(index, localType));
269 WASM_VALIDATOR_FAIL_IF(!isSubtype(value, localType), "set_local to type ", value, " expected ", localType);
270 return { };
271}
272
273auto Validate::getGlobal(uint32_t index, ExpressionType& result) -> Result
274{
275 WASM_VALIDATOR_FAIL_IF(index >= m_module.globals.size(), "get_global ", index, " of unknown global, limit is ", m_module.globals.size());
276 result = m_module.globals[index].type;
277 ASSERT(isValueType(result));
278 return { };
279}
280
281auto Validate::setGlobal(uint32_t index, ExpressionType value) -> Result
282{
283 WASM_VALIDATOR_FAIL_IF(index >= m_module.globals.size(), "set_global ", index, " of unknown global, limit is ", m_module.globals.size());
284 WASM_VALIDATOR_FAIL_IF(m_module.globals[index].mutability == Global::Immutable, "set_global ", index, " is immutable");
285
286 ExpressionType globalType = m_module.globals[index].type;
287 ASSERT(isValueType(globalType));
288 WASM_VALIDATOR_FAIL_IF(globalType != value, "set_global ", index, " with type ", globalType, " with a variable of type ", value);
289 return { };
290}
291
292Validate::ControlType Validate::addTopLevel(Type signature)
293{
294 return ControlData(BlockType::TopLevel, signature);
295}
296
297Validate::ControlType Validate::addBlock(Type signature)
298{
299 return ControlData(BlockType::Block, signature);
300}
301
302Validate::ControlType Validate::addLoop(Type signature)
303{
304 return ControlData(BlockType::Loop, signature);
305}
306
307auto Validate::addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result) -> Result
308{
309 WASM_VALIDATOR_FAIL_IF(condition != I32, "select condition must be i32, got ", condition);
310 WASM_VALIDATOR_FAIL_IF(nonZero != zero, "select result types must match, got ", nonZero, " and ", zero);
311 result = zero;
312 return { };
313}
314
315auto Validate::addIf(ExpressionType condition, Type signature, ControlType& result) -> Result
316{
317 WASM_VALIDATOR_FAIL_IF(condition != I32, "if condition must be i32, got ", condition);
318 result = ControlData(BlockType::If, signature);
319 return { };
320}
321
322auto Validate::addElse(ControlType& current, const ExpressionList& values) -> Result
323{
324 WASM_FAIL_IF_HELPER_FAILS(unify(values, current));
325 return addElseToUnreachable(current);
326}
327
328auto Validate::addElseToUnreachable(ControlType& current) -> Result
329{
330 WASM_VALIDATOR_FAIL_IF(current.type() != BlockType::If, "else block isn't associated to an if");
331 current = ControlData(BlockType::Block, current.signature());
332 return { };
333}
334
335auto Validate::addReturn(ControlType& topLevel, const ExpressionList& returnValues) -> Result
336{
337 ASSERT(topLevel.type() == BlockType::TopLevel);
338 if (topLevel.signature() == Void)
339 return { };
340 ASSERT(returnValues.size() == 1);
341 WASM_VALIDATOR_FAIL_IF(topLevel.signature() != returnValues[0], "return type ", returnValues[0], " doesn't match function's return type ", topLevel.signature());
342 return { };
343}
344
345auto Validate::checkBranchTarget(ControlType& target, const ExpressionList& expressionStack) -> Result
346{
347 if (target.branchTargetSignature() == Void)
348 return { };
349
350 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());
351 WASM_VALIDATOR_FAIL_IF(target.branchTargetSignature() != expressionStack.last(), "branch's stack type doesn't match block's type");
352
353 return { };
354}
355
356auto Validate::addBranch(ControlType& target, ExpressionType condition, const ExpressionList& stack) -> Result
357{
358 // Void means this is an unconditional branch.
359 WASM_VALIDATOR_FAIL_IF(condition != Void && condition != I32, "conditional branch with non-i32 condition ", condition);
360 return checkBranchTarget(target, stack);
361}
362
363auto Validate::addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const ExpressionList& expressionStack) -> Result
364{
365 WASM_VALIDATOR_FAIL_IF(condition != I32, "br_table with non-i32 condition ", condition);
366
367 for (auto target : targets)
368 WASM_VALIDATOR_FAIL_IF(defaultTarget.branchTargetSignature() != target->branchTargetSignature(), "br_table target type mismatch");
369
370 return checkBranchTarget(defaultTarget, expressionStack);
371}
372
373auto Validate::addGrowMemory(ExpressionType delta, ExpressionType& result) -> Result
374{
375 WASM_VALIDATOR_FAIL_IF(delta != I32, "grow_memory with non-i32 delta");
376 result = I32;
377 return { };
378}
379
380auto Validate::addCurrentMemory(ExpressionType& result) -> Result
381{
382 result = I32;
383 return { };
384}
385
386auto Validate::endBlock(ControlEntry& entry, ExpressionList& stack) -> Result
387{
388 WASM_FAIL_IF_HELPER_FAILS(unify(stack, entry.controlData));
389 return addEndToUnreachable(entry);
390}
391
392auto Validate::addEndToUnreachable(ControlEntry& entry) -> Result
393{
394 auto block = entry.controlData;
395 if (block.signature() != Void) {
396 WASM_VALIDATOR_FAIL_IF(block.type() == BlockType::If, "If-block had a non-void result type: ", block.signature(), " but had no else-block");
397 entry.enclosedExpressionStack.append(block.signature());
398 }
399 return { };
400}
401
402auto Validate::addCall(unsigned, const Signature& signature, const Vector<ExpressionType>& args, ExpressionType& result) -> Result
403{
404 WASM_VALIDATOR_FAIL_IF(signature.argumentCount() != args.size(), "arity mismatch in call, got ", args.size(), " arguments, expected ", signature.argumentCount());
405
406 for (unsigned i = 0; i < args.size(); ++i)
407 WASM_VALIDATOR_FAIL_IF(!isSubtype(args[i], signature.argument(i)), "argument type mismatch in call, got ", args[i], ", expected ", signature.argument(i));
408
409 result = signature.returnType();
410 return { };
411}
412
413auto Validate::addCallIndirect(unsigned tableIndex, const Signature& signature, const Vector<ExpressionType>& args, ExpressionType& result) -> Result
414{
415 RELEASE_ASSERT(tableIndex < m_module.tableCount());
416 RELEASE_ASSERT(m_module.tables[tableIndex].type() == TableElementType::Funcref);
417 const auto argumentCount = signature.argumentCount();
418 WASM_VALIDATOR_FAIL_IF(argumentCount != args.size() - 1, "arity mismatch in call_indirect, got ", args.size() - 1, " arguments, expected ", argumentCount);
419
420 for (unsigned i = 0; i < argumentCount; ++i)
421 WASM_VALIDATOR_FAIL_IF(!isSubtype(args[i], signature.argument(i)), "argument type mismatch in call_indirect, got ", args[i], ", expected ", signature.argument(i));
422
423 WASM_VALIDATOR_FAIL_IF(args.last() != I32, "non-i32 call_indirect index ", args.last());
424
425 result = signature.returnType();
426 return { };
427}
428
429auto Validate::unify(const ExpressionList& values, const ControlType& block) -> Result
430{
431 if (block.signature() == Void) {
432 WASM_VALIDATOR_FAIL_IF(!values.isEmpty(), "void block should end with an empty stack");
433 return { };
434 }
435
436 WASM_VALIDATOR_FAIL_IF(values.size() != 1, "block with type: ", block.signature(), " ends with a stack containing more than one value");
437 WASM_VALIDATOR_FAIL_IF(!isSubtype(values[0], block.signature()), "control flow returns with unexpected type");
438 return { };
439}
440
441static void dumpExpressionStack(const CommaPrinter& comma, const Validate::ExpressionList& expressionStack)
442{
443 dataLog(comma, " ExpressionStack:");
444 for (const auto& expression : expressionStack)
445 dataLog(comma, makeString(expression));
446}
447
448void Validate::dump(const Vector<ControlEntry>& controlStack, const ExpressionList* expressionStack)
449{
450 for (size_t i = controlStack.size(); i--;) {
451 dataLog(" ", controlStack[i].controlData);
452 CommaPrinter comma(", ", "");
453 dumpExpressionStack(comma, *expressionStack);
454 expressionStack = &controlStack[i].enclosedExpressionStack;
455 dataLogLn();
456 }
457 dataLogLn();
458}
459
460Expected<void, String> validateFunction(const uint8_t* source, size_t length, const Signature& signature, const ModuleInformation& module)
461{
462 Validate context(module);
463 FunctionParser<Validate> validator(context, source, length, signature, module);
464 WASM_FAIL_IF_HELPER_FAILS(validator.parse());
465 return { };
466}
467
468} } // namespace JSC::Wasm
469
470#include "WasmValidateInlines.h"
471
472#endif // ENABLE(WEBASSEMBLY)
473