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