WasmValidate.h (8302B)
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 "js/Utility.h" 23 #include "js/WasmFeatures.h" 24 25 #include "wasm/WasmBinary.h" 26 #include "wasm/WasmCompile.h" 27 #include "wasm/WasmCompileArgs.h" 28 #include "wasm/WasmModuleTypes.h" 29 #include "wasm/WasmOpIter.h" 30 #include "wasm/WasmProcess.h" 31 #include "wasm/WasmTypeDef.h" 32 33 namespace js { 34 namespace wasm { 35 36 // ElemSegmentFlags provides methods for decoding and encoding the flags field 37 // of an element segment. This is needed as the flags field has a non-trivial 38 // encoding that is effectively split into independent `kind` and `payload` 39 // enums. 40 class ElemSegmentFlags { 41 enum class Flags : uint32_t { 42 // 0 means active. 1 means (passive or declared), disambiguated by the next 43 // bit. 44 Passive = 0x1, 45 // For active segments, 1 means a table index is present. Otherwise, 0 means 46 // passive and 1 means declared. 47 TableIndexOrDeclared = 0x2, 48 // 0 means element kind / index (currently only func indexes). 1 means 49 // element ref type and initializer expressions. 50 ElemExpressions = 0x4, 51 52 // Below this line are convenient combinations of flags 53 KindMask = Passive | TableIndexOrDeclared, 54 PayloadMask = ElemExpressions, 55 AllFlags = Passive | TableIndexOrDeclared | ElemExpressions, 56 }; 57 uint32_t encoded_; 58 59 explicit ElemSegmentFlags(uint32_t encoded) : encoded_(encoded) {} 60 61 public: 62 ElemSegmentFlags(ElemSegmentKind kind, ElemSegmentPayload payload) { 63 encoded_ = uint32_t(kind) | uint32_t(payload); 64 } 65 66 static mozilla::Maybe<ElemSegmentFlags> construct(uint32_t encoded) { 67 if (encoded > uint32_t(Flags::AllFlags)) { 68 return mozilla::Nothing(); 69 } 70 return mozilla::Some(ElemSegmentFlags(encoded)); 71 } 72 73 uint32_t encoded() const { return encoded_; } 74 75 ElemSegmentKind kind() const { 76 return static_cast<ElemSegmentKind>(encoded_ & uint32_t(Flags::KindMask)); 77 } 78 ElemSegmentPayload payload() const { 79 return static_cast<ElemSegmentPayload>(encoded_ & 80 uint32_t(Flags::PayloadMask)); 81 } 82 }; 83 84 // OpIter specialized for validation. 85 86 class NothingVector { 87 mozilla::Nothing unused_; 88 89 public: 90 bool reserve(size_t size) { return true; } 91 bool resize(size_t length) { return true; } 92 mozilla::Nothing& operator[](size_t) { return unused_; } 93 mozilla::Nothing& back() { return unused_; } 94 size_t length() const { return 0; } 95 bool append(mozilla::Nothing& nothing) { return true; } 96 void infallibleAppend(mozilla::Nothing& nothing) {} 97 }; 98 99 struct ValidatingPolicy { 100 using Value = mozilla::Nothing; 101 using ValueVector = NothingVector; 102 using ControlItem = mozilla::Nothing; 103 }; 104 105 template <typename Policy> 106 class OpIter; 107 108 using ValidatingOpIter = OpIter<ValidatingPolicy>; 109 110 // Shared subtyping function across validation. 111 112 [[nodiscard]] bool CheckIsSubtypeOf(Decoder& d, const CodeMetadata& codeMeta, 113 size_t opcodeOffset, ResultType subType, 114 ResultType superType); 115 [[nodiscard]] bool CheckIsSubtypeOf(Decoder& d, const CodeMetadata& codeMeta, 116 size_t opcodeOffset, StorageType subType, 117 StorageType superType); 118 119 // The local entries are part of function bodies and thus serialized by both 120 // wasm and asm.js and decoded as part of both validation and compilation. 121 122 [[nodiscard]] bool EncodeLocalEntries(Encoder& e, const ValTypeVector& locals); 123 124 // This performs no validation; the local entries must already have been 125 // validated by an earlier pass. 126 127 [[nodiscard]] bool DecodeValidatedLocalEntries(const TypeContext& types, 128 Decoder& d, 129 ValTypeVector* locals); 130 131 // This validates the entries. Function params are inserted before the locals 132 // to generate the full local entries for use in validation 133 134 [[nodiscard]] bool DecodeLocalEntriesWithParams(Decoder& d, 135 const CodeMetadata& codeMeta, 136 uint32_t funcIndex, 137 ValTypeVector* locals); 138 139 // Returns whether the given [begin, end) prefix of a module's bytecode starts a 140 // code section and, if so, returns the BytecodeRange of that code section. 141 // Note that, even if this function returns 'false', [begin, end) may actually 142 // be a valid module in the special case when there are no function defs and the 143 // code section is not present. Such modules can be valid so the caller must 144 // handle this special case. 145 146 [[nodiscard]] bool StartsCodeSection(const uint8_t* begin, const uint8_t* end, 147 BytecodeRange* codeSection); 148 149 // Calling DecodeModuleEnvironment decodes all sections up to the code section 150 // and performs full validation of all those sections. The client must then 151 // decode the code section itself, reusing ValidateFunctionBody if necessary, 152 // and finally call DecodeModuleTail to decode all remaining sections after the 153 // code section (again, performing full validation). 154 155 [[nodiscard]] bool DecodeModuleEnvironment(Decoder& d, CodeMetadata* codeMeta, 156 ModuleMetadata* moduleMeta); 157 158 [[nodiscard]] bool ValidateFunctionBody(const CodeMetadata& codeMeta, 159 uint32_t funcIndex, uint32_t bodySize, 160 Decoder& d); 161 162 [[nodiscard]] bool DecodeModuleTail(Decoder& d, CodeMetadata* codeMeta, 163 ModuleMetadata* meta); 164 165 // Validate an entire module, returning true if the module was validated 166 // successfully. If Validate returns false: 167 // - if *error is null, the caller should report out-of-memory 168 // - otherwise, there was a legitimate error described by *error 169 170 [[nodiscard]] bool Validate(JSContext* cx, const BytecodeSource& bytecode, 171 const FeatureOptions& options, UniqueChars* error); 172 173 struct NopOpDumper { 174 void dumpOpBegin(OpBytes op) {} 175 void dumpOpEnd() {} 176 void dumpTypeIndex(uint32_t typeIndex, bool asTypeUse = false) {} 177 void dumpFuncIndex(uint32_t funcIndex) {} 178 void dumpTableIndex(uint32_t tableIndex) {} 179 void dumpGlobalIndex(uint32_t globalIndex) {} 180 void dumpMemoryIndex(uint32_t memoryIndex) {} 181 void dumpElemIndex(uint32_t elemIndex) {} 182 void dumpDataIndex(uint32_t dataIndex) {} 183 void dumpTagIndex(uint32_t tagIndex) {} 184 void dumpLocalIndex(uint32_t localIndex) {} 185 void dumpBlockType(BlockType type) {} 186 void dumpI32Const(int32_t constant) {} 187 void dumpI64Const(int64_t constant) {} 188 void dumpF32Const(float constant) {} 189 void dumpF64Const(double constant) {} 190 void dumpV128Const(V128 constant) {} 191 void dumpVectorMask(V128 mask) {} 192 void dumpRefType(RefType type) {} 193 void dumpHeapType(RefType type) {} 194 void dumpValType(ValType type) {} 195 void dumpTryTableCatches(const TryTableCatchVector& catches) {} 196 void dumpLinearMemoryAddress(LinearMemoryAddress<mozilla::Nothing> addr) {} 197 void dumpBlockDepth(uint32_t relativeDepth) {} 198 void dumpBlockDepths(const Uint32Vector& relativeDepths) {} 199 void dumpFieldIndex(uint32_t fieldIndex) {} 200 void dumpNumElements(uint32_t numElements) {} 201 void dumpLaneIndex(uint32_t laneIndex) {} 202 203 void startScope() {}; 204 void endScope() {}; 205 }; 206 207 template <class T> 208 [[nodiscard]] bool ValidateOps(ValidatingOpIter& iter, T& dumper, 209 const CodeMetadata& codeMeta); 210 211 } // namespace wasm 212 } // namespace js 213 214 #endif // namespace wasm_validate_h