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