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  *
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 <type_traits>
23 
24 #include "js/WasmFeatures.h"
25 
26 #include "wasm/WasmBinary.h"
27 #include "wasm/WasmCompile.h"
28 #include "wasm/WasmTypes.h"
29 
30 namespace js {
31 namespace wasm {
32 
33 // ModuleEnvironment contains all the state necessary to process or render
34 // functions, and all of the state necessary to validate all aspects of the
35 // functions.
36 //
37 // A ModuleEnvironment is created by decoding all the sections before the wasm
38 // code section and then used immutably during. When compiling a module using a
39 // ModuleGenerator, the ModuleEnvironment holds state shared between the
40 // ModuleGenerator thread and background compile threads. All the threads
41 // are given a read-only view of the ModuleEnvironment, thus preventing race
42 // conditions.
43 
44 struct ModuleEnvironment {
45   // Constant parameters for the entire compilation:
46   const ModuleKind kind;
47   const FeatureArgs features;
48 
49   // Module fields decoded from the module environment (or initialized while
50   // validating an asm.js module) and immutable during compilation:
51   Maybe<uint32_t> dataCount;
52   Maybe<MemoryDesc> memory;
53   TypeContext types;
54   TypeIdDescVector typeIds;
55   FuncDescVector funcs;
56   Uint32Vector funcImportGlobalDataOffsets;
57 
58   GlobalDescVector globals;
59 #ifdef ENABLE_WASM_EXCEPTIONS
60   EventDescVector events;
61 #endif
62   TableDescVector tables;
63   Uint32Vector asmJSSigToTableIndex;
64   ImportVector imports;
65   ExportVector exports;
66   Maybe<uint32_t> startFuncIndex;
67   ElemSegmentVector elemSegments;
68   MaybeSectionRange codeSection;
69   bool usesDuplicateImports;
70 
71   // Fields decoded as part of the wasm module tail:
72   DataSegmentEnvVector dataSegments;
73   CustomSectionEnvVector customSections;
74   Maybe<uint32_t> nameCustomSectionIndex;
75   Maybe<Name> moduleName;
76   NameVector funcNames;
77 
78   explicit ModuleEnvironment(FeatureArgs features,
79                              ModuleKind kind = ModuleKind::Wasm)
kindModuleEnvironment80       : kind(kind),
81         features(features),
82         memory(Nothing()),
83         types(features, TypeDefVector()),
84         usesDuplicateImports(false) {}
85 
numTablesModuleEnvironment86   size_t numTables() const { return tables.length(); }
numTypesModuleEnvironment87   size_t numTypes() const { return types.length(); }
numFuncsModuleEnvironment88   size_t numFuncs() const { return funcs.length(); }
numFuncImportsModuleEnvironment89   size_t numFuncImports() const { return funcImportGlobalDataOffsets.length(); }
numFuncDefsModuleEnvironment90   size_t numFuncDefs() const {
91     return funcs.length() - funcImportGlobalDataOffsets.length();
92   }
93 
94 #define WASM_FEATURE(NAME, SHORT_NAME, ...) \
95   bool SHORT_NAME##Enabled() const { return features.SHORT_NAME; }
JS_FOR_WASM_FEATURESModuleEnvironment96   JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE)
97 #undef WASM_FEATURE
98   Shareable sharedMemoryEnabled() const { return features.sharedMemory; }
hugeMemoryEnabledModuleEnvironment99   bool hugeMemoryEnabled() const { return !isAsmJS() && features.hugeMemory; }
simdWormholeEnabledModuleEnvironment100   bool simdWormholeEnabled() const { return features.simdWormhole; }
101 
isAsmJSModuleEnvironment102   bool isAsmJS() const { return kind == ModuleKind::AsmJS; }
103 
funcIsImportModuleEnvironment104   bool funcIsImport(uint32_t funcIndex) const {
105     return funcIndex < funcImportGlobalDataOffsets.length();
106   }
107 
usesMemoryModuleEnvironment108   bool usesMemory() const { return memory.isSome(); }
usesSharedMemoryModuleEnvironment109   bool usesSharedMemory() const {
110     return memory.isSome() && memory->isShared();
111   }
112 
declareFuncExportedModuleEnvironment113   void declareFuncExported(uint32_t funcIndex, bool eager, bool canRefFunc) {
114     FuncFlags flags = funcs[funcIndex].flags;
115 
116     // Set the `Exported` flag, if not set.
117     flags = FuncFlags(uint8_t(flags) | uint8_t(FuncFlags::Exported));
118 
119     // Merge in the `Eager` and `CanRefFunc` flags, if they're set. Be sure
120     // to not unset them if they've already been set.
121     if (eager) {
122       flags = FuncFlags(uint8_t(flags) | uint8_t(FuncFlags::Eager));
123     }
124     if (canRefFunc) {
125       flags = FuncFlags(uint8_t(flags) | uint8_t(FuncFlags::CanRefFunc));
126     }
127 
128     funcs[funcIndex].flags = flags;
129   }
130 };
131 
132 // ElemSegmentFlags provides methods for decoding and encoding the flags field
133 // of an element segment. This is needed as the flags field has a non-trivial
134 // encoding that is effectively split into independent `kind` and `payload`
135 // enums.
136 class ElemSegmentFlags {
137   enum class Flags : uint32_t {
138     Passive = 0x1,
139     WithIndexOrDeclared = 0x2,
140     ElemExpression = 0x4,
141     // Below this line are convenient combinations of flags
142     KindMask = Passive | WithIndexOrDeclared,
143     PayloadMask = ElemExpression,
144     AllFlags = Passive | WithIndexOrDeclared | ElemExpression,
145   };
146   uint32_t encoded_;
147 
ElemSegmentFlags(uint32_t encoded)148   explicit ElemSegmentFlags(uint32_t encoded) : encoded_(encoded) {}
149 
150  public:
ElemSegmentFlags(ElemSegmentKind kind,ElemSegmentPayload payload)151   ElemSegmentFlags(ElemSegmentKind kind, ElemSegmentPayload payload) {
152     encoded_ = uint32_t(kind) | uint32_t(payload);
153   }
154 
construct(uint32_t encoded)155   static Maybe<ElemSegmentFlags> construct(uint32_t encoded) {
156     if (encoded > uint32_t(Flags::AllFlags)) {
157       return Nothing();
158     }
159     return Some(ElemSegmentFlags(encoded));
160   }
161 
encoded()162   uint32_t encoded() const { return encoded_; }
163 
kind()164   ElemSegmentKind kind() const {
165     return static_cast<ElemSegmentKind>(encoded_ & uint32_t(Flags::KindMask));
166   }
payload()167   ElemSegmentPayload payload() const {
168     return static_cast<ElemSegmentPayload>(encoded_ &
169                                            uint32_t(Flags::PayloadMask));
170   }
171 };
172 
173 // OpIter specialized for validation.
174 
175 class NothingVector {
176   Nothing unused_;
177 
178  public:
resize(size_t length)179   bool resize(size_t length) { return true; }
180   Nothing& operator[](size_t) { return unused_; }
back()181   Nothing& back() { return unused_; }
182 };
183 
184 struct ValidatingPolicy {
185   using Value = Nothing;
186   using ValueVector = NothingVector;
187   using ControlItem = Nothing;
188 };
189 
190 template <typename Policy>
191 class OpIter;
192 
193 using ValidatingOpIter = OpIter<ValidatingPolicy>;
194 
195 // Shared subtyping function across validation.
196 
197 [[nodiscard]] bool CheckIsSubtypeOf(Decoder& d, const ModuleEnvironment& env,
198                                     size_t opcodeOffset, ValType actual,
199                                     ValType expected, TypeCache* cache);
200 
201 // The local entries are part of function bodies and thus serialized by both
202 // wasm and asm.js and decoded as part of both validation and compilation.
203 
204 [[nodiscard]] bool EncodeLocalEntries(Encoder& e, const ValTypeVector& locals);
205 
206 // This performs no validation; the local entries must already have been
207 // validated by an earlier pass.
208 
209 [[nodiscard]] bool DecodeValidatedLocalEntries(Decoder& d,
210                                                ValTypeVector* locals);
211 
212 // This validates the entries.
213 
214 [[nodiscard]] bool DecodeLocalEntries(Decoder& d, const TypeContext& types,
215                                       const FeatureArgs& features,
216                                       ValTypeVector* locals);
217 
218 // Returns whether the given [begin, end) prefix of a module's bytecode starts a
219 // code section and, if so, returns the SectionRange of that code section.
220 // Note that, even if this function returns 'false', [begin, end) may actually
221 // be a valid module in the special case when there are no function defs and the
222 // code section is not present. Such modules can be valid so the caller must
223 // handle this special case.
224 
225 [[nodiscard]] bool StartsCodeSection(const uint8_t* begin, const uint8_t* end,
226                                      SectionRange* codeSection);
227 
228 // Calling DecodeModuleEnvironment decodes all sections up to the code section
229 // and performs full validation of all those sections. The client must then
230 // decode the code section itself, reusing ValidateFunctionBody if necessary,
231 // and finally call DecodeModuleTail to decode all remaining sections after the
232 // code section (again, performing full validation).
233 
234 [[nodiscard]] bool DecodeModuleEnvironment(Decoder& d, ModuleEnvironment* env);
235 
236 [[nodiscard]] bool ValidateFunctionBody(const ModuleEnvironment& env,
237                                         uint32_t funcIndex, uint32_t bodySize,
238                                         Decoder& d);
239 
240 [[nodiscard]] bool DecodeModuleTail(Decoder& d, ModuleEnvironment* env);
241 
242 // Validate an entire module, returning true if the module was validated
243 // successfully. If Validate returns false:
244 //  - if *error is null, the caller should report out-of-memory
245 //  - otherwise, there was a legitimate error described by *error
246 
247 [[nodiscard]] bool Validate(JSContext* cx, const ShareableBytes& bytecode,
248                             const FeatureOptions& options, UniqueChars* error);
249 
250 }  // namespace wasm
251 }  // namespace js
252 
253 #endif  // namespace wasm_validate_h
254