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 #ifndef wasm_binary_h
20 #define wasm_binary_h
21 
22 #include "mozilla/Maybe.h"
23 
24 #include <type_traits>
25 
26 #include "js/WasmFeatures.h"
27 
28 #include "wasm/WasmCompile.h"
29 #include "wasm/WasmCompileArgs.h"
30 #include "wasm/WasmConstants.h"
31 #include "wasm/WasmTypeDecls.h"
32 #include "wasm/WasmTypeDef.h"
33 #include "wasm/WasmValType.h"
34 
35 namespace js {
36 namespace wasm {
37 
38 using mozilla::Maybe;
39 
40 struct ModuleEnvironment;
41 
42 // The Opcode compactly and safely represents the primary opcode plus any
43 // extension, with convenient predicates and accessors.
44 
45 class Opcode {
46   uint32_t bits_;
47 
48  public:
Opcode(Op op)49   MOZ_IMPLICIT Opcode(Op op) : bits_(uint32_t(op)) {
50     static_assert(size_t(Op::Limit) == 256, "fits");
51     MOZ_ASSERT(size_t(op) < size_t(Op::Limit));
52   }
Opcode(MiscOp op)53   MOZ_IMPLICIT Opcode(MiscOp op)
54       : bits_((uint32_t(op) << 8) | uint32_t(Op::MiscPrefix)) {
55     static_assert(size_t(MiscOp::Limit) <= 0xFFFFFF, "fits");
56     MOZ_ASSERT(size_t(op) < size_t(MiscOp::Limit));
57   }
Opcode(ThreadOp op)58   MOZ_IMPLICIT Opcode(ThreadOp op)
59       : bits_((uint32_t(op) << 8) | uint32_t(Op::ThreadPrefix)) {
60     static_assert(size_t(ThreadOp::Limit) <= 0xFFFFFF, "fits");
61     MOZ_ASSERT(size_t(op) < size_t(ThreadOp::Limit));
62   }
Opcode(MozOp op)63   MOZ_IMPLICIT Opcode(MozOp op)
64       : bits_((uint32_t(op) << 8) | uint32_t(Op::MozPrefix)) {
65     static_assert(size_t(MozOp::Limit) <= 0xFFFFFF, "fits");
66     MOZ_ASSERT(size_t(op) < size_t(MozOp::Limit));
67   }
Opcode(SimdOp op)68   MOZ_IMPLICIT Opcode(SimdOp op)
69       : bits_((uint32_t(op) << 8) | uint32_t(Op::SimdPrefix)) {
70     static_assert(size_t(SimdOp::Limit) <= 0xFFFFFF, "fits");
71     MOZ_ASSERT(size_t(op) < size_t(SimdOp::Limit));
72   }
73 
isOp()74   bool isOp() const { return bits_ < uint32_t(Op::FirstPrefix); }
isMisc()75   bool isMisc() const { return (bits_ & 255) == uint32_t(Op::MiscPrefix); }
isThread()76   bool isThread() const { return (bits_ & 255) == uint32_t(Op::ThreadPrefix); }
isMoz()77   bool isMoz() const { return (bits_ & 255) == uint32_t(Op::MozPrefix); }
isSimd()78   bool isSimd() const { return (bits_ & 255) == uint32_t(Op::SimdPrefix); }
79 
asOp()80   Op asOp() const {
81     MOZ_ASSERT(isOp());
82     return Op(bits_);
83   }
asMisc()84   MiscOp asMisc() const {
85     MOZ_ASSERT(isMisc());
86     return MiscOp(bits_ >> 8);
87   }
asThread()88   ThreadOp asThread() const {
89     MOZ_ASSERT(isThread());
90     return ThreadOp(bits_ >> 8);
91   }
asMoz()92   MozOp asMoz() const {
93     MOZ_ASSERT(isMoz());
94     return MozOp(bits_ >> 8);
95   }
asSimd()96   SimdOp asSimd() const {
97     MOZ_ASSERT(isSimd());
98     return SimdOp(bits_ >> 8);
99   }
100 
bits()101   uint32_t bits() const { return bits_; }
102 
103   bool operator==(const Opcode& that) const { return bits_ == that.bits_; }
104   bool operator!=(const Opcode& that) const { return bits_ != that.bits_; }
105 };
106 
107 // This struct captures the bytecode offset of a section's payload (so not
108 // including the header) and the size of the payload.
109 
110 struct SectionRange {
111   uint32_t start;
112   uint32_t size;
113 
endSectionRange114   uint32_t end() const { return start + size; }
115   bool operator==(const SectionRange& rhs) const {
116     return start == rhs.start && size == rhs.size;
117   }
118 };
119 
120 using MaybeSectionRange = Maybe<SectionRange>;
121 
122 // The Encoder class appends bytes to the Bytes object it is given during
123 // construction. The client is responsible for the Bytes's lifetime and must
124 // keep the Bytes alive as long as the Encoder is used.
125 
126 class Encoder {
127   Bytes& bytes_;
128 
129   template <class T>
write(const T & v)130   [[nodiscard]] bool write(const T& v) {
131     return bytes_.append(reinterpret_cast<const uint8_t*>(&v), sizeof(T));
132   }
133 
134   template <typename UInt>
writeVarU(UInt i)135   [[nodiscard]] bool writeVarU(UInt i) {
136     do {
137       uint8_t byte = i & 0x7f;
138       i >>= 7;
139       if (i != 0) {
140         byte |= 0x80;
141       }
142       if (!bytes_.append(byte)) {
143         return false;
144       }
145     } while (i != 0);
146     return true;
147   }
148 
149   template <typename SInt>
writeVarS(SInt i)150   [[nodiscard]] bool writeVarS(SInt i) {
151     bool done;
152     do {
153       uint8_t byte = i & 0x7f;
154       i >>= 7;
155       done = ((i == 0) && !(byte & 0x40)) || ((i == -1) && (byte & 0x40));
156       if (!done) {
157         byte |= 0x80;
158       }
159       if (!bytes_.append(byte)) {
160         return false;
161       }
162     } while (!done);
163     return true;
164   }
165 
patchVarU32(size_t offset,uint32_t patchBits,uint32_t assertBits)166   void patchVarU32(size_t offset, uint32_t patchBits, uint32_t assertBits) {
167     do {
168       uint8_t assertByte = assertBits & 0x7f;
169       uint8_t patchByte = patchBits & 0x7f;
170       assertBits >>= 7;
171       patchBits >>= 7;
172       if (assertBits != 0) {
173         assertByte |= 0x80;
174         patchByte |= 0x80;
175       }
176       MOZ_ASSERT(assertByte == bytes_[offset]);
177       bytes_[offset] = patchByte;
178       offset++;
179     } while (assertBits != 0);
180   }
181 
patchFixedU7(size_t offset,uint8_t patchBits,uint8_t assertBits)182   void patchFixedU7(size_t offset, uint8_t patchBits, uint8_t assertBits) {
183     MOZ_ASSERT(patchBits <= uint8_t(INT8_MAX));
184     patchFixedU8(offset, patchBits, assertBits);
185   }
186 
patchFixedU8(size_t offset,uint8_t patchBits,uint8_t assertBits)187   void patchFixedU8(size_t offset, uint8_t patchBits, uint8_t assertBits) {
188     MOZ_ASSERT(bytes_[offset] == assertBits);
189     bytes_[offset] = patchBits;
190   }
191 
varU32ByteLength(size_t offset)192   uint32_t varU32ByteLength(size_t offset) const {
193     size_t start = offset;
194     while (bytes_[offset] & 0x80) {
195       offset++;
196     }
197     return offset - start + 1;
198   }
199 
200  public:
Encoder(Bytes & bytes)201   explicit Encoder(Bytes& bytes) : bytes_(bytes) { MOZ_ASSERT(empty()); }
202 
currentOffset()203   size_t currentOffset() const { return bytes_.length(); }
empty()204   bool empty() const { return currentOffset() == 0; }
205 
206   // Fixed-size encoding operations simply copy the literal bytes (without
207   // attempting to align).
208 
writeFixedU7(uint8_t i)209   [[nodiscard]] bool writeFixedU7(uint8_t i) {
210     MOZ_ASSERT(i <= uint8_t(INT8_MAX));
211     return writeFixedU8(i);
212   }
writeFixedU8(uint8_t i)213   [[nodiscard]] bool writeFixedU8(uint8_t i) { return write<uint8_t>(i); }
writeFixedU32(uint32_t i)214   [[nodiscard]] bool writeFixedU32(uint32_t i) { return write<uint32_t>(i); }
writeFixedF32(float f)215   [[nodiscard]] bool writeFixedF32(float f) { return write<float>(f); }
writeFixedF64(double d)216   [[nodiscard]] bool writeFixedF64(double d) { return write<double>(d); }
217 
218   // Variable-length encodings that all use LEB128.
219 
writeVarU32(uint32_t i)220   [[nodiscard]] bool writeVarU32(uint32_t i) { return writeVarU<uint32_t>(i); }
writeVarS32(int32_t i)221   [[nodiscard]] bool writeVarS32(int32_t i) { return writeVarS<int32_t>(i); }
writeVarU64(uint64_t i)222   [[nodiscard]] bool writeVarU64(uint64_t i) { return writeVarU<uint64_t>(i); }
writeVarS64(int64_t i)223   [[nodiscard]] bool writeVarS64(int64_t i) { return writeVarS<int64_t>(i); }
writeValType(ValType type)224   [[nodiscard]] bool writeValType(ValType type) {
225     static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits");
226     if (type.isTypeIndex()) {
227       return writeFixedU8(uint8_t(TypeCode::NullableRef)) &&
228              writeVarU32(type.refType().typeIndex());
229     }
230     TypeCode tc = type.packed().typeCode();
231     MOZ_ASSERT(size_t(tc) < size_t(TypeCode::Limit));
232     return writeFixedU8(uint8_t(tc));
233   }
writeOp(Opcode opcode)234   [[nodiscard]] bool writeOp(Opcode opcode) {
235     // The Opcode constructor has asserted that `opcode` is meaningful, so no
236     // further correctness checking is necessary here.
237     uint32_t bits = opcode.bits();
238     if (!writeFixedU8(bits & 255)) {
239       return false;
240     }
241     if (opcode.isOp()) {
242       return true;
243     }
244     return writeVarU32(bits >> 8);
245   }
246 
247   // Fixed-length encodings that allow back-patching.
248 
writePatchableFixedU7(size_t * offset)249   [[nodiscard]] bool writePatchableFixedU7(size_t* offset) {
250     *offset = bytes_.length();
251     return writeFixedU8(UINT8_MAX);
252   }
patchFixedU7(size_t offset,uint8_t patchBits)253   void patchFixedU7(size_t offset, uint8_t patchBits) {
254     return patchFixedU7(offset, patchBits, UINT8_MAX);
255   }
256 
257   // Variable-length encodings that allow back-patching.
258 
writePatchableVarU32(size_t * offset)259   [[nodiscard]] bool writePatchableVarU32(size_t* offset) {
260     *offset = bytes_.length();
261     return writeVarU32(UINT32_MAX);
262   }
patchVarU32(size_t offset,uint32_t patchBits)263   void patchVarU32(size_t offset, uint32_t patchBits) {
264     return patchVarU32(offset, patchBits, UINT32_MAX);
265   }
266 
267   // Byte ranges start with an LEB128 length followed by an arbitrary sequence
268   // of bytes. When used for strings, bytes are to be interpreted as utf8.
269 
writeBytes(const void * bytes,uint32_t numBytes)270   [[nodiscard]] bool writeBytes(const void* bytes, uint32_t numBytes) {
271     return writeVarU32(numBytes) &&
272            bytes_.append(reinterpret_cast<const uint8_t*>(bytes), numBytes);
273   }
274 
275   // A "section" is a contiguous range of bytes that stores its own size so
276   // that it may be trivially skipped without examining the payload. Sections
277   // require backpatching since the size of the section is only known at the
278   // end while the size's varU32 must be stored at the beginning. Immediately
279   // after the section length is the string id of the section.
280 
startSection(SectionId id,size_t * offset)281   [[nodiscard]] bool startSection(SectionId id, size_t* offset) {
282     MOZ_ASSERT(uint32_t(id) < 128);
283     return writeVarU32(uint32_t(id)) && writePatchableVarU32(offset);
284   }
finishSection(size_t offset)285   void finishSection(size_t offset) {
286     return patchVarU32(offset,
287                        bytes_.length() - offset - varU32ByteLength(offset));
288   }
289 };
290 
291 // The Decoder class decodes the bytes in the range it is given during
292 // construction. The client is responsible for keeping the byte range alive as
293 // long as the Decoder is used.
294 
295 class Decoder {
296   const uint8_t* const beg_;
297   const uint8_t* const end_;
298   const uint8_t* cur_;
299   const size_t offsetInModule_;
300   UniqueChars* error_;
301   UniqueCharsVector* warnings_;
302   bool resilientMode_;
303 
304   template <class T>
read(T * out)305   [[nodiscard]] bool read(T* out) {
306     if (bytesRemain() < sizeof(T)) {
307       return false;
308     }
309     memcpy((void*)out, cur_, sizeof(T));
310     cur_ += sizeof(T);
311     return true;
312   }
313 
314   template <class T>
uncheckedRead()315   T uncheckedRead() {
316     MOZ_ASSERT(bytesRemain() >= sizeof(T));
317     T ret;
318     memcpy(&ret, cur_, sizeof(T));
319     cur_ += sizeof(T);
320     return ret;
321   }
322 
323   template <class T>
uncheckedRead(T * ret)324   void uncheckedRead(T* ret) {
325     MOZ_ASSERT(bytesRemain() >= sizeof(T));
326     memcpy(ret, cur_, sizeof(T));
327     cur_ += sizeof(T);
328   }
329 
330   template <typename UInt>
readVarU(UInt * out)331   [[nodiscard]] bool readVarU(UInt* out) {
332     DebugOnly<const uint8_t*> before = cur_;
333     const unsigned numBits = sizeof(UInt) * CHAR_BIT;
334     const unsigned remainderBits = numBits % 7;
335     const unsigned numBitsInSevens = numBits - remainderBits;
336     UInt u = 0;
337     uint8_t byte;
338     UInt shift = 0;
339     do {
340       if (!readFixedU8(&byte)) {
341         return false;
342       }
343       if (!(byte & 0x80)) {
344         *out = u | UInt(byte) << shift;
345         return true;
346       }
347       u |= UInt(byte & 0x7F) << shift;
348       shift += 7;
349     } while (shift != numBitsInSevens);
350     if (!readFixedU8(&byte) || (byte & (unsigned(-1) << remainderBits))) {
351       return false;
352     }
353     *out = u | (UInt(byte) << numBitsInSevens);
354     MOZ_ASSERT_IF(sizeof(UInt) == 4,
355                   unsigned(cur_ - before) <= MaxVarU32DecodedBytes);
356     return true;
357   }
358 
359   template <typename SInt>
readVarS(SInt * out)360   [[nodiscard]] bool readVarS(SInt* out) {
361     using UInt = std::make_unsigned_t<SInt>;
362     const unsigned numBits = sizeof(SInt) * CHAR_BIT;
363     const unsigned remainderBits = numBits % 7;
364     const unsigned numBitsInSevens = numBits - remainderBits;
365     SInt s = 0;
366     uint8_t byte;
367     unsigned shift = 0;
368     do {
369       if (!readFixedU8(&byte)) {
370         return false;
371       }
372       s |= SInt(byte & 0x7f) << shift;
373       shift += 7;
374       if (!(byte & 0x80)) {
375         if (byte & 0x40) {
376           s |= UInt(-1) << shift;
377         }
378         *out = s;
379         return true;
380       }
381     } while (shift < numBitsInSevens);
382     if (!remainderBits || !readFixedU8(&byte) || (byte & 0x80)) {
383       return false;
384     }
385     uint8_t mask = 0x7f & (uint8_t(-1) << remainderBits);
386     if ((byte & mask) != ((byte & (1 << (remainderBits - 1))) ? mask : 0)) {
387       return false;
388     }
389     *out = s | UInt(byte) << shift;
390     return true;
391   }
392 
393  public:
394   Decoder(const uint8_t* begin, const uint8_t* end, size_t offsetInModule,
395           UniqueChars* error, UniqueCharsVector* warnings = nullptr,
396           bool resilientMode = false)
beg_(begin)397       : beg_(begin),
398         end_(end),
399         cur_(begin),
400         offsetInModule_(offsetInModule),
401         error_(error),
402         warnings_(warnings),
403         resilientMode_(resilientMode) {
404     MOZ_ASSERT(begin <= end);
405   }
406   explicit Decoder(const Bytes& bytes, size_t offsetInModule = 0,
407                    UniqueChars* error = nullptr,
408                    UniqueCharsVector* warnings = nullptr)
409       : beg_(bytes.begin()),
410         end_(bytes.end()),
411         cur_(bytes.begin()),
412         offsetInModule_(offsetInModule),
413         error_(error),
414         warnings_(warnings),
415         resilientMode_(false) {}
416 
417   // These convenience functions use currentOffset() as the errorOffset.
fail(const char * msg)418   bool fail(const char* msg) { return fail(currentOffset(), msg); }
419   bool failf(const char* msg, ...) MOZ_FORMAT_PRINTF(2, 3);
420   void warnf(const char* msg, ...) MOZ_FORMAT_PRINTF(2, 3);
421 
422   // Report an error at the given offset (relative to the whole module).
423   bool fail(size_t errorOffset, const char* msg);
424 
error()425   UniqueChars* error() { return error_; }
426 
clearError()427   void clearError() {
428     if (error_) {
429       error_->reset();
430     }
431   }
432 
done()433   bool done() const {
434     MOZ_ASSERT(cur_ <= end_);
435     return cur_ == end_;
436   }
resilientMode()437   bool resilientMode() const { return resilientMode_; }
438 
bytesRemain()439   size_t bytesRemain() const {
440     MOZ_ASSERT(end_ >= cur_);
441     return size_t(end_ - cur_);
442   }
443   // pos must be a value previously returned from currentPosition.
rollbackPosition(const uint8_t * pos)444   void rollbackPosition(const uint8_t* pos) { cur_ = pos; }
currentPosition()445   const uint8_t* currentPosition() const { return cur_; }
currentOffset()446   size_t currentOffset() const { return offsetInModule_ + (cur_ - beg_); }
begin()447   const uint8_t* begin() const { return beg_; }
end()448   const uint8_t* end() const { return end_; }
449 
450   // Peek at the next byte, if it exists, without advancing the position.
451 
peekByte(uint8_t * byte)452   bool peekByte(uint8_t* byte) {
453     if (done()) {
454       return false;
455     }
456     *byte = *cur_;
457     return true;
458   }
459 
460   // Fixed-size encoding operations simply copy the literal bytes (without
461   // attempting to align).
462 
readFixedU8(uint8_t * i)463   [[nodiscard]] bool readFixedU8(uint8_t* i) { return read<uint8_t>(i); }
readFixedU32(uint32_t * u)464   [[nodiscard]] bool readFixedU32(uint32_t* u) { return read<uint32_t>(u); }
readFixedF32(float * f)465   [[nodiscard]] bool readFixedF32(float* f) { return read<float>(f); }
readFixedF64(double * d)466   [[nodiscard]] bool readFixedF64(double* d) { return read<double>(d); }
467 #ifdef ENABLE_WASM_SIMD
readFixedV128(V128 * d)468   [[nodiscard]] bool readFixedV128(V128* d) {
469     for (unsigned i = 0; i < 16; i++) {
470       if (!read<uint8_t>(d->bytes + i)) {
471         return false;
472       }
473     }
474     return true;
475   }
476 #endif
477 
478   // Variable-length encodings that all use LEB128.
479 
readVarU32(uint32_t * out)480   [[nodiscard]] bool readVarU32(uint32_t* out) {
481     return readVarU<uint32_t>(out);
482   }
readVarS32(int32_t * out)483   [[nodiscard]] bool readVarS32(int32_t* out) { return readVarS<int32_t>(out); }
readVarU64(uint64_t * out)484   [[nodiscard]] bool readVarU64(uint64_t* out) {
485     return readVarU<uint64_t>(out);
486   }
readVarS64(int64_t * out)487   [[nodiscard]] bool readVarS64(int64_t* out) { return readVarS<int64_t>(out); }
488 
489   // Value and reference types
490 
491   [[nodiscard]] ValType uncheckedReadValType();
492   template <class T>
493   [[nodiscard]] bool readPackedType(uint32_t numTypes,
494                                     const FeatureArgs& features, T* type);
495   template <class T>
496   [[nodiscard]] bool readPackedType(const TypeContext& types,
497                                     const FeatureArgs& features, T* type);
498 
499   [[nodiscard]] bool readValType(uint32_t numTypes, const FeatureArgs& features,
500                                  ValType* type);
501   [[nodiscard]] bool readValType(const TypeContext& types,
502                                  const FeatureArgs& features, ValType* type);
503 
504   [[nodiscard]] bool readFieldType(uint32_t numTypes,
505                                    const FeatureArgs& features,
506                                    FieldType* type);
507   [[nodiscard]] bool readFieldType(const TypeContext& types,
508                                    const FeatureArgs& features,
509                                    FieldType* type);
510 
511   [[nodiscard]] bool readHeapType(uint32_t numTypes,
512                                   const FeatureArgs& features, bool nullable,
513                                   RefType* type);
514   [[nodiscard]] bool readHeapType(const TypeContext& types,
515                                   const FeatureArgs& features, bool nullable,
516                                   RefType* type);
517   [[nodiscard]] bool readRefType(uint32_t numTypes, const FeatureArgs& features,
518                                  RefType* type);
519   [[nodiscard]] bool readRefType(const TypeContext& types,
520                                  const FeatureArgs& features, RefType* type);
521   [[nodiscard]] bool validateTypeIndex(const TypeContext& types,
522                                        const FeatureArgs& features,
523                                        RefType type);
524 
525   // Instruction opcode
526 
527   [[nodiscard]] bool readOp(OpBytes* op);
528 
529   // Instruction immediates for constant instructions
530 
readBinary()531   [[nodiscard]] bool readBinary() { return true; }
532   [[nodiscard]] bool readGetGlobal(uint32_t* id);
533   [[nodiscard]] bool readI32Const(int32_t* i32);
534   [[nodiscard]] bool readI64Const(int64_t* i64);
535   [[nodiscard]] bool readF32Const(float* f32);
536   [[nodiscard]] bool readF64Const(double* f64);
537 #ifdef ENABLE_WASM_SIMD
538   [[nodiscard]] bool readV128Const(V128* value);
539 #endif
540   [[nodiscard]] bool readRefFunc(uint32_t* funcIndex);
541   [[nodiscard]] bool readRefNull(const TypeContext& types,
542                                  const FeatureArgs& features, RefType* type);
543   [[nodiscard]] bool readRefNull(const FeatureArgs& features, RefType* type);
544 
545   // See writeBytes comment.
546 
547   [[nodiscard]] bool readBytes(uint32_t numBytes,
548                                const uint8_t** bytes = nullptr) {
549     if (bytes) {
550       *bytes = cur_;
551     }
552     if (bytesRemain() < numBytes) {
553       return false;
554     }
555     cur_ += numBytes;
556     return true;
557   }
558 
559   // See "section" description in Encoder.
560 
561   [[nodiscard]] bool readSectionHeader(uint8_t* id, SectionRange* range);
562 
563   [[nodiscard]] bool startSection(SectionId id, ModuleEnvironment* env,
564                                   MaybeSectionRange* range,
565                                   const char* sectionName);
566   [[nodiscard]] bool finishSection(const SectionRange& range,
567                                    const char* sectionName);
568 
569   // Custom sections do not cause validation errors unless the error is in
570   // the section header itself.
571 
572   [[nodiscard]] bool startCustomSection(const char* expected,
573                                         size_t expectedLength,
574                                         ModuleEnvironment* env,
575                                         MaybeSectionRange* range);
576 
577   template <size_t NameSizeWith0>
startCustomSection(const char (& name)[NameSizeWith0],ModuleEnvironment * env,MaybeSectionRange * range)578   [[nodiscard]] bool startCustomSection(const char (&name)[NameSizeWith0],
579                                         ModuleEnvironment* env,
580                                         MaybeSectionRange* range) {
581     MOZ_ASSERT(name[NameSizeWith0 - 1] == '\0');
582     return startCustomSection(name, NameSizeWith0 - 1, env, range);
583   }
584 
585   void finishCustomSection(const char* name, const SectionRange& range);
586   void skipAndFinishCustomSection(const SectionRange& range);
587 
588   [[nodiscard]] bool skipCustomSection(ModuleEnvironment* env);
589 
590   // The Name section has its own optional subsections.
591 
592   [[nodiscard]] bool startNameSubsection(NameType nameType,
593                                          Maybe<uint32_t>* endOffset);
594   [[nodiscard]] bool finishNameSubsection(uint32_t endOffset);
595   [[nodiscard]] bool skipNameSubsection();
596 
597   // The infallible "unchecked" decoding functions can be used when we are
598   // sure that the bytes are well-formed (by construction or due to previous
599   // validation).
600 
uncheckedReadFixedU8()601   uint8_t uncheckedReadFixedU8() { return uncheckedRead<uint8_t>(); }
uncheckedReadFixedU32()602   uint32_t uncheckedReadFixedU32() { return uncheckedRead<uint32_t>(); }
uncheckedReadFixedF32(float * out)603   void uncheckedReadFixedF32(float* out) { uncheckedRead<float>(out); }
uncheckedReadFixedF64(double * out)604   void uncheckedReadFixedF64(double* out) { uncheckedRead<double>(out); }
605   template <typename UInt>
uncheckedReadVarU()606   UInt uncheckedReadVarU() {
607     static const unsigned numBits = sizeof(UInt) * CHAR_BIT;
608     static const unsigned remainderBits = numBits % 7;
609     static const unsigned numBitsInSevens = numBits - remainderBits;
610     UInt decoded = 0;
611     uint32_t shift = 0;
612     do {
613       uint8_t byte = *cur_++;
614       if (!(byte & 0x80)) {
615         return decoded | (UInt(byte) << shift);
616       }
617       decoded |= UInt(byte & 0x7f) << shift;
618       shift += 7;
619     } while (shift != numBitsInSevens);
620     uint8_t byte = *cur_++;
621     MOZ_ASSERT(!(byte & 0xf0));
622     return decoded | (UInt(byte) << numBitsInSevens);
623   }
uncheckedReadVarU32()624   uint32_t uncheckedReadVarU32() { return uncheckedReadVarU<uint32_t>(); }
uncheckedReadVarS32()625   int32_t uncheckedReadVarS32() {
626     int32_t i32 = 0;
627     MOZ_ALWAYS_TRUE(readVarS32(&i32));
628     return i32;
629   }
uncheckedReadVarU64()630   uint64_t uncheckedReadVarU64() { return uncheckedReadVarU<uint64_t>(); }
uncheckedReadVarS64()631   int64_t uncheckedReadVarS64() {
632     int64_t i64 = 0;
633     MOZ_ALWAYS_TRUE(readVarS64(&i64));
634     return i64;
635   }
uncheckedReadOp()636   Op uncheckedReadOp() {
637     static_assert(size_t(Op::Limit) == 256, "fits");
638     uint8_t u8 = uncheckedReadFixedU8();
639     return u8 != UINT8_MAX ? Op(u8) : Op(uncheckedReadFixedU8() + UINT8_MAX);
640   }
641 };
642 
643 // Value and reference types
644 
uncheckedReadValType()645 inline ValType Decoder::uncheckedReadValType() {
646   uint8_t code = uncheckedReadFixedU8();
647   switch (code) {
648     case uint8_t(TypeCode::FuncRef):
649     case uint8_t(TypeCode::ExternRef):
650       return RefType::fromTypeCode(TypeCode(code), true);
651     case uint8_t(TypeCode::Rtt): {
652       uint32_t rttDepth = uncheckedReadVarU32();
653       int32_t typeIndex = uncheckedReadVarS32();
654       return ValType::fromRtt(typeIndex, rttDepth);
655     }
656     case uint8_t(TypeCode::Ref):
657     case uint8_t(TypeCode::NullableRef): {
658       bool nullable = code == uint8_t(TypeCode::NullableRef);
659 
660       uint8_t nextByte;
661       peekByte(&nextByte);
662 
663       if ((nextByte & SLEB128SignMask) == SLEB128SignBit) {
664         uint8_t code = uncheckedReadFixedU8();
665         return RefType::fromTypeCode(TypeCode(code), nullable);
666       }
667 
668       int32_t x = uncheckedReadVarS32();
669       return RefType::fromTypeIndex(x, nullable);
670     }
671     default:
672       return ValType::fromNonRefTypeCode(TypeCode(code));
673   }
674 }
675 
676 template <class T>
readPackedType(uint32_t numTypes,const FeatureArgs & features,T * type)677 inline bool Decoder::readPackedType(uint32_t numTypes,
678                                     const FeatureArgs& features, T* type) {
679   static_assert(uint8_t(TypeCode::Limit) <= UINT8_MAX, "fits");
680   uint8_t code;
681   if (!readFixedU8(&code)) {
682     return fail("expected type code");
683   }
684   switch (code) {
685     case uint8_t(TypeCode::V128): {
686 #ifdef ENABLE_WASM_SIMD
687       if (!features.v128) {
688         return fail("v128 not enabled");
689       }
690       *type = T::fromNonRefTypeCode(TypeCode(code));
691       return true;
692 #else
693       break;
694 #endif
695     }
696     case uint8_t(TypeCode::FuncRef):
697     case uint8_t(TypeCode::ExternRef): {
698       *type = RefType::fromTypeCode(TypeCode(code), true);
699       return true;
700     }
701     case uint8_t(TypeCode::Ref):
702     case uint8_t(TypeCode::NullableRef): {
703 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
704       if (!features.functionReferences) {
705         return fail("(ref T) types not enabled");
706       }
707       bool nullable = code == uint8_t(TypeCode::NullableRef);
708       RefType refType;
709       if (!readHeapType(numTypes, features, nullable, &refType)) {
710         return false;
711       }
712       *type = refType;
713       return true;
714 #else
715       break;
716 #endif
717     }
718     case uint8_t(TypeCode::Rtt): {
719 #ifdef ENABLE_WASM_GC
720       if (!features.gc) {
721         return fail("gc types not enabled");
722       }
723 
724       uint32_t rttDepth;
725       if (!readVarU32(&rttDepth) || uint32_t(rttDepth) >= MaxRttDepth) {
726         return fail("invalid rtt depth");
727       }
728 
729       RefType heapType;
730       if (!readHeapType(numTypes, features, true, &heapType)) {
731         return false;
732       }
733 
734       if (!heapType.isTypeIndex()) {
735         return fail("invalid heap type for rtt");
736       }
737 
738       *type = T::fromRtt(heapType.typeIndex(), rttDepth);
739       return true;
740 #else
741       break;
742 #endif
743     }
744     case uint8_t(TypeCode::EqRef): {
745 #ifdef ENABLE_WASM_GC
746       if (!features.gc) {
747         return fail("gc types not enabled");
748       }
749       *type = RefType::fromTypeCode(TypeCode(code), true);
750       return true;
751 #else
752       break;
753 #endif
754     }
755     default: {
756       if (!T::isValidTypeCode(TypeCode(code))) {
757         break;
758       }
759       *type = T::fromNonRefTypeCode(TypeCode(code));
760       return true;
761     }
762   }
763   return fail("bad type");
764 }
765 template <class T>
readPackedType(const TypeContext & types,const FeatureArgs & features,T * type)766 inline bool Decoder::readPackedType(const TypeContext& types,
767                                     const FeatureArgs& features, T* type) {
768   if (!readPackedType(types.length(), features, type)) {
769     return false;
770   }
771   if (type->isTypeIndex() &&
772       !validateTypeIndex(types, features, type->refType())) {
773     return false;
774   }
775   return true;
776 }
777 
readValType(uint32_t numTypes,const FeatureArgs & features,ValType * type)778 inline bool Decoder::readValType(uint32_t numTypes, const FeatureArgs& features,
779                                  ValType* type) {
780   return readPackedType<ValType>(numTypes, features, type);
781 }
readValType(const TypeContext & types,const FeatureArgs & features,ValType * type)782 inline bool Decoder::readValType(const TypeContext& types,
783                                  const FeatureArgs& features, ValType* type) {
784   return readPackedType<ValType>(types, features, type);
785 }
786 
readFieldType(uint32_t numTypes,const FeatureArgs & features,FieldType * type)787 inline bool Decoder::readFieldType(uint32_t numTypes,
788                                    const FeatureArgs& features,
789                                    FieldType* type) {
790   return readPackedType<FieldType>(numTypes, features, type);
791 }
readFieldType(const TypeContext & types,const FeatureArgs & features,FieldType * type)792 inline bool Decoder::readFieldType(const TypeContext& types,
793                                    const FeatureArgs& features,
794                                    FieldType* type) {
795   return readPackedType<FieldType>(types, features, type);
796 }
797 
readHeapType(uint32_t numTypes,const FeatureArgs & features,bool nullable,RefType * type)798 inline bool Decoder::readHeapType(uint32_t numTypes,
799                                   const FeatureArgs& features, bool nullable,
800                                   RefType* type) {
801   uint8_t nextByte;
802   if (!peekByte(&nextByte)) {
803     return fail("expected heap type code");
804   }
805 
806   if ((nextByte & SLEB128SignMask) == SLEB128SignBit) {
807     uint8_t code;
808     if (!readFixedU8(&code)) {
809       return false;
810     }
811 
812     switch (code) {
813       case uint8_t(TypeCode::FuncRef):
814       case uint8_t(TypeCode::ExternRef):
815         *type = RefType::fromTypeCode(TypeCode(code), nullable);
816         return true;
817 #ifdef ENABLE_WASM_GC
818       case uint8_t(TypeCode::EqRef):
819         if (!features.gc) {
820           return fail("gc types not enabled");
821         }
822         *type = RefType::fromTypeCode(TypeCode(code), nullable);
823         return true;
824 #endif
825       default:
826         return fail("invalid heap type");
827     }
828   }
829 
830 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
831   if (features.functionReferences) {
832     int32_t x;
833     if (!readVarS32(&x) || x < 0 || uint32_t(x) >= numTypes ||
834         uint32_t(x) >= MaxTypeIndex) {
835       return fail("invalid heap type index");
836     }
837     *type = RefType::fromTypeIndex(x, nullable);
838     return true;
839   }
840 #endif
841   return fail("invalid heap type");
842 }
readHeapType(const TypeContext & types,const FeatureArgs & features,bool nullable,RefType * type)843 inline bool Decoder::readHeapType(const TypeContext& types,
844                                   const FeatureArgs& features, bool nullable,
845                                   RefType* type) {
846   if (!readHeapType(types.length(), features, nullable, type)) {
847     return false;
848   }
849 
850   if (type->isTypeIndex() && !validateTypeIndex(types, features, *type)) {
851     return false;
852   }
853   return true;
854 }
readRefType(uint32_t numTypes,const FeatureArgs & features,RefType * type)855 inline bool Decoder::readRefType(uint32_t numTypes, const FeatureArgs& features,
856                                  RefType* type) {
857   ValType valType;
858   if (!readValType(numTypes, features, &valType)) {
859     return false;
860   }
861   if (!valType.isReference()) {
862     return fail("bad type");
863   }
864   *type = valType.refType();
865   return true;
866 }
readRefType(const TypeContext & types,const FeatureArgs & features,RefType * type)867 inline bool Decoder::readRefType(const TypeContext& types,
868                                  const FeatureArgs& features, RefType* type) {
869   ValType valType;
870   if (!readValType(types, features, &valType)) {
871     return false;
872   }
873   if (!valType.isReference()) {
874     return fail("bad type");
875   }
876   *type = valType.refType();
877   return true;
878 }
validateTypeIndex(const TypeContext & types,const FeatureArgs & features,RefType type)879 inline bool Decoder::validateTypeIndex(const TypeContext& types,
880                                        const FeatureArgs& features,
881                                        RefType type) {
882   MOZ_ASSERT(type.isTypeIndex());
883 
884   if (features.gc && (types[type.typeIndex()].isStructType() ||
885                       types[type.typeIndex()].isArrayType())) {
886     return true;
887   }
888   return fail("type index references an invalid type");
889 }
890 
891 // Instruction opcode
892 
readOp(OpBytes * op)893 inline bool Decoder::readOp(OpBytes* op) {
894   static_assert(size_t(Op::Limit) == 256, "fits");
895   uint8_t u8;
896   if (!readFixedU8(&u8)) {
897     return false;
898   }
899   op->b0 = u8;
900   if (MOZ_LIKELY(!IsPrefixByte(u8))) {
901     return true;
902   }
903   return readVarU32(&op->b1);
904 }
905 
906 // Instruction immediates for constant instructions
907 
readGetGlobal(uint32_t * id)908 inline bool Decoder::readGetGlobal(uint32_t* id) {
909   if (!readVarU32(id)) {
910     return fail("unable to read global index");
911   }
912   return true;
913 }
914 
readI32Const(int32_t * i32)915 inline bool Decoder::readI32Const(int32_t* i32) {
916   if (!readVarS32(i32)) {
917     return fail("failed to read I32 constant");
918   }
919   return true;
920 }
921 
readI64Const(int64_t * i64)922 inline bool Decoder::readI64Const(int64_t* i64) {
923   if (!readVarS64(i64)) {
924     return fail("failed to read I64 constant");
925   }
926   return true;
927 }
928 
readF32Const(float * f32)929 inline bool Decoder::readF32Const(float* f32) {
930   if (!readFixedF32(f32)) {
931     return fail("failed to read F32 constant");
932   }
933   return true;
934 }
935 
readF64Const(double * f64)936 inline bool Decoder::readF64Const(double* f64) {
937   if (!readFixedF64(f64)) {
938     return fail("failed to read F64 constant");
939   }
940   return true;
941 }
942 
943 #ifdef ENABLE_WASM_SIMD
readV128Const(V128 * value)944 inline bool Decoder::readV128Const(V128* value) {
945   if (!readFixedV128(value)) {
946     return fail("unable to read V128 constant");
947   }
948   return true;
949 }
950 #endif
951 
readRefFunc(uint32_t * funcIndex)952 inline bool Decoder::readRefFunc(uint32_t* funcIndex) {
953   if (!readVarU32(funcIndex)) {
954     return fail("unable to read function index");
955   }
956   return true;
957 }
958 
readRefNull(const TypeContext & types,const FeatureArgs & features,RefType * type)959 inline bool Decoder::readRefNull(const TypeContext& types,
960                                  const FeatureArgs& features, RefType* type) {
961   return readHeapType(types, features, true, type);
962 }
963 
readRefNull(const FeatureArgs & features,RefType * type)964 inline bool Decoder::readRefNull(const FeatureArgs& features, RefType* type) {
965   return readHeapType(MaxTypes, features, true, type);
966 }
967 
968 }  // namespace wasm
969 }  // namespace js
970 
971 #endif  // namespace wasm_binary_h
972