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