Source code

Revision control

Other Tools

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 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
*
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 "ds/Bitmap.h"
25
26
#include "wasm/WasmTypes.h"
27
28
namespace js {
29
namespace wasm {
30
31
// This struct captures the bytecode offset of a section's payload (so not
32
// including the header) and the size of the payload.
33
34
struct SectionRange {
35
uint32_t start;
36
uint32_t size;
37
38
uint32_t end() const { return start + size; }
39
bool operator==(const SectionRange& rhs) const {
40
return start == rhs.start && size == rhs.size;
41
}
42
};
43
44
typedef Maybe<SectionRange> MaybeSectionRange;
45
46
// CompilerEnvironment holds any values that will be needed to compute
47
// compilation parameters once the module's feature opt-in sections have been
48
// parsed.
49
//
50
// Subsequent to construction a computeParameters() call will compute the final
51
// compilation parameters, and the object can then be queried for their values.
52
53
struct CompileArgs;
54
class Decoder;
55
56
struct CompilerEnvironment {
57
// The object starts in one of two "initial" states; computeParameters moves
58
// it into the "computed" state.
59
enum State { InitialWithArgs, InitialWithModeTierDebug, Computed };
60
61
State state_;
62
union {
63
// Value if the state_ == InitialWithArgs.
64
const CompileArgs* args_;
65
66
// Value in the other two states.
67
struct {
68
CompileMode mode_;
69
Tier tier_;
70
OptimizedBackend optimizedBackend_;
71
DebugEnabled debug_;
72
bool refTypes_;
73
bool gcTypes_;
74
bool multiValues_;
75
bool hugeMemory_;
76
bool bigInt_;
77
};
78
};
79
80
public:
81
// Retain a reference to the CompileArgs. A subsequent computeParameters()
82
// will compute all parameters from the CompileArgs and additional values.
83
explicit CompilerEnvironment(const CompileArgs& args);
84
85
// Save the provided values for mode, tier, and debug, and the initial value
86
// for gcTypes/refTypes. A subsequent computeParameters() will compute the
87
// final value of gcTypes/refTypes.
88
CompilerEnvironment(CompileMode mode, Tier tier,
89
OptimizedBackend optimizedBackend,
90
DebugEnabled debugEnabled, bool multiValueConfigured,
91
bool refTypesConfigured, bool gcTypesConfigured,
92
bool hugeMemory, bool bigIntConfigured);
93
94
// Compute any remaining compilation parameters.
95
void computeParameters(Decoder& d, bool gcFeatureOptIn);
96
97
// Compute any remaining compilation parameters. Only use this method if
98
// the CompilerEnvironment was created with values for mode, tier, and
99
// debug.
100
void computeParameters(bool gcFeatureOptIn);
101
102
bool isComputed() const { return state_ == Computed; }
103
CompileMode mode() const {
104
MOZ_ASSERT(isComputed());
105
return mode_;
106
}
107
Tier tier() const {
108
MOZ_ASSERT(isComputed());
109
return tier_;
110
}
111
OptimizedBackend optimizedBackend() const {
112
MOZ_ASSERT(isComputed());
113
return optimizedBackend_;
114
}
115
DebugEnabled debug() const {
116
MOZ_ASSERT(isComputed());
117
return debug_;
118
}
119
bool gcTypes() const {
120
MOZ_ASSERT(isComputed());
121
return gcTypes_;
122
}
123
bool refTypes() const {
124
MOZ_ASSERT(isComputed());
125
return refTypes_;
126
}
127
bool multiValues() const {
128
MOZ_ASSERT(isComputed());
129
return multiValues_;
130
}
131
bool hugeMemory() const {
132
MOZ_ASSERT(isComputed());
133
return hugeMemory_;
134
}
135
bool bigInt() const {
136
MOZ_ASSERT(isComputed());
137
return bigInt_;
138
}
139
};
140
141
// ModuleEnvironment contains all the state necessary to process or render
142
// functions, and all of the state necessary to validate all aspects of the
143
// functions.
144
//
145
// A ModuleEnvironment is created by decoding all the sections before the wasm
146
// code section and then used immutably during. When compiling a module using a
147
// ModuleGenerator, the ModuleEnvironment holds state shared between the
148
// ModuleGenerator thread and background compile threads. All the threads
149
// are given a read-only view of the ModuleEnvironment, thus preventing race
150
// conditions.
151
152
struct ModuleEnvironment {
153
// Constant parameters for the entire compilation:
154
const ModuleKind kind;
155
const Shareable sharedMemoryEnabled;
156
CompilerEnvironment* const compilerEnv;
157
158
// Module fields decoded from the module environment (or initialized while
159
// validating an asm.js module) and immutable during compilation:
160
#ifdef ENABLE_WASM_GC
161
// `gcFeatureOptIn` reflects the presence in a module of a GcFeatureOptIn
162
// section. This variable will be removed eventually, allowing it to be
163
// replaced everywhere by the value true.
164
//
165
// The flag is used in the value of gcTypesEnabled(), which controls whether
166
// struct types and associated instructions are accepted during validation.
167
bool gcFeatureOptIn;
168
#endif
169
Maybe<uint32_t> dataCount;
170
MemoryUsage memoryUsage;
171
uint32_t minMemoryLength;
172
Maybe<uint32_t> maxMemoryLength;
173
uint32_t numStructTypes;
174
TypeDefVector types;
175
FuncTypeWithIdPtrVector funcTypes;
176
Uint32Vector funcImportGlobalDataOffsets;
177
GlobalDescVector globals;
178
TableDescVector tables;
179
Uint32Vector asmJSSigToTableIndex;
180
ImportVector imports;
181
ExportVector exports;
182
Maybe<uint32_t> startFuncIndex;
183
ElemSegmentVector elemSegments;
184
MaybeSectionRange codeSection;
185
SparseBitmap validForRefFunc;
186
187
// Fields decoded as part of the wasm module tail:
188
DataSegmentEnvVector dataSegments;
189
CustomSectionEnvVector customSections;
190
Maybe<uint32_t> nameCustomSectionIndex;
191
Maybe<Name> moduleName;
192
NameVector funcNames;
193
194
explicit ModuleEnvironment(CompilerEnvironment* compilerEnv,
195
Shareable sharedMemoryEnabled,
196
ModuleKind kind = ModuleKind::Wasm)
197
: kind(kind),
198
sharedMemoryEnabled(sharedMemoryEnabled),
199
compilerEnv(compilerEnv),
200
#ifdef ENABLE_WASM_GC
201
gcFeatureOptIn(false),
202
#endif
203
memoryUsage(MemoryUsage::None),
204
minMemoryLength(0),
205
numStructTypes(0) {
206
}
207
208
Tier tier() const { return compilerEnv->tier(); }
209
OptimizedBackend optimizedBackend() const {
210
return compilerEnv->optimizedBackend();
211
}
212
CompileMode mode() const { return compilerEnv->mode(); }
213
DebugEnabled debug() const { return compilerEnv->debug(); }
214
size_t numTables() const { return tables.length(); }
215
size_t numTypes() const { return types.length(); }
216
size_t numFuncs() const { return funcTypes.length(); }
217
size_t numFuncImports() const { return funcImportGlobalDataOffsets.length(); }
218
size_t numFuncDefs() const {
219
return funcTypes.length() - funcImportGlobalDataOffsets.length();
220
}
221
bool gcTypesEnabled() const { return compilerEnv->gcTypes(); }
222
bool refTypesEnabled() const { return compilerEnv->refTypes(); }
223
bool multiValuesEnabled() const { return compilerEnv->multiValues(); }
224
bool bigIntEnabled() const { return compilerEnv->bigInt(); }
225
bool usesMemory() const { return memoryUsage != MemoryUsage::None; }
226
bool usesSharedMemory() const { return memoryUsage == MemoryUsage::Shared; }
227
bool isAsmJS() const { return kind == ModuleKind::AsmJS; }
228
bool debugEnabled() const {
229
return compilerEnv->debug() == DebugEnabled::True;
230
}
231
bool hugeMemoryEnabled() const {
232
return !isAsmJS() && compilerEnv->hugeMemory();
233
}
234
uint32_t funcMaxResults() const {
235
return multiValuesEnabled() ? MaxResults : 1;
236
}
237
bool funcIsImport(uint32_t funcIndex) const {
238
return funcIndex < funcImportGlobalDataOffsets.length();
239
}
240
bool isRefSubtypeOf(ValType one, ValType two) const {
241
MOZ_ASSERT(one.isReference());
242
MOZ_ASSERT(two.isReference());
243
#if defined(ENABLE_WASM_REFTYPES)
244
# if defined(ENABLE_WASM_GC)
245
return one == two || two == ValType::AnyRef || one == ValType::NullRef ||
246
(one.isRef() && two.isRef() && gcTypesEnabled() &&
247
isStructPrefixOf(two, one));
248
# else
249
return one == two || two == ValType::AnyRef || one == ValType::NullRef;
250
# endif
251
#else
252
return one == two;
253
#endif
254
}
255
256
private:
257
bool isStructPrefixOf(ValType a, ValType b) const {
258
const StructType& other = types[a.refTypeIndex()].structType();
259
return types[b.refTypeIndex()].structType().hasPrefix(other);
260
}
261
};
262
263
// ElemSegmentFlags provides methods for decoding and encoding the flags field
264
// of an element segment. This is needed as the flags field has a non-trivial
265
// encoding that is effectively split into independent `kind` and `payload`
266
// enums.
267
class ElemSegmentFlags {
268
enum class Flags : uint32_t {
269
Passive = 0x1,
270
WithIndexOrDeclared = 0x2,
271
ElemExpression = 0x4,
272
// Below this line are convenient combinations of flags
273
KindMask = Passive | WithIndexOrDeclared,
274
PayloadMask = ElemExpression,
275
AllFlags = Passive | WithIndexOrDeclared | ElemExpression,
276
};
277
uint32_t encoded_;
278
279
explicit ElemSegmentFlags(uint32_t encoded) : encoded_(encoded) {}
280
281
public:
282
ElemSegmentFlags(ElemSegmentKind kind, ElemSegmentPayload payload) {
283
encoded_ = uint32_t(kind) | uint32_t(payload);
284
}
285
286
static Maybe<ElemSegmentFlags> construct(uint32_t encoded) {
287
if (encoded > uint32_t(Flags::AllFlags)) {
288
return Nothing();
289
}
290
return Some(ElemSegmentFlags(encoded));
291
}
292
293
uint32_t encoded() const { return encoded_; }
294
295
ElemSegmentKind kind() const {
296
return static_cast<ElemSegmentKind>(encoded_ & uint32_t(Flags::KindMask));
297
}
298
ElemSegmentPayload payload() const {
299
return static_cast<ElemSegmentPayload>(encoded_ &
300
uint32_t(Flags::PayloadMask));
301
}
302
};
303
304
// The Encoder class appends bytes to the Bytes object it is given during
305
// construction. The client is responsible for the Bytes's lifetime and must
306
// keep the Bytes alive as long as the Encoder is used.
307
308
class Encoder {
309
Bytes& bytes_;
310
311
template <class T>
312
MOZ_MUST_USE bool write(const T& v) {
313
return bytes_.append(reinterpret_cast<const uint8_t*>(&v), sizeof(T));
314
}
315
316
template <typename UInt>
317
MOZ_MUST_USE bool writeVarU(UInt i) {
318
do {
319
uint8_t byte = i & 0x7f;
320
i >>= 7;
321
if (i != 0) {
322
byte |= 0x80;
323
}
324
if (!bytes_.append(byte)) {
325
return false;
326
}
327
} while (i != 0);
328
return true;
329
}
330
331
template <typename SInt>
332
MOZ_MUST_USE bool writeVarS(SInt i) {
333
bool done;
334
do {
335
uint8_t byte = i & 0x7f;
336
i >>= 7;
337
done = ((i == 0) && !(byte & 0x40)) || ((i == -1) && (byte & 0x40));
338
if (!done) {
339
byte |= 0x80;
340
}
341
if (!bytes_.append(byte)) {
342
return false;
343
}
344
} while (!done);
345
return true;
346
}
347
348
void patchVarU32(size_t offset, uint32_t patchBits, uint32_t assertBits) {
349
do {
350
uint8_t assertByte = assertBits & 0x7f;
351
uint8_t patchByte = patchBits & 0x7f;
352
assertBits >>= 7;
353
patchBits >>= 7;
354
if (assertBits != 0) {
355
assertByte |= 0x80;
356
patchByte |= 0x80;
357
}
358
MOZ_ASSERT(assertByte == bytes_[offset]);
359
bytes_[offset] = patchByte;
360
offset++;
361
} while (assertBits != 0);
362
}
363
364
void patchFixedU7(size_t offset, uint8_t patchBits, uint8_t assertBits) {
365
MOZ_ASSERT(patchBits <= uint8_t(INT8_MAX));
366
patchFixedU8(offset, patchBits, assertBits);
367
}
368
369
void patchFixedU8(size_t offset, uint8_t patchBits, uint8_t assertBits) {
370
MOZ_ASSERT(bytes_[offset] == assertBits);
371
bytes_[offset] = patchBits;
372
}
373
374
uint32_t varU32ByteLength(size_t offset) const {
375
size_t start = offset;
376
while (bytes_[offset] & 0x80) {
377
offset++;
378
}
379
return offset - start + 1;
380
}
381
382
public:
383
explicit Encoder(Bytes& bytes) : bytes_(bytes) { MOZ_ASSERT(empty()); }
384
385
size_t currentOffset() const { return bytes_.length(); }
386
bool empty() const { return currentOffset() == 0; }
387
388
// Fixed-size encoding operations simply copy the literal bytes (without
389
// attempting to align).
390
391
MOZ_MUST_USE bool writeFixedU7(uint8_t i) {
392
MOZ_ASSERT(i <= uint8_t(INT8_MAX));
393
return writeFixedU8(i);
394
}
395
MOZ_MUST_USE bool writeFixedU8(uint8_t i) { return write<uint8_t>(i); }
396
MOZ_MUST_USE bool writeFixedU32(uint32_t i) { return write<uint32_t>(i); }
397
MOZ_MUST_USE bool writeFixedF32(float f) { return write<float>(f); }
398
MOZ_MUST_USE bool writeFixedF64(double d) { return write<double>(d); }
399
400
// Variable-length encodings that all use LEB128.
401
402
MOZ_MUST_USE bool writeVarU32(uint32_t i) { return writeVarU<uint32_t>(i); }
403
MOZ_MUST_USE bool writeVarS32(int32_t i) { return writeVarS<int32_t>(i); }
404
MOZ_MUST_USE bool writeVarU64(uint64_t i) { return writeVarU<uint64_t>(i); }
405
MOZ_MUST_USE bool writeVarS64(int64_t i) { return writeVarS<int64_t>(i); }
406
MOZ_MUST_USE bool writeValType(ValType type) {
407
static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits");
408
MOZ_ASSERT(size_t(type.code()) < size_t(TypeCode::Limit));
409
if (type.isRef()) {
410
return writeFixedU8(uint8_t(TypeCode::Ref)) &&
411
writeVarU32(type.refTypeIndex());
412
}
413
return writeFixedU8(uint8_t(type.code()));
414
}
415
MOZ_MUST_USE bool writeOp(Op op) {
416
static_assert(size_t(Op::Limit) == 256, "fits");
417
MOZ_ASSERT(size_t(op) < size_t(Op::Limit));
418
return writeFixedU8(uint8_t(op));
419
}
420
MOZ_MUST_USE bool writeOp(MiscOp op) {
421
MOZ_ASSERT(size_t(op) < size_t(MiscOp::Limit));
422
return writeFixedU8(uint8_t(Op::MiscPrefix)) && writeVarU32(uint32_t(op));
423
}
424
MOZ_MUST_USE bool writeOp(ThreadOp op) {
425
MOZ_ASSERT(size_t(op) < size_t(ThreadOp::Limit));
426
return writeFixedU8(uint8_t(Op::ThreadPrefix)) && writeVarU32(uint32_t(op));
427
}
428
MOZ_MUST_USE bool writeOp(MozOp op) {
429
MOZ_ASSERT(size_t(op) < size_t(MozOp::Limit));
430
return writeFixedU8(uint8_t(Op::MozPrefix)) && writeVarU32(uint32_t(op));
431
}
432
433
// Fixed-length encodings that allow back-patching.
434
435
MOZ_MUST_USE bool writePatchableFixedU7(size_t* offset) {
436
*offset = bytes_.length();
437
return writeFixedU8(UINT8_MAX);
438
}
439
void patchFixedU7(size_t offset, uint8_t patchBits) {
440
return patchFixedU7(offset, patchBits, UINT8_MAX);
441
}
442
443
// Variable-length encodings that allow back-patching.
444
445
MOZ_MUST_USE bool writePatchableVarU32(size_t* offset) {
446
*offset = bytes_.length();
447
return writeVarU32(UINT32_MAX);
448
}
449
void patchVarU32(size_t offset, uint32_t patchBits) {
450
return patchVarU32(offset, patchBits, UINT32_MAX);
451
}
452
453
// Byte ranges start with an LEB128 length followed by an arbitrary sequence
454
// of bytes. When used for strings, bytes are to be interpreted as utf8.
455
456
MOZ_MUST_USE bool writeBytes(const void* bytes, uint32_t numBytes) {
457
return writeVarU32(numBytes) &&
458
bytes_.append(reinterpret_cast<const uint8_t*>(bytes), numBytes);
459
}
460
461
// A "section" is a contiguous range of bytes that stores its own size so
462
// that it may be trivially skipped without examining the payload. Sections
463
// require backpatching since the size of the section is only known at the
464
// end while the size's varU32 must be stored at the beginning. Immediately
465
// after the section length is the string id of the section.
466
467
MOZ_MUST_USE bool startSection(SectionId id, size_t* offset) {
468
MOZ_ASSERT(uint32_t(id) < 128);
469
return writeVarU32(uint32_t(id)) && writePatchableVarU32(offset);
470
}
471
void finishSection(size_t offset) {
472
return patchVarU32(offset,
473
bytes_.length() - offset - varU32ByteLength(offset));
474
}
475
};
476
477
// The Decoder class decodes the bytes in the range it is given during
478
// construction. The client is responsible for keeping the byte range alive as
479
// long as the Decoder is used.
480
481
class Decoder {
482
const uint8_t* const beg_;
483
const uint8_t* const end_;
484
const uint8_t* cur_;
485
const size_t offsetInModule_;
486
UniqueChars* error_;
487
UniqueCharsVector* warnings_;
488
bool resilientMode_;
489
490
template <class T>
491
MOZ_MUST_USE bool read(T* out) {
492
if (bytesRemain() < sizeof(T)) {
493
return false;
494
}
495
memcpy((void*)out, cur_, sizeof(T));
496
cur_ += sizeof(T);
497
return true;
498
}
499
500
template <class T>
501
T uncheckedRead() {
502
MOZ_ASSERT(bytesRemain() >= sizeof(T));
503
T ret;
504
memcpy(&ret, cur_, sizeof(T));
505
cur_ += sizeof(T);
506
return ret;
507
}
508
509
template <class T>
510
void uncheckedRead(T* ret) {
511
MOZ_ASSERT(bytesRemain() >= sizeof(T));
512
memcpy(ret, cur_, sizeof(T));
513
cur_ += sizeof(T);
514
}
515
516
template <typename UInt>
517
MOZ_MUST_USE bool readVarU(UInt* out) {
518
DebugOnly<const uint8_t*> before = cur_;
519
const unsigned numBits = sizeof(UInt) * CHAR_BIT;
520
const unsigned remainderBits = numBits % 7;
521
const unsigned numBitsInSevens = numBits - remainderBits;
522
UInt u = 0;
523
uint8_t byte;
524
UInt shift = 0;
525
do {
526
if (!readFixedU8(&byte)) {
527
return false;
528
}
529
if (!(byte & 0x80)) {
530
*out = u | UInt(byte) << shift;
531
return true;
532
}
533
u |= UInt(byte & 0x7F) << shift;
534
shift += 7;
535
} while (shift != numBitsInSevens);
536
if (!readFixedU8(&byte) || (byte & (unsigned(-1) << remainderBits))) {
537
return false;
538
}
539
*out = u | (UInt(byte) << numBitsInSevens);
540
MOZ_ASSERT_IF(sizeof(UInt) == 4,
541
unsigned(cur_ - before) <= MaxVarU32DecodedBytes);
542
return true;
543
}
544
545
template <typename SInt>
546
MOZ_MUST_USE bool readVarS(SInt* out) {
547
using UInt = typename mozilla::MakeUnsigned<SInt>::Type;
548
const unsigned numBits = sizeof(SInt) * CHAR_BIT;
549
const unsigned remainderBits = numBits % 7;
550
const unsigned numBitsInSevens = numBits - remainderBits;
551
SInt s = 0;
552
uint8_t byte;
553
unsigned shift = 0;
554
do {
555
if (!readFixedU8(&byte)) {
556
return false;
557
}
558
s |= SInt(byte & 0x7f) << shift;
559
shift += 7;
560
if (!(byte & 0x80)) {
561
if (byte & 0x40) {
562
s |= UInt(-1) << shift;
563
}
564
*out = s;
565
return true;
566
}
567
} while (shift < numBitsInSevens);
568
if (!remainderBits || !readFixedU8(&byte) || (byte & 0x80)) {
569
return false;
570
}
571
uint8_t mask = 0x7f & (uint8_t(-1) << remainderBits);
572
if ((byte & mask) != ((byte & (1 << (remainderBits - 1))) ? mask : 0)) {
573
return false;
574
}
575
*out = s | UInt(byte) << shift;
576
return true;
577
}
578
579
public:
580
Decoder(const uint8_t* begin, const uint8_t* end, size_t offsetInModule,
581
UniqueChars* error, UniqueCharsVector* warnings = nullptr,
582
bool resilientMode = false)
583
: beg_(begin),
584
end_(end),
585
cur_(begin),
586
offsetInModule_(offsetInModule),
587
error_(error),
588
warnings_(warnings),
589
resilientMode_(resilientMode) {
590
MOZ_ASSERT(begin <= end);
591
}
592
explicit Decoder(const Bytes& bytes, size_t offsetInModule = 0,
593
UniqueChars* error = nullptr,
594
UniqueCharsVector* warnings = nullptr)
595
: beg_(bytes.begin()),
596
end_(bytes.end()),
597
cur_(bytes.begin()),
598
offsetInModule_(offsetInModule),
599
error_(error),
600
warnings_(warnings),
601
resilientMode_(false) {}
602
603
// These convenience functions use currentOffset() as the errorOffset.
604
bool fail(const char* msg) { return fail(currentOffset(), msg); }
605
bool failf(const char* msg, ...) MOZ_FORMAT_PRINTF(2, 3);
606
void warnf(const char* msg, ...) MOZ_FORMAT_PRINTF(2, 3);
607
608
// Report an error at the given offset (relative to the whole module).
609
bool fail(size_t errorOffset, const char* msg);
610
611
UniqueChars* error() { return error_; }
612
613
void clearError() {
614
if (error_) {
615
error_->reset();
616
}
617
}
618
619
bool done() const {
620
MOZ_ASSERT(cur_ <= end_);
621
return cur_ == end_;
622
}
623
bool resilientMode() const { return resilientMode_; }
624
625
size_t bytesRemain() const {
626
MOZ_ASSERT(end_ >= cur_);
627
return size_t(end_ - cur_);
628
}
629
// pos must be a value previously returned from currentPosition.
630
void rollbackPosition(const uint8_t* pos) { cur_ = pos; }
631
const uint8_t* currentPosition() const { return cur_; }
632
size_t currentOffset() const { return offsetInModule_ + (cur_ - beg_); }
633
const uint8_t* begin() const { return beg_; }
634
const uint8_t* end() const { return end_; }
635
636
// Peek at the next byte, if it exists, without advancing the position.
637
638
bool peekByte(uint8_t* byte) {
639
if (done()) {
640
return false;
641
}
642
*byte = *cur_;
643
return true;
644
}
645
646
// Fixed-size encoding operations simply copy the literal bytes (without
647
// attempting to align).
648
649
MOZ_MUST_USE bool readFixedU8(uint8_t* i) { return read<uint8_t>(i); }
650
MOZ_MUST_USE bool readFixedU32(uint32_t* u) { return read<uint32_t>(u); }
651
MOZ_MUST_USE bool readFixedF32(float* f) { return read<float>(f); }
652
MOZ_MUST_USE bool readFixedF64(double* d) { return read<double>(d); }
653
654
// Variable-length encodings that all use LEB128.
655
656
MOZ_MUST_USE bool readVarU32(uint32_t* out) {
657
return readVarU<uint32_t>(out);
658
}
659
MOZ_MUST_USE bool readVarS32(int32_t* out) { return readVarS<int32_t>(out); }
660
MOZ_MUST_USE bool readVarU64(uint64_t* out) {
661
return readVarU<uint64_t>(out);
662
}
663
MOZ_MUST_USE bool readVarS64(int64_t* out) { return readVarS<int64_t>(out); }
664
665
MOZ_MUST_USE ValType uncheckedReadValType() {
666
uint8_t code = uncheckedReadFixedU8();
667
switch (code) {
668
case uint8_t(ValType::Ref):
669
return ValType(ValType::Code(code), uncheckedReadVarU32());
670
default:
671
return ValType::Code(code);
672
}
673
}
674
MOZ_MUST_USE bool readValType(uint32_t numTypes, bool refTypesEnabled,
675
bool gcTypesEnabled, ValType* type) {
676
static_assert(uint8_t(TypeCode::Limit) <= UINT8_MAX, "fits");
677
uint8_t code;
678
if (!readFixedU8(&code)) {
679
return false;
680
}
681
switch (code) {
682
case uint8_t(ValType::I32):
683
case uint8_t(ValType::F32):
684
case uint8_t(ValType::F64):
685
case uint8_t(ValType::I64):
686
*type = ValType::Code(code);
687
return true;
688
#ifdef ENABLE_WASM_REFTYPES
689
case uint8_t(ValType::FuncRef):
690
case uint8_t(ValType::AnyRef):
691
if (!refTypesEnabled) {
692
return fail("reference types not enabled");
693
}
694
*type = ValType::Code(code);
695
return true;
696
# ifdef ENABLE_WASM_GC
697
case uint8_t(ValType::Ref): {
698
if (!gcTypesEnabled) {
699
return fail("(ref T) types not enabled");
700
}
701
uint32_t typeIndex;
702
if (!readVarU32(&typeIndex)) {
703
return false;
704
}
705
if (typeIndex >= numTypes) {
706
return fail("ref index out of range");
707
}
708
*type = ValType(ValType::Code(code), typeIndex);
709
return true;
710
}
711
# endif
712
#endif
713
default:
714
return fail("bad type");
715
}
716
}
717
MOZ_MUST_USE bool readValType(const TypeDefVector& types,
718
bool refTypesEnabled, bool gcTypesEnabled,
719
ValType* type) {
720
if (!readValType(types.length(), refTypesEnabled, gcTypesEnabled, type)) {
721
return false;
722
}
723
if (type->isRef() && !types[type->refTypeIndex()].isStructType()) {
724
return fail("ref does not reference a struct type");
725
}
726
return true;
727
}
728
MOZ_MUST_USE bool readOp(OpBytes* op) {
729
static_assert(size_t(Op::Limit) == 256, "fits");
730
uint8_t u8;
731
if (!readFixedU8(&u8)) {
732
return false;
733
}
734
op->b0 = u8;
735
if (MOZ_LIKELY(!IsPrefixByte(u8))) {
736
return true;
737
}
738
if (!readFixedU8(&u8)) {
739
op->b1 = 0; // Make it sane
740
return false;
741
}
742
op->b1 = u8;
743
return true;
744
}
745
746
// See writeBytes comment.
747
748
MOZ_MUST_USE bool readBytes(uint32_t numBytes,
749
const uint8_t** bytes = nullptr) {
750
if (bytes) {
751
*bytes = cur_;
752
}
753
if (bytesRemain() < numBytes) {
754
return false;
755
}
756
cur_ += numBytes;
757
return true;
758
}
759
760
// See "section" description in Encoder.
761
762
MOZ_MUST_USE bool readSectionHeader(uint8_t* id, SectionRange* range);
763
764
MOZ_MUST_USE bool startSection(SectionId id, ModuleEnvironment* env,
765
MaybeSectionRange* range,
766
const char* sectionName);
767
MOZ_MUST_USE bool finishSection(const SectionRange& range,
768
const char* sectionName);
769
770
// Custom sections do not cause validation errors unless the error is in
771
// the section header itself.
772
773
MOZ_MUST_USE bool startCustomSection(const char* expected,
774
size_t expectedLength,
775
ModuleEnvironment* env,
776
MaybeSectionRange* range);
777
778
template <size_t NameSizeWith0>
779
MOZ_MUST_USE bool startCustomSection(const char (&name)[NameSizeWith0],
780
ModuleEnvironment* env,
781
MaybeSectionRange* range) {
782
MOZ_ASSERT(name[NameSizeWith0 - 1] == '\0');
783
return startCustomSection(name, NameSizeWith0 - 1, env, range);
784
}
785
786
void finishCustomSection(const char* name, const SectionRange& range);
787
void skipAndFinishCustomSection(const SectionRange& range);
788
789
MOZ_MUST_USE bool skipCustomSection(ModuleEnvironment* env);
790
791
// The Name section has its own optional subsections.
792
793
MOZ_MUST_USE bool startNameSubsection(NameType nameType,
794
Maybe<uint32_t>* endOffset);
795
MOZ_MUST_USE bool finishNameSubsection(uint32_t endOffset);
796
MOZ_MUST_USE bool skipNameSubsection();
797
798
// The infallible "unchecked" decoding functions can be used when we are
799
// sure that the bytes are well-formed (by construction or due to previous
800
// validation).
801
802
uint8_t uncheckedReadFixedU8() { return uncheckedRead<uint8_t>(); }
803
uint32_t uncheckedReadFixedU32() { return uncheckedRead<uint32_t>(); }
804
void uncheckedReadFixedF32(float* out) { uncheckedRead<float>(out); }
805
void uncheckedReadFixedF64(double* out) { uncheckedRead<double>(out); }
806
template <typename UInt>
807
UInt uncheckedReadVarU() {
808
static const unsigned numBits = sizeof(UInt) * CHAR_BIT;
809
static const unsigned remainderBits = numBits % 7;
810
static const unsigned numBitsInSevens = numBits - remainderBits;
811
UInt decoded = 0;
812
uint32_t shift = 0;
813
do {
814
uint8_t byte = *cur_++;
815
if (!(byte & 0x80)) {
816
return decoded | (UInt(byte) << shift);
817
}
818
decoded |= UInt(byte & 0x7f) << shift;
819
shift += 7;
820
} while (shift != numBitsInSevens);
821
uint8_t byte = *cur_++;
822
MOZ_ASSERT(!(byte & 0xf0));
823
return decoded | (UInt(byte) << numBitsInSevens);
824
}
825
uint32_t uncheckedReadVarU32() { return uncheckedReadVarU<uint32_t>(); }
826
int32_t uncheckedReadVarS32() {
827
int32_t i32 = 0;
828
MOZ_ALWAYS_TRUE(readVarS32(&i32));
829
return i32;
830
}
831
uint64_t uncheckedReadVarU64() { return uncheckedReadVarU<uint64_t>(); }
832
int64_t uncheckedReadVarS64() {
833
int64_t i64 = 0;
834
MOZ_ALWAYS_TRUE(readVarS64(&i64));
835
return i64;
836
}
837
Op uncheckedReadOp() {
838
static_assert(size_t(Op::Limit) == 256, "fits");
839
uint8_t u8 = uncheckedReadFixedU8();
840
return u8 != UINT8_MAX ? Op(u8) : Op(uncheckedReadFixedU8() + UINT8_MAX);
841
}
842
};
843
844
// The local entries are part of function bodies and thus serialized by both
845
// wasm and asm.js and decoded as part of both validation and compilation.
846
847
MOZ_MUST_USE bool EncodeLocalEntries(Encoder& d, const ValTypeVector& locals);
848
849
// This performs no validation; the local entries must already have been
850
// validated by an earlier pass.
851
852
MOZ_MUST_USE bool DecodeValidatedLocalEntries(Decoder& d,
853
ValTypeVector* locals);
854
855
// This validates the entries.
856
857
MOZ_MUST_USE bool DecodeLocalEntries(Decoder& d, const TypeDefVector& types,
858
bool refTypesEnabled, bool gcTypesEnabled,
859
ValTypeVector* locals);
860
861
// Returns whether the given [begin, end) prefix of a module's bytecode starts a
862
// code section and, if so, returns the SectionRange of that code section.
863
// Note that, even if this function returns 'false', [begin, end) may actually
864
// be a valid module in the special case when there are no function defs and the
865
// code section is not present. Such modules can be valid so the caller must
866
// handle this special case.
867
868
MOZ_MUST_USE bool StartsCodeSection(const uint8_t* begin, const uint8_t* end,
869
SectionRange* range);
870
871
// Calling DecodeModuleEnvironment decodes all sections up to the code section
872
// and performs full validation of all those sections. The client must then
873
// decode the code section itself, reusing ValidateFunctionBody if necessary,
874
// and finally call DecodeModuleTail to decode all remaining sections after the
875
// code section (again, performing full validation).
876
877
MOZ_MUST_USE bool DecodeModuleEnvironment(Decoder& d, ModuleEnvironment* env);
878
879
MOZ_MUST_USE bool ValidateFunctionBody(const ModuleEnvironment& env,
880
uint32_t funcIndex, uint32_t bodySize,
881
Decoder& d);
882
883
MOZ_MUST_USE bool DecodeModuleTail(Decoder& d, ModuleEnvironment* env);
884
885
void ConvertMemoryPagesToBytes(Limits* memory);
886
887
// Validate an entire module, returning true if the module was validated
888
// successfully. If Validate returns false:
889
// - if *error is null, the caller should report out-of-memory
890
// - otherwise, there was a legitimate error described by *error
891
892
MOZ_MUST_USE bool Validate(JSContext* cx, const ShareableBytes& bytecode,
893
UniqueChars* error);
894
895
} // namespace wasm
896
} // namespace js
897
898
#endif // namespace wasm_validate_h