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