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