1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
3  *
4  * Copyright 2016 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_validate_h
20 #define wasm_validate_h
21 
22 #include "mozilla/TypeTraits.h"
23 
24 #include "wasm/WasmCode.h"
25 #include "wasm/WasmTypes.h"
26 
27 namespace js {
28 namespace wasm {
29 
30 // This struct captures the bytecode offset of a section's payload (so not
31 // including the header) and the size of the payload.
32 
33 struct SectionRange {
34   uint32_t start;
35   uint32_t size;
36 
endSectionRange37   uint32_t end() const { return start + size; }
38   bool operator==(const SectionRange& rhs) const {
39     return start == rhs.start && size == rhs.size;
40   }
41 };
42 
43 typedef Maybe<SectionRange> MaybeSectionRange;
44 
45 // ModuleEnvironment contains all the state necessary to validate, process or
46 // render functions. It is created by decoding all the sections before the wasm
47 // code section and then used immutably during. When compiling a module using a
48 // ModuleGenerator, the ModuleEnvironment holds state shared between the
49 // ModuleGenerator thread and background compile threads. All the threads
50 // are given a read-only view of the ModuleEnvironment, thus preventing race
51 // conditions.
52 
53 struct ModuleEnvironment {
54   // Constant parameters for the entire compilation:
55   const DebugEnabled debug;
56   const ModuleKind kind;
57   const CompileMode mode;
58   const Shareable sharedMemoryEnabled;
59   const Tier tier;
60 
61   // Module fields decoded from the module environment (or initialized while
62   // validating an asm.js module) and immutable during compilation:
63   MemoryUsage memoryUsage;
64   uint32_t minMemoryLength;
65   Maybe<uint32_t> maxMemoryLength;
66   SigWithIdVector sigs;
67   SigWithIdPtrVector funcSigs;
68   Uint32Vector funcImportGlobalDataOffsets;
69   GlobalDescVector globals;
70   TableDescVector tables;
71   Uint32Vector asmJSSigToTableIndex;
72   ImportVector imports;
73   ExportVector exports;
74   Maybe<uint32_t> startFuncIndex;
75   MaybeSectionRange codeSection;
76 
77   // Fields decoded as part of the wasm module tail:
78   ElemSegmentVector elemSegments;
79   DataSegmentVector dataSegments;
80   NameInBytecodeVector funcNames;
81   CustomSectionVector customSections;
82 
83   explicit ModuleEnvironment(CompileMode mode = CompileMode::Once,
84                              Tier tier = Tier::Ion,
85                              DebugEnabled debug = DebugEnabled::False,
86                              Shareable sharedMemoryEnabled = Shareable::False,
87                              ModuleKind kind = ModuleKind::Wasm)
debugModuleEnvironment88       : debug(debug),
89         kind(kind),
90         mode(mode),
91         sharedMemoryEnabled(sharedMemoryEnabled),
92         tier(tier),
93         memoryUsage(MemoryUsage::None),
94         minMemoryLength(0) {}
95 
numTablesModuleEnvironment96   size_t numTables() const { return tables.length(); }
numSigsModuleEnvironment97   size_t numSigs() const { return sigs.length(); }
numFuncsModuleEnvironment98   size_t numFuncs() const { return funcSigs.length(); }
numFuncImportsModuleEnvironment99   size_t numFuncImports() const { return funcImportGlobalDataOffsets.length(); }
numFuncDefsModuleEnvironment100   size_t numFuncDefs() const {
101     return funcSigs.length() - funcImportGlobalDataOffsets.length();
102   }
usesMemoryModuleEnvironment103   bool usesMemory() const { return memoryUsage != MemoryUsage::None; }
usesSharedMemoryModuleEnvironment104   bool usesSharedMemory() const { return memoryUsage == MemoryUsage::Shared; }
isAsmJSModuleEnvironment105   bool isAsmJS() const { return kind == ModuleKind::AsmJS; }
debugEnabledModuleEnvironment106   bool debugEnabled() const { return debug == DebugEnabled::True; }
funcIsImportModuleEnvironment107   bool funcIsImport(uint32_t funcIndex) const {
108     return funcIndex < funcImportGlobalDataOffsets.length();
109   }
funcIndexToSigIndexModuleEnvironment110   uint32_t funcIndexToSigIndex(uint32_t funcIndex) const {
111     return funcSigs[funcIndex] - sigs.begin();
112   }
113 };
114 
115 // The Encoder class appends bytes to the Bytes object it is given during
116 // construction. The client is responsible for the Bytes's lifetime and must
117 // keep the Bytes alive as long as the Encoder is used.
118 
119 class Encoder {
120   Bytes& bytes_;
121 
122   template <class T>
write(const T & v)123   MOZ_MUST_USE bool write(const T& v) {
124     return bytes_.append(reinterpret_cast<const uint8_t*>(&v), sizeof(T));
125   }
126 
127   template <typename UInt>
writeVarU(UInt i)128   MOZ_MUST_USE bool writeVarU(UInt i) {
129     do {
130       uint8_t byte = i & 0x7f;
131       i >>= 7;
132       if (i != 0) byte |= 0x80;
133       if (!bytes_.append(byte)) return false;
134     } while (i != 0);
135     return true;
136   }
137 
138   template <typename SInt>
writeVarS(SInt i)139   MOZ_MUST_USE bool writeVarS(SInt i) {
140     bool done;
141     do {
142       uint8_t byte = i & 0x7f;
143       i >>= 7;
144       done = ((i == 0) && !(byte & 0x40)) || ((i == -1) && (byte & 0x40));
145       if (!done) byte |= 0x80;
146       if (!bytes_.append(byte)) return false;
147     } while (!done);
148     return true;
149   }
150 
patchVarU32(size_t offset,uint32_t patchBits,uint32_t assertBits)151   void patchVarU32(size_t offset, uint32_t patchBits, uint32_t assertBits) {
152     do {
153       uint8_t assertByte = assertBits & 0x7f;
154       uint8_t patchByte = patchBits & 0x7f;
155       assertBits >>= 7;
156       patchBits >>= 7;
157       if (assertBits != 0) {
158         assertByte |= 0x80;
159         patchByte |= 0x80;
160       }
161       MOZ_ASSERT(assertByte == bytes_[offset]);
162       bytes_[offset] = patchByte;
163       offset++;
164     } while (assertBits != 0);
165   }
166 
patchFixedU7(size_t offset,uint8_t patchBits,uint8_t assertBits)167   void patchFixedU7(size_t offset, uint8_t patchBits, uint8_t assertBits) {
168     MOZ_ASSERT(patchBits <= uint8_t(INT8_MAX));
169     patchFixedU8(offset, patchBits, assertBits);
170   }
171 
patchFixedU8(size_t offset,uint8_t patchBits,uint8_t assertBits)172   void patchFixedU8(size_t offset, uint8_t patchBits, uint8_t assertBits) {
173     MOZ_ASSERT(bytes_[offset] == assertBits);
174     bytes_[offset] = patchBits;
175   }
176 
varU32ByteLength(size_t offset)177   uint32_t varU32ByteLength(size_t offset) const {
178     size_t start = offset;
179     while (bytes_[offset] & 0x80) offset++;
180     return offset - start + 1;
181   }
182 
183  public:
Encoder(Bytes & bytes)184   explicit Encoder(Bytes& bytes) : bytes_(bytes) { MOZ_ASSERT(empty()); }
185 
currentOffset()186   size_t currentOffset() const { return bytes_.length(); }
empty()187   bool empty() const { return currentOffset() == 0; }
188 
189   // Fixed-size encoding operations simply copy the literal bytes (without
190   // attempting to align).
191 
writeFixedU7(uint8_t i)192   MOZ_MUST_USE bool writeFixedU7(uint8_t i) {
193     MOZ_ASSERT(i <= uint8_t(INT8_MAX));
194     return writeFixedU8(i);
195   }
writeFixedU8(uint8_t i)196   MOZ_MUST_USE bool writeFixedU8(uint8_t i) { return write<uint8_t>(i); }
writeFixedU32(uint32_t i)197   MOZ_MUST_USE bool writeFixedU32(uint32_t i) { return write<uint32_t>(i); }
writeFixedF32(float f)198   MOZ_MUST_USE bool writeFixedF32(float f) { return write<float>(f); }
writeFixedF64(double d)199   MOZ_MUST_USE bool writeFixedF64(double d) { return write<double>(d); }
writeFixedI8x16(const I8x16 & i8x16)200   MOZ_MUST_USE bool writeFixedI8x16(const I8x16& i8x16) {
201     return write<I8x16>(i8x16);
202   }
writeFixedI16x8(const I16x8 & i16x8)203   MOZ_MUST_USE bool writeFixedI16x8(const I16x8& i16x8) {
204     return write<I16x8>(i16x8);
205   }
writeFixedI32x4(const I32x4 & i32x4)206   MOZ_MUST_USE bool writeFixedI32x4(const I32x4& i32x4) {
207     return write<I32x4>(i32x4);
208   }
writeFixedF32x4(const F32x4 & f32x4)209   MOZ_MUST_USE bool writeFixedF32x4(const F32x4& f32x4) {
210     return write<F32x4>(f32x4);
211   }
212 
213   // Variable-length encodings that all use LEB128.
214 
writeVarU32(uint32_t i)215   MOZ_MUST_USE bool writeVarU32(uint32_t i) { return writeVarU<uint32_t>(i); }
writeVarS32(int32_t i)216   MOZ_MUST_USE bool writeVarS32(int32_t i) { return writeVarS<int32_t>(i); }
writeVarU64(uint64_t i)217   MOZ_MUST_USE bool writeVarU64(uint64_t i) { return writeVarU<uint64_t>(i); }
writeVarS64(int64_t i)218   MOZ_MUST_USE bool writeVarS64(int64_t i) { return writeVarS<int64_t>(i); }
writeValType(ValType type)219   MOZ_MUST_USE bool writeValType(ValType type) {
220     static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits");
221     MOZ_ASSERT(size_t(type) < size_t(TypeCode::Limit));
222     return writeFixedU8(uint8_t(type));
223   }
writeBlockType(ExprType type)224   MOZ_MUST_USE bool writeBlockType(ExprType type) {
225     static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits");
226     MOZ_ASSERT(size_t(type) < size_t(TypeCode::Limit));
227     return writeFixedU8(uint8_t(type));
228   }
writeOp(Op op)229   MOZ_MUST_USE bool writeOp(Op op) {
230     static_assert(size_t(Op::Limit) == 256, "fits");
231     MOZ_ASSERT(size_t(op) < size_t(Op::Limit));
232     return writeFixedU8(uint8_t(op));
233   }
writeOp(MozOp op)234   MOZ_MUST_USE bool writeOp(MozOp op) {
235     static_assert(size_t(MozOp::Limit) <= 256, "fits");
236     MOZ_ASSERT(size_t(op) < size_t(MozOp::Limit));
237     return writeFixedU8(uint8_t(Op::MozPrefix)) && writeFixedU8(uint8_t(op));
238   }
writeOp(NumericOp op)239   MOZ_MUST_USE bool writeOp(NumericOp op) {
240     static_assert(size_t(NumericOp::Limit) <= 256, "fits");
241     MOZ_ASSERT(size_t(op) < size_t(NumericOp::Limit));
242     return writeFixedU8(uint8_t(Op::NumericPrefix)) &&
243            writeFixedU8(uint8_t(op));
244   }
writeOp(ThreadOp op)245   MOZ_MUST_USE bool writeOp(ThreadOp op) {
246     static_assert(size_t(ThreadOp::Limit) <= 256, "fits");
247     MOZ_ASSERT(size_t(op) < size_t(ThreadOp::Limit));
248     return writeFixedU8(uint8_t(Op::ThreadPrefix)) && writeFixedU8(uint8_t(op));
249   }
250 
251   // Fixed-length encodings that allow back-patching.
252 
writePatchableFixedU7(size_t * offset)253   MOZ_MUST_USE bool writePatchableFixedU7(size_t* offset) {
254     *offset = bytes_.length();
255     return writeFixedU8(UINT8_MAX);
256   }
patchFixedU7(size_t offset,uint8_t patchBits)257   void patchFixedU7(size_t offset, uint8_t patchBits) {
258     return patchFixedU7(offset, patchBits, UINT8_MAX);
259   }
260 
261   // Variable-length encodings that allow back-patching.
262 
writePatchableVarU32(size_t * offset)263   MOZ_MUST_USE bool writePatchableVarU32(size_t* offset) {
264     *offset = bytes_.length();
265     return writeVarU32(UINT32_MAX);
266   }
patchVarU32(size_t offset,uint32_t patchBits)267   void patchVarU32(size_t offset, uint32_t patchBits) {
268     return patchVarU32(offset, patchBits, UINT32_MAX);
269   }
270 
271   // Byte ranges start with an LEB128 length followed by an arbitrary sequence
272   // of bytes. When used for strings, bytes are to be interpreted as utf8.
273 
writeBytes(const void * bytes,uint32_t numBytes)274   MOZ_MUST_USE bool writeBytes(const void* bytes, uint32_t numBytes) {
275     return writeVarU32(numBytes) &&
276            bytes_.append(reinterpret_cast<const uint8_t*>(bytes), numBytes);
277   }
278 
279   // A "section" is a contiguous range of bytes that stores its own size so
280   // that it may be trivially skipped without examining the contents. Sections
281   // require backpatching since the size of the section is only known at the
282   // end while the size's varU32 must be stored at the beginning. Immediately
283   // after the section length is the string id of the section.
284 
startSection(SectionId id,size_t * offset)285   MOZ_MUST_USE bool startSection(SectionId id, size_t* offset) {
286     return writeVarU32(uint32_t(id)) && writePatchableVarU32(offset);
287   }
finishSection(size_t offset)288   void finishSection(size_t offset) {
289     return patchVarU32(offset,
290                        bytes_.length() - offset - varU32ByteLength(offset));
291   }
292 };
293 
294 // The Decoder class decodes the bytes in the range it is given during
295 // construction. The client is responsible for keeping the byte range alive as
296 // long as the Decoder is used.
297 
298 class Decoder {
299   const uint8_t* const beg_;
300   const uint8_t* const end_;
301   const uint8_t* cur_;
302   const size_t offsetInModule_;
303   UniqueChars* error_;
304   bool resilientMode_;
305 
306   template <class T>
read(T * out)307   MOZ_MUST_USE bool read(T* out) {
308     if (bytesRemain() < sizeof(T)) return false;
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   MOZ_MUST_USE 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)) return false;
341       if (!(byte & 0x80)) {
342         *out = u | UInt(byte) << shift;
343         return true;
344       }
345       u |= UInt(byte & 0x7F) << shift;
346       shift += 7;
347     } while (shift != numBitsInSevens);
348     if (!readFixedU8(&byte) || (byte & (unsigned(-1) << remainderBits)))
349       return false;
350     *out = u | (UInt(byte) << numBitsInSevens);
351     MOZ_ASSERT_IF(sizeof(UInt) == 4,
352                   unsigned(cur_ - before) <= MaxVarU32DecodedBytes);
353     return true;
354   }
355 
356   template <typename SInt>
readVarS(SInt * out)357   MOZ_MUST_USE bool readVarS(SInt* out) {
358     using UInt = typename mozilla::MakeUnsigned<SInt>::Type;
359     const unsigned numBits = sizeof(SInt) * CHAR_BIT;
360     const unsigned remainderBits = numBits % 7;
361     const unsigned numBitsInSevens = numBits - remainderBits;
362     SInt s = 0;
363     uint8_t byte;
364     unsigned shift = 0;
365     do {
366       if (!readFixedU8(&byte)) return false;
367       s |= SInt(byte & 0x7f) << shift;
368       shift += 7;
369       if (!(byte & 0x80)) {
370         if (byte & 0x40) s |= UInt(-1) << shift;
371         *out = s;
372         return true;
373       }
374     } while (shift < numBitsInSevens);
375     if (!remainderBits || !readFixedU8(&byte) || (byte & 0x80)) return false;
376     uint8_t mask = 0x7f & (uint8_t(-1) << remainderBits);
377     if ((byte & mask) != ((byte & (1 << (remainderBits - 1))) ? mask : 0))
378       return false;
379     *out = s | UInt(byte) << shift;
380     return true;
381   }
382 
383  public:
384   Decoder(const uint8_t* begin, const uint8_t* end, size_t offsetInModule,
385           UniqueChars* error, bool resilientMode = false)
beg_(begin)386       : beg_(begin),
387         end_(end),
388         cur_(begin),
389         offsetInModule_(offsetInModule),
390         error_(error),
391         resilientMode_(resilientMode) {
392     MOZ_ASSERT(begin <= end);
393   }
394   explicit Decoder(const Bytes& bytes, size_t offsetInModule = 0,
395                    UniqueChars* error = nullptr)
396       : beg_(bytes.begin()),
397         end_(bytes.end()),
398         cur_(bytes.begin()),
399         offsetInModule_(offsetInModule),
400         error_(error),
401         resilientMode_(false) {}
402 
403   // These convenience functions use currentOffset() as the errorOffset.
fail(const char * msg)404   bool fail(const char* msg) { return fail(currentOffset(), msg); }
405   bool failf(const char* msg, ...) MOZ_FORMAT_PRINTF(2, 3);
406 
407   // Report an error at the given offset (relative to the whole module).
408   bool fail(size_t errorOffset, const char* msg);
409 
clearError()410   void clearError() {
411     if (error_) error_->reset();
412   }
413 
done()414   bool done() const {
415     MOZ_ASSERT(cur_ <= end_);
416     return cur_ == end_;
417   }
resilientMode()418   bool resilientMode() const { return resilientMode_; }
419 
bytesRemain()420   size_t bytesRemain() const {
421     MOZ_ASSERT(end_ >= cur_);
422     return size_t(end_ - cur_);
423   }
424   // pos must be a value previously returned from currentPosition.
rollbackPosition(const uint8_t * pos)425   void rollbackPosition(const uint8_t* pos) { cur_ = pos; }
currentPosition()426   const uint8_t* currentPosition() const { return cur_; }
currentOffset()427   size_t currentOffset() const { return offsetInModule_ + (cur_ - beg_); }
begin()428   const uint8_t* begin() const { return beg_; }
end()429   const uint8_t* end() const { return end_; }
430 
431   // Fixed-size encoding operations simply copy the literal bytes (without
432   // attempting to align).
433 
readFixedU8(uint8_t * i)434   MOZ_MUST_USE bool readFixedU8(uint8_t* i) { return read<uint8_t>(i); }
readFixedU32(uint32_t * u)435   MOZ_MUST_USE bool readFixedU32(uint32_t* u) { return read<uint32_t>(u); }
readFixedF32(float * f)436   MOZ_MUST_USE bool readFixedF32(float* f) { return read<float>(f); }
readFixedF64(double * d)437   MOZ_MUST_USE bool readFixedF64(double* d) { return read<double>(d); }
readFixedI8x16(I8x16 * i8x16)438   MOZ_MUST_USE bool readFixedI8x16(I8x16* i8x16) { return read<I8x16>(i8x16); }
readFixedI16x8(I16x8 * i16x8)439   MOZ_MUST_USE bool readFixedI16x8(I16x8* i16x8) { return read<I16x8>(i16x8); }
readFixedI32x4(I32x4 * i32x4)440   MOZ_MUST_USE bool readFixedI32x4(I32x4* i32x4) { return read<I32x4>(i32x4); }
readFixedF32x4(F32x4 * f32x4)441   MOZ_MUST_USE bool readFixedF32x4(F32x4* f32x4) { return read<F32x4>(f32x4); }
442 
443   // Variable-length encodings that all use LEB128.
444 
readVarU32(uint32_t * out)445   MOZ_MUST_USE bool readVarU32(uint32_t* out) {
446     return readVarU<uint32_t>(out);
447   }
readVarS32(int32_t * out)448   MOZ_MUST_USE bool readVarS32(int32_t* out) { return readVarS<int32_t>(out); }
readVarU64(uint64_t * out)449   MOZ_MUST_USE bool readVarU64(uint64_t* out) {
450     return readVarU<uint64_t>(out);
451   }
readVarS64(int64_t * out)452   MOZ_MUST_USE bool readVarS64(int64_t* out) { return readVarS<int64_t>(out); }
readValType(uint8_t * type)453   MOZ_MUST_USE bool readValType(uint8_t* type) {
454     static_assert(uint8_t(TypeCode::Limit) <= UINT8_MAX, "fits");
455     return readFixedU8(type);
456   }
readBlockType(uint8_t * type)457   MOZ_MUST_USE bool readBlockType(uint8_t* type) {
458     static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits");
459     return readFixedU8(type);
460   }
readOp(OpBytes * op)461   MOZ_MUST_USE bool readOp(OpBytes* op) {
462     static_assert(size_t(Op::Limit) == 256, "fits");
463     uint8_t u8;
464     if (!readFixedU8(&u8)) return false;
465     op->b0 = u8;
466     if (MOZ_LIKELY(!IsPrefixByte(u8))) return true;
467     if (!readFixedU8(&u8)) {
468       op->b1 = 0;  // Make it sane
469       return false;
470     }
471     op->b1 = u8;
472     return true;
473   }
474 
475   // See writeBytes comment.
476 
477   MOZ_MUST_USE bool readBytes(uint32_t numBytes,
478                               const uint8_t** bytes = nullptr) {
479     if (bytes) *bytes = cur_;
480     if (bytesRemain() < numBytes) return false;
481     cur_ += numBytes;
482     return true;
483   }
484 
485   // See "section" description in Encoder.
486 
487   MOZ_MUST_USE bool readSectionHeader(uint8_t* id, SectionRange* range);
488 
489   MOZ_MUST_USE bool startSection(SectionId id, ModuleEnvironment* env,
490                                  MaybeSectionRange* range,
491                                  const char* sectionName);
492   MOZ_MUST_USE bool finishSection(const SectionRange& range,
493                                   const char* sectionName);
494 
495   // Custom sections do not cause validation errors unless the error is in
496   // the section header itself.
497 
498   MOZ_MUST_USE bool startCustomSection(const char* expected,
499                                        size_t expectedLength,
500                                        ModuleEnvironment* env,
501                                        MaybeSectionRange* range);
502   template <size_t NameSizeWith0>
startCustomSection(const char (& name)[NameSizeWith0],ModuleEnvironment * env,MaybeSectionRange * range)503   MOZ_MUST_USE bool startCustomSection(const char (&name)[NameSizeWith0],
504                                        ModuleEnvironment* env,
505                                        MaybeSectionRange* range) {
506     MOZ_ASSERT(name[NameSizeWith0 - 1] == '\0');
507     return startCustomSection(name, NameSizeWith0 - 1, env, range);
508   }
509   void finishCustomSection(const SectionRange& range);
510   MOZ_MUST_USE bool skipCustomSection(ModuleEnvironment* env);
511 
512   // The Name section has its own optional subsections.
513 
514   MOZ_MUST_USE bool startNameSubsection(NameType nameType,
515                                         Maybe<uint32_t>* endOffset);
516   MOZ_MUST_USE bool finishNameSubsection(uint32_t endOffset);
517 
518   // The infallible "unchecked" decoding functions can be used when we are
519   // sure that the bytes are well-formed (by construction or due to previous
520   // validation).
521 
uncheckedReadFixedU8()522   uint8_t uncheckedReadFixedU8() { return uncheckedRead<uint8_t>(); }
uncheckedReadFixedU32()523   uint32_t uncheckedReadFixedU32() { return uncheckedRead<uint32_t>(); }
uncheckedReadFixedF32(float * out)524   void uncheckedReadFixedF32(float* out) { uncheckedRead<float>(out); }
uncheckedReadFixedF64(double * out)525   void uncheckedReadFixedF64(double* out) { uncheckedRead<double>(out); }
526   template <typename UInt>
uncheckedReadVarU()527   UInt uncheckedReadVarU() {
528     static const unsigned numBits = sizeof(UInt) * CHAR_BIT;
529     static const unsigned remainderBits = numBits % 7;
530     static const unsigned numBitsInSevens = numBits - remainderBits;
531     UInt decoded = 0;
532     uint32_t shift = 0;
533     do {
534       uint8_t byte = *cur_++;
535       if (!(byte & 0x80)) return decoded | (UInt(byte) << shift);
536       decoded |= UInt(byte & 0x7f) << shift;
537       shift += 7;
538     } while (shift != numBitsInSevens);
539     uint8_t byte = *cur_++;
540     MOZ_ASSERT(!(byte & 0xf0));
541     return decoded | (UInt(byte) << numBitsInSevens);
542   }
uncheckedReadVarU32()543   uint32_t uncheckedReadVarU32() { return uncheckedReadVarU<uint32_t>(); }
uncheckedReadVarS32()544   int32_t uncheckedReadVarS32() {
545     int32_t i32 = 0;
546     MOZ_ALWAYS_TRUE(readVarS32(&i32));
547     return i32;
548   }
uncheckedReadVarU64()549   uint64_t uncheckedReadVarU64() { return uncheckedReadVarU<uint64_t>(); }
uncheckedReadVarS64()550   int64_t uncheckedReadVarS64() {
551     int64_t i64 = 0;
552     MOZ_ALWAYS_TRUE(readVarS64(&i64));
553     return i64;
554   }
uncheckedReadValType()555   ValType uncheckedReadValType() { return (ValType)uncheckedReadFixedU8(); }
uncheckedReadOp()556   Op uncheckedReadOp() {
557     static_assert(size_t(Op::Limit) == 256, "fits");
558     uint8_t u8 = uncheckedReadFixedU8();
559     return u8 != UINT8_MAX ? Op(u8) : Op(uncheckedReadFixedU8() + UINT8_MAX);
560   }
uncheckedReadFixedI8x16(I8x16 * i8x16)561   void uncheckedReadFixedI8x16(I8x16* i8x16) {
562     struct T {
563       I8x16 v;
564     };
565     T t = uncheckedRead<T>();
566     memcpy(i8x16, &t, sizeof(t));
567   }
uncheckedReadFixedI16x8(I16x8 * i16x8)568   void uncheckedReadFixedI16x8(I16x8* i16x8) {
569     struct T {
570       I16x8 v;
571     };
572     T t = uncheckedRead<T>();
573     memcpy(i16x8, &t, sizeof(t));
574   }
uncheckedReadFixedI32x4(I32x4 * i32x4)575   void uncheckedReadFixedI32x4(I32x4* i32x4) {
576     struct T {
577       I32x4 v;
578     };
579     T t = uncheckedRead<T>();
580     memcpy(i32x4, &t, sizeof(t));
581   }
uncheckedReadFixedF32x4(F32x4 * f32x4)582   void uncheckedReadFixedF32x4(F32x4* f32x4) {
583     struct T {
584       F32x4 v;
585     };
586     T t = uncheckedRead<T>();
587     memcpy(f32x4, &t, sizeof(t));
588   }
589 };
590 
591 // The local entries are part of function bodies and thus serialized by both
592 // wasm and asm.js and decoded as part of both validation and compilation.
593 
594 MOZ_MUST_USE bool EncodeLocalEntries(Encoder& d, const ValTypeVector& locals);
595 
596 MOZ_MUST_USE bool DecodeLocalEntries(Decoder& d, ModuleKind kind,
597                                      ValTypeVector* locals);
598 
599 // Returns whether the given [begin, end) prefix of a module's bytecode starts a
600 // code section and, if so, returns the SectionRange of that code section.
601 // Note that, even if this function returns 'false', [begin, end) may actually
602 // be a valid module in the special case when there are no function defs and the
603 // code section is not present. Such modules can be valid so the caller must
604 // handle this special case.
605 
606 MOZ_MUST_USE bool StartsCodeSection(const uint8_t* begin, const uint8_t* end,
607                                     SectionRange* range);
608 
609 // Calling DecodeModuleEnvironment decodes all sections up to the code section
610 // and performs full validation of all those sections. The client must then
611 // decode the code section itself, reusing ValidateFunctionBody if necessary,
612 // and finally call DecodeModuleTail to decode all remaining sections after the
613 // code section (again, performing full validation).
614 
615 MOZ_MUST_USE bool DecodeModuleEnvironment(Decoder& d, ModuleEnvironment* env);
616 
617 MOZ_MUST_USE bool ValidateFunctionBody(const ModuleEnvironment& env,
618                                        uint32_t funcIndex, uint32_t bodySize,
619                                        Decoder& d);
620 
621 MOZ_MUST_USE bool DecodeModuleTail(Decoder& d, ModuleEnvironment* env);
622 
623 // Validate an entire module, returning true if the module was validated
624 // successfully. If Validate returns false:
625 //  - if *error is null, the caller should report out-of-memory
626 //  - otherwise, there was a legitimate error described by *error
627 
628 MOZ_MUST_USE bool Validate(JSContext* cx, const ShareableBytes& bytecode,
629                            UniqueChars* error);
630 
631 }  // namespace wasm
632 }  // namespace js
633 
634 #endif  // namespace wasm_validate_h
635