1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
3  *
4  * Copyright 2021 Mozilla Foundation
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 #include "wasm/WasmInitExpr.h"
20 
21 #include "mozilla/Maybe.h"
22 
23 #include "wasm/WasmInstance.h"
24 #include "wasm/WasmOpIter.h"
25 #include "wasm/WasmSerialize.h"
26 #include "wasm/WasmValidate.h"
27 
28 using namespace js;
29 using namespace js::wasm;
30 
ValidateInitExpr(Decoder & d,ModuleEnvironment * env,ValType expected,Maybe<LitVal> * literal)31 static bool ValidateInitExpr(Decoder& d, ModuleEnvironment* env,
32                              ValType expected, Maybe<LitVal>* literal) {
33   ValidatingOpIter iter(*env, d, ValidatingOpIter::InitExpr);
34 
35   if (!iter.startInitExpr(expected)) {
36     return false;
37   }
38 
39   // Perform trivial constant recovery, this is done so that codegen may
40   // generate optimal code for global.get on immutable globals with simple
41   // initializers.
42   //
43   // We simply update the last seen literal value while validating an
44   // instruction with a literal value, and clear the literal value when
45   // validating an instruction with a dynamic value. The last value is the
46   // literal for this init expressions, if any. This is correct because there
47   // are no drops or control flow allowed in init expressions.
48   *literal = Nothing();
49 
50   while (true) {
51     OpBytes op;
52     if (!iter.readOp(&op)) {
53       return false;
54     }
55 
56 #ifdef ENABLE_WASM_EXTENDED_CONST
57     Nothing nothing;
58 #endif
59     NothingVector nothings{};
60     ResultType unusedType;
61 
62     switch (op.b0) {
63       case uint16_t(Op::End): {
64         LabelKind kind;
65         if (!iter.readEnd(&kind, &unusedType, &nothings, &nothings)) {
66           return false;
67         }
68         MOZ_ASSERT(kind == LabelKind::Body);
69         iter.popEnd();
70         if (iter.controlStackEmpty()) {
71           return iter.endInitExpr();
72         }
73         break;
74       }
75       case uint16_t(Op::GetGlobal): {
76         uint32_t index;
77         if (!iter.readGetGlobal(&index)) {
78           return false;
79         }
80         *literal = Nothing();
81         break;
82       }
83       case uint16_t(Op::I32Const): {
84         int32_t c;
85         if (!iter.readI32Const(&c)) {
86           return false;
87         }
88         *literal = Some(LitVal(uint32_t(c)));
89         break;
90       }
91       case uint16_t(Op::I64Const): {
92         int64_t c;
93         if (!iter.readI64Const(&c)) {
94           return false;
95         }
96         *literal = Some(LitVal(uint64_t(c)));
97         break;
98       }
99       case uint16_t(Op::F32Const): {
100         float c;
101         if (!iter.readF32Const(&c)) {
102           return false;
103         }
104         *literal = Some(LitVal(c));
105         break;
106       }
107       case uint16_t(Op::F64Const): {
108         double c;
109         if (!iter.readF64Const(&c)) {
110           return false;
111         }
112         *literal = Some(LitVal(c));
113         break;
114       }
115 #ifdef ENABLE_WASM_SIMD
116       case uint16_t(Op::SimdPrefix): {
117         if (!env->v128Enabled()) {
118           return d.fail("v128 not enabled");
119         }
120         if (op.b1 != uint32_t(SimdOp::V128Const)) {
121           return d.fail("unexpected initializer opcode");
122         }
123         V128 c;
124         if (!iter.readV128Const(&c)) {
125           return false;
126         }
127         *literal = Some(LitVal(c));
128         break;
129       }
130 #endif
131       case uint16_t(Op::RefFunc): {
132         uint32_t funcIndex;
133         if (!iter.readRefFunc(&funcIndex)) {
134           return false;
135         }
136         env->declareFuncExported(funcIndex, /* eager */ false,
137                                  /* canRefFunc */ true);
138         *literal = Nothing();
139         break;
140       }
141       case uint16_t(Op::RefNull): {
142         RefType type;
143         if (!iter.readRefNull(&type)) {
144           return false;
145         }
146         *literal = Some(LitVal(ValType(type)));
147         break;
148       }
149 #ifdef ENABLE_WASM_EXTENDED_CONST
150       case uint16_t(Op::I32Add):
151       case uint16_t(Op::I32Sub):
152       case uint16_t(Op::I32Mul): {
153         if (!env->extendedConstEnabled()) {
154           return d.fail("unexpected initializer opcode");
155         }
156         if (!iter.readBinary(ValType::I32, &nothing, &nothing)) {
157           return false;
158         }
159         *literal = Nothing();
160         break;
161       }
162       case uint16_t(Op::I64Add):
163       case uint16_t(Op::I64Sub):
164       case uint16_t(Op::I64Mul): {
165         if (!env->extendedConstEnabled()) {
166           return d.fail("unexpected initializer opcode");
167         }
168         if (!iter.readBinary(ValType::I64, &nothing, &nothing)) {
169           return false;
170         }
171         *literal = Nothing();
172         break;
173       }
174 #endif
175       default: {
176         return d.fail("unexpected initializer opcode");
177       }
178     }
179   }
180 }
181 
182 class MOZ_STACK_CLASS InitExprInterpreter {
183  public:
InitExprInterpreter(JSContext * cx,const ValVector & globalImportValues,HandleWasmInstanceObject instanceObj)184   explicit InitExprInterpreter(JSContext* cx,
185                                const ValVector& globalImportValues,
186                                HandleWasmInstanceObject instanceObj)
187       : features(FeatureArgs::build(cx, FeatureOptions())),
188         stack(cx),
189         globalImportValues(globalImportValues),
190         instanceObj(cx, instanceObj) {}
191 
192   bool evaluate(Decoder& d);
193 
result()194   Val result() {
195     MOZ_ASSERT(stack.length() == 1);
196     return stack.popCopy();
197   }
198 
199  private:
200   FeatureArgs features;
201   RootedValVector stack;
202   const ValVector& globalImportValues;
203   RootedWasmInstanceObject instanceObj;
204 
pushI32(int32_t c)205   bool pushI32(int32_t c) { return stack.append(Val(uint32_t(c))); }
pushI64(int64_t c)206   bool pushI64(int64_t c) { return stack.append(Val(uint64_t(c))); }
pushF32(float c)207   bool pushF32(float c) { return stack.append(Val(c)); }
pushF64(double c)208   bool pushF64(double c) { return stack.append(Val(c)); }
pushV128(V128 c)209   bool pushV128(V128 c) { return stack.append(Val(c)); }
pushRef(ValType type,AnyRef ref)210   bool pushRef(ValType type, AnyRef ref) {
211     return stack.append(Val(type, ref));
212   }
pushFuncRef(FuncRef ref)213   bool pushFuncRef(FuncRef ref) {
214     return stack.append(Val(RefType::func(), ref));
215   }
216 
217 #ifdef ENABLE_WASM_EXTENDED_CONST
popI32()218   int32_t popI32() {
219     uint32_t result = stack.back().i32();
220     stack.popBack();
221     return int32_t(result);
222   }
popI64()223   int64_t popI64() {
224     uint64_t result = stack.back().i64();
225     stack.popBack();
226     return int64_t(result);
227   }
228 #endif
229 
evalGetGlobal(uint32_t index)230   bool evalGetGlobal(uint32_t index) {
231     return stack.append(globalImportValues[index]);
232   }
evalI32Const(int32_t c)233   bool evalI32Const(int32_t c) { return pushI32(c); }
evalI64Const(int64_t c)234   bool evalI64Const(int64_t c) { return pushI64(c); }
evalF32Const(float c)235   bool evalF32Const(float c) { return pushF32(c); }
evalF64Const(double c)236   bool evalF64Const(double c) { return pushF64(c); }
evalV128Const(V128 c)237   bool evalV128Const(V128 c) { return pushV128(c); }
evalRefFunc(uint32_t funcIndex)238   bool evalRefFunc(uint32_t funcIndex) {
239     void* fnref = Instance::refFunc(&instanceObj->instance(), funcIndex);
240     if (fnref == AnyRef::invalid().forCompiledCode()) {
241       return false;  // OOM, which has already been reported.
242     }
243     return pushFuncRef(FuncRef::fromCompiledCode(fnref));
244   }
evalRefNull(RefType type)245   bool evalRefNull(RefType type) { return pushRef(type, AnyRef::null()); }
246 #ifdef ENABLE_WASM_EXTENDED_CONST
evalI32Add()247   bool evalI32Add() {
248     uint32_t a = popI32();
249     uint32_t b = popI32();
250     pushI32(a + b);
251     return true;
252   }
evalI32Sub()253   bool evalI32Sub() {
254     uint32_t a = popI32();
255     uint32_t b = popI32();
256     pushI32(a - b);
257     return true;
258   }
evalI32Mul()259   bool evalI32Mul() {
260     uint32_t a = popI32();
261     uint32_t b = popI32();
262     pushI32(a * b);
263     return true;
264   }
evalI64Add()265   bool evalI64Add() {
266     uint64_t a = popI64();
267     uint64_t b = popI64();
268     pushI64(a + b);
269     return true;
270   }
evalI64Sub()271   bool evalI64Sub() {
272     uint64_t a = popI64();
273     uint64_t b = popI64();
274     pushI64(a - b);
275     return true;
276   }
evalI64Mul()277   bool evalI64Mul() {
278     uint64_t a = popI64();
279     uint64_t b = popI64();
280     pushI64(a * b);
281     return true;
282   }
283 #endif
284 };
285 
evaluate(Decoder & d)286 bool InitExprInterpreter::evaluate(Decoder& d) {
287 #define CHECK(c)          \
288   if (!(c)) return false; \
289   break
290 
291   while (true) {
292     OpBytes op;
293     if (!d.readOp(&op)) {
294       return false;
295     }
296 
297     switch (op.b0) {
298       case uint16_t(Op::End): {
299         return true;
300       }
301       case uint16_t(Op::GetGlobal): {
302         uint32_t index;
303         if (!d.readGetGlobal(&index)) {
304           return false;
305         }
306         CHECK(evalGetGlobal(index));
307       }
308       case uint16_t(Op::I32Const): {
309         int32_t c;
310         if (!d.readI32Const(&c)) {
311           return false;
312         }
313         CHECK(evalI32Const(c));
314       }
315       case uint16_t(Op::I64Const): {
316         int64_t c;
317         if (!d.readI64Const(&c)) {
318           return false;
319         }
320         CHECK(evalI64Const(c));
321       }
322       case uint16_t(Op::F32Const): {
323         float c;
324         if (!d.readF32Const(&c)) {
325           return false;
326         }
327         CHECK(evalF32Const(c));
328       }
329       case uint16_t(Op::F64Const): {
330         double c;
331         if (!d.readF64Const(&c)) {
332           return false;
333         }
334         CHECK(evalF64Const(c));
335       }
336 #ifdef ENABLE_WASM_SIMD
337       case uint16_t(Op::SimdPrefix): {
338         MOZ_RELEASE_ASSERT(op.b1 == uint32_t(SimdOp::V128Const));
339         V128 c;
340         if (!d.readV128Const(&c)) {
341           return false;
342         }
343         CHECK(evalV128Const(c));
344       }
345 #endif
346       case uint16_t(Op::RefFunc): {
347         uint32_t funcIndex;
348         if (!d.readRefFunc(&funcIndex)) {
349           return false;
350         }
351         CHECK(evalRefFunc(funcIndex));
352       }
353       case uint16_t(Op::RefNull): {
354         RefType type;
355         if (!d.readRefNull(features, &type)) {
356           return false;
357         }
358         CHECK(evalRefNull(type));
359       }
360 #ifdef ENABLE_WASM_EXTENDED_CONST
361       case uint16_t(Op::I32Add): {
362         if (!d.readBinary()) {
363           return false;
364         }
365         CHECK(evalI32Add());
366       }
367       case uint16_t(Op::I32Sub): {
368         if (!d.readBinary()) {
369           return false;
370         }
371         CHECK(evalI32Sub());
372       }
373       case uint16_t(Op::I32Mul): {
374         if (!d.readBinary()) {
375           return false;
376         }
377         CHECK(evalI32Mul());
378       }
379       case uint16_t(Op::I64Add): {
380         if (!d.readBinary()) {
381           return false;
382         }
383         CHECK(evalI64Add());
384       }
385       case uint16_t(Op::I64Sub): {
386         if (!d.readBinary()) {
387           return false;
388         }
389         CHECK(evalI64Sub());
390       }
391       case uint16_t(Op::I64Mul): {
392         if (!d.readBinary()) {
393           return false;
394         }
395         CHECK(evalI64Mul());
396       }
397 #endif
398       default: {
399         MOZ_CRASH();
400       }
401     }
402   }
403 
404 #undef CHECK
405 }
406 
decodeAndValidate(Decoder & d,ModuleEnvironment * env,ValType expected,InitExpr * expr)407 bool InitExpr::decodeAndValidate(Decoder& d, ModuleEnvironment* env,
408                                  ValType expected, InitExpr* expr) {
409   Maybe<LitVal> literal = Nothing();
410   const uint8_t* exprStart = d.currentPosition();
411   if (!ValidateInitExpr(d, env, expected, &literal)) {
412     return false;
413   }
414   const uint8_t* exprEnd = d.currentPosition();
415   size_t exprSize = exprEnd - exprStart;
416 
417   MOZ_ASSERT(expr->kind_ == InitExprKind::None);
418   expr->type_ = expected;
419 
420   if (literal) {
421     expr->kind_ = InitExprKind::Literal;
422     expr->literal_ = *literal;
423     return true;
424   }
425 
426   expr->kind_ = InitExprKind::Variable;
427   return expr->bytecode_.reserve(exprSize) &&
428          expr->bytecode_.append(exprStart, exprEnd);
429 }
430 
evaluate(JSContext * cx,const ValVector & globalImportValues,HandleWasmInstanceObject instanceObj,MutableHandleVal result) const431 bool InitExpr::evaluate(JSContext* cx, const ValVector& globalImportValues,
432                         HandleWasmInstanceObject instanceObj,
433                         MutableHandleVal result) const {
434   MOZ_ASSERT(kind_ != InitExprKind::None);
435 
436   if (isLiteral()) {
437     result.set(Val(literal()));
438     return true;
439   }
440 
441   UniqueChars error;
442   Decoder d(bytecode_.begin(), bytecode_.end(), 0, &error);
443   InitExprInterpreter interp(cx, globalImportValues, instanceObj);
444   if (!interp.evaluate(d)) {
445     // This expression should have been validated already. So we should only be
446     // able to OOM, which is reported by having no error message.
447     MOZ_RELEASE_ASSERT(!error);
448     return false;
449   }
450 
451   result.set(interp.result());
452   return true;
453 }
454 
clone(const InitExpr & src)455 bool InitExpr::clone(const InitExpr& src) {
456   kind_ = src.kind_;
457   MOZ_ASSERT(bytecode_.empty());
458   if (!bytecode_.appendAll(src.bytecode_)) {
459     return false;
460   }
461   literal_ = src.literal_;
462   type_ = src.type_;
463   return true;
464 }
465 
serializedSize() const466 size_t InitExpr::serializedSize() const {
467   size_t size = sizeof(kind_) + sizeof(type_);
468   switch (kind_) {
469     case InitExprKind::Literal:
470       size += sizeof(literal_);
471       break;
472     case InitExprKind::Variable:
473       size += SerializedPodVectorSize(bytecode_);
474       break;
475     default:
476       MOZ_CRASH();
477   }
478   return size;
479 }
480 
serialize(uint8_t * cursor) const481 uint8_t* InitExpr::serialize(uint8_t* cursor) const {
482   cursor = WriteBytes(cursor, &kind_, sizeof(kind_));
483   cursor = WriteBytes(cursor, &type_, sizeof(type_));
484   switch (kind_) {
485     case InitExprKind::Literal:
486       cursor = WriteBytes(cursor, &literal_, sizeof(literal_));
487       break;
488     case InitExprKind::Variable:
489       cursor = SerializePodVector(cursor, bytecode_);
490       break;
491     default:
492       MOZ_CRASH();
493   }
494   return cursor;
495 }
496 
deserialize(const uint8_t * cursor)497 const uint8_t* InitExpr::deserialize(const uint8_t* cursor) {
498   if (!(cursor = ReadBytes(cursor, &kind_, sizeof(kind_))) ||
499       !(cursor = ReadBytes(cursor, &type_, sizeof(type_)))) {
500     return nullptr;
501   }
502   switch (kind_) {
503     case InitExprKind::Literal:
504       cursor = ReadBytes(cursor, &literal_, sizeof(literal_));
505       break;
506     case InitExprKind::Variable:
507       cursor = DeserializePodVector(cursor, &bytecode_);
508       break;
509     default:
510       MOZ_CRASH();
511   }
512   return cursor;
513 }
514 
sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const515 size_t InitExpr::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
516   return bytecode_.sizeOfExcludingThis(mallocSizeOf);
517 }
518