/src/WasmEdge/lib/validator/validator.cpp
Line | Count | Source |
1 | | // SPDX-License-Identifier: Apache-2.0 |
2 | | // SPDX-FileCopyrightText: Copyright The WasmEdge Authors |
3 | | |
4 | | #include "validator/validator.h" |
5 | | |
6 | | #include "ast/section.h" |
7 | | #include "common/errinfo.h" |
8 | | #include "common/hash.h" |
9 | | |
10 | | #include <map> |
11 | | #include <numeric> |
12 | | #include <string> |
13 | | #include <unordered_set> |
14 | | #include <vector> |
15 | | |
16 | | using namespace std::literals; |
17 | | |
18 | | namespace WasmEdge { |
19 | | namespace Validator { |
20 | | |
21 | | namespace { |
22 | | |
23 | | static constexpr uint32_t MaxSubtypeDepth = 63; |
24 | | static constexpr uint32_t Unvisited = UINT32_MAX; |
25 | | static constexpr uint32_t Visiting = UINT32_MAX - 1; |
26 | | |
27 | | // TODO: make the super type depth table instead of recursively querying. |
28 | | Expect<uint32_t> |
29 | | checkSubtypeDepth(const uint32_t BaseIdx, uint32_t TestIdx, uint32_t Depth, |
30 | | std::vector<uint32_t> &DepthMap, |
31 | 4.46k | const std::vector<const WasmEdge::AST::SubType *> &TypeVec) { |
32 | 4.46k | if (TestIdx >= DepthMap.size()) { |
33 | 0 | DepthMap.resize(TestIdx + 1, Unvisited); |
34 | 4.46k | } else if (DepthMap[TestIdx] == Visiting) { |
35 | 0 | spdlog::error(ErrCode::Value::InvalidSubType); |
36 | 0 | spdlog::error(" Cycle detected in subtype hierarchy for type {}."sv, |
37 | 0 | BaseIdx); |
38 | 0 | return Unexpect(ErrCode::Value::InvalidSubType); |
39 | 4.46k | } else if (DepthMap[TestIdx] != Unvisited) { |
40 | 3.41k | return DepthMap[TestIdx]; |
41 | 3.41k | } |
42 | | |
43 | 1.05k | if (Depth >= MaxSubtypeDepth) { |
44 | 0 | spdlog::error(ErrCode::Value::InvalidSubType); |
45 | 0 | spdlog::error(" Subtype depth for type {} exceeded the limits of {}"sv, |
46 | 0 | BaseIdx, MaxSubtypeDepth); |
47 | 0 | return Unexpect(ErrCode::Value::InvalidSubType); |
48 | 0 | } |
49 | | |
50 | 1.05k | DepthMap[TestIdx] = Visiting; |
51 | 1.05k | uint32_t MaxDepth = 0; |
52 | | // A component core:type index may name a module type, which is not a sub |
53 | | // type and holds no place in the hierarchy. |
54 | 1.05k | const auto *TestType = TypeVec[TestIdx]; |
55 | 1.05k | if (unlikely(TestType == nullptr)) { |
56 | 0 | spdlog::error(ErrCode::Value::InvalidSubType); |
57 | 0 | spdlog::error(" Type index {} does not refer to a sub type."sv, TestIdx); |
58 | 0 | return Unexpect(ErrCode::Value::InvalidSubType); |
59 | 0 | } |
60 | 1.05k | for (const auto SuperIdx : TestType->getSuperTypeIndices()) { |
61 | 469 | if (unlikely(SuperIdx >= TypeVec.size())) { |
62 | 0 | spdlog::error(ErrCode::Value::InvalidSubType); |
63 | 0 | spdlog::error(ErrInfo::InfoForbidIndex( |
64 | 0 | ErrInfo::IndexCategory::DefinedType, SuperIdx, |
65 | 0 | static_cast<uint32_t>(TypeVec.size()))); |
66 | 0 | return Unexpect(ErrCode::Value::InvalidSubType); |
67 | 0 | } |
68 | 938 | EXPECTED_TRY( |
69 | 938 | auto RetDepth, |
70 | 938 | checkSubtypeDepth(BaseIdx, SuperIdx, Depth + 1, DepthMap, TypeVec) |
71 | 938 | .map_error([=](auto E) { |
72 | 938 | spdlog::error( |
73 | 938 | " When checking subtype hierarchy of super type {}."sv, |
74 | 938 | SuperIdx); |
75 | 938 | return E; |
76 | 938 | })); |
77 | 938 | MaxDepth = std::max(MaxDepth, RetDepth + 1); |
78 | 938 | } |
79 | | |
80 | 1.05k | if (MaxDepth >= MaxSubtypeDepth) { |
81 | 0 | spdlog::error(ErrCode::Value::InvalidSubType); |
82 | 0 | spdlog::error(" Subtype depth for type {} exceeded the limits of {}"sv, |
83 | 0 | BaseIdx, MaxSubtypeDepth); |
84 | 0 | return Unexpect(ErrCode::Value::InvalidSubType); |
85 | 0 | } |
86 | | |
87 | 1.05k | DepthMap[TestIdx] = MaxDepth; |
88 | 1.05k | return MaxDepth; |
89 | 1.05k | } |
90 | | |
91 | | // Set the canonical indices of a rec type from the first with the same key. |
92 | | void setCanonicalIndices(Span<const AST::SubType> RecType, uint32_t BaseIdx, |
93 | | const std::vector<const AST::SubType *> &TypeVec, |
94 | 16.3k | std::map<std::vector<uint32_t>, uint32_t> &RecTypes) { |
95 | 16.3k | std::vector<uint32_t> Key; |
96 | 16.3k | auto AddTypeIdx = [&](uint32_t Idx) { |
97 | 13.4k | if (Idx >= BaseIdx && Idx - BaseIdx < RecType.size()) { |
98 | 10.0k | Key.push_back(0U); |
99 | 10.0k | Key.push_back(Idx - BaseIdx); |
100 | 10.0k | } else { |
101 | 3.34k | Key.push_back(1U); |
102 | 3.34k | Key.push_back(TypeVec[Idx]->getCanonicalIndex().value_or(Idx)); |
103 | 3.34k | } |
104 | 13.4k | }; |
105 | 122k | auto AddValType = [&](const ValType &VT) { |
106 | 122k | Key.push_back(static_cast<uint32_t>(VT.getCode())); |
107 | 122k | Key.push_back(static_cast<uint32_t>(VT.getHeapTypeCode())); |
108 | 122k | if (VT.getHeapTypeCode() == TypeCode::TypeIndex) { |
109 | 11.0k | AddTypeIdx(VT.getTypeIndex()); |
110 | 11.0k | } |
111 | 122k | }; |
112 | | |
113 | 16.3k | Key.push_back(static_cast<uint32_t>(RecType.size())); |
114 | 31.0k | for (const auto &Type : RecType) { |
115 | 31.0k | const auto &CompType = Type.getCompositeType(); |
116 | 31.0k | Key.push_back(Type.isFinal() ? 1U : 0U); |
117 | 31.0k | Key.push_back(static_cast<uint32_t>(Type.getSuperTypeIndices().size())); |
118 | 31.0k | for (const auto Idx : Type.getSuperTypeIndices()) { |
119 | 2.40k | AddTypeIdx(Idx); |
120 | 2.40k | } |
121 | 31.0k | Key.push_back(static_cast<uint32_t>(CompType.getContentTypeCode())); |
122 | 31.0k | if (CompType.isFunc()) { |
123 | 17.1k | const auto &FType = CompType.getFuncType(); |
124 | 17.1k | Key.push_back(static_cast<uint32_t>(FType.getParamTypes().size())); |
125 | 45.7k | for (const auto &VT : FType.getParamTypes()) { |
126 | 45.7k | AddValType(VT); |
127 | 45.7k | } |
128 | 17.1k | Key.push_back(static_cast<uint32_t>(FType.getReturnTypes().size())); |
129 | 43.5k | for (const auto &VT : FType.getReturnTypes()) { |
130 | 43.5k | AddValType(VT); |
131 | 43.5k | } |
132 | 17.1k | } else { |
133 | 13.8k | const auto &FTypes = CompType.getFieldTypes(); |
134 | 13.8k | Key.push_back(static_cast<uint32_t>(FTypes.size())); |
135 | 33.5k | for (const auto &FType : FTypes) { |
136 | 33.5k | Key.push_back(static_cast<uint32_t>(FType.getValMut())); |
137 | 33.5k | AddValType(FType.getStorageType()); |
138 | 33.5k | } |
139 | 13.8k | } |
140 | 31.0k | } |
141 | 16.3k | const uint32_t CanonIdx = |
142 | 16.3k | RecTypes.try_emplace(std::move(Key), BaseIdx).first->second; |
143 | 47.4k | for (uint32_t I = 0; I < RecType.size(); I++) { |
144 | 31.0k | const_cast<AST::SubType &>(RecType[I]).setCanonicalIndex(CanonIdx + I); |
145 | 31.0k | } |
146 | 16.3k | } |
147 | | |
148 | | } // namespace |
149 | | |
150 | | // Validate Module. See "include/validator/validator.h". |
151 | 8.74k | Expect<void> Validator::validate(const AST::Module &Mod) { |
152 | | // https://webassembly.github.io/spec/core/valid/modules.html |
153 | 8.74k | Checker.reset(true); |
154 | | |
155 | | // Validate and register type section. |
156 | 8.74k | EXPECTED_TRY(validate(Mod.getTypeSection()).map_error([](auto E) { |
157 | 8.58k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Type)); |
158 | 8.58k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
159 | 8.58k | return E; |
160 | 8.58k | })); |
161 | | |
162 | | // Validate and register the import section in FormChecker. |
163 | 8.58k | EXPECTED_TRY(validate(Mod.getImportSection()).map_error([](auto E) { |
164 | 8.50k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Import)); |
165 | 8.50k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
166 | 8.50k | return E; |
167 | 8.50k | })); |
168 | | |
169 | | // Validate the function section and register functions in FormChecker. |
170 | 8.50k | EXPECTED_TRY(validate(Mod.getFunctionSection()).map_error([](auto E) { |
171 | 8.46k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Function)); |
172 | 8.46k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
173 | 8.46k | return E; |
174 | 8.46k | })); |
175 | | |
176 | | // Validate the table section and register tables in FormChecker. |
177 | 8.46k | EXPECTED_TRY(validate(Mod.getTableSection()).map_error([](auto E) { |
178 | 8.28k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Table)); |
179 | 8.28k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
180 | 8.28k | return E; |
181 | 8.28k | })); |
182 | | |
183 | | // Validate the memory section and register memories in FormChecker. |
184 | 8.28k | EXPECTED_TRY(validate(Mod.getMemorySection()).map_error([](auto E) { |
185 | 8.01k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Memory)); |
186 | 8.01k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
187 | 8.01k | return E; |
188 | 8.01k | })); |
189 | | |
190 | | // Validate the global section and register globals in FormChecker. |
191 | 8.01k | EXPECTED_TRY(validate(Mod.getGlobalSection()).map_error([](auto E) { |
192 | 7.69k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Global)); |
193 | 7.69k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
194 | 7.69k | return E; |
195 | 7.69k | })); |
196 | | |
197 | | // Validate the tag section and register tags in FormChecker. |
198 | 7.69k | EXPECTED_TRY(validate(Mod.getTagSection()).map_error([](auto E) { |
199 | 7.65k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Tag)); |
200 | 7.65k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
201 | 7.65k | return E; |
202 | 7.65k | })); |
203 | | |
204 | | // Validate export section. |
205 | 7.65k | EXPECTED_TRY(validate(Mod.getExportSection()).map_error([](auto E) { |
206 | 7.39k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Export)); |
207 | 7.39k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
208 | 7.39k | return E; |
209 | 7.39k | })); |
210 | | |
211 | | // Validate start section. |
212 | 7.39k | EXPECTED_TRY(validate(Mod.getStartSection()).map_error([](auto E) { |
213 | 7.33k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Start)); |
214 | 7.33k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
215 | 7.33k | return E; |
216 | 7.33k | })); |
217 | | |
218 | | // Validate the element section that initializes tables. |
219 | 7.33k | EXPECTED_TRY(validate(Mod.getElementSection()).map_error([](auto E) { |
220 | 7.18k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Element)); |
221 | 7.18k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
222 | 7.18k | return E; |
223 | 7.18k | })); |
224 | | |
225 | | // Validate the data section that initializes memories. |
226 | 7.18k | EXPECTED_TRY(validate(Mod.getDataSection()).map_error([](auto E) { |
227 | 7.15k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Data)); |
228 | 7.15k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
229 | 7.15k | return E; |
230 | 7.15k | })); |
231 | | |
232 | | // Validate code section and expressions. |
233 | 7.15k | EXPECTED_TRY(validate(Mod.getCodeSection()).map_error([](auto E) { |
234 | 2.92k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Code)); |
235 | 2.92k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
236 | 2.92k | return E; |
237 | 2.92k | })); |
238 | | |
239 | | // Multiple tables are for the ReferenceTypes proposal. |
240 | 2.92k | if (Checker.getTables().size() > 1 && |
241 | 66 | !Conf.hasProposal(Proposal::ReferenceTypes)) { |
242 | 0 | spdlog::error(ErrCode::Value::MultiTables); |
243 | 0 | spdlog::error(ErrInfo::InfoProposal(Proposal::ReferenceTypes)); |
244 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
245 | 0 | return Unexpect(ErrCode::Value::MultiTables); |
246 | 0 | } |
247 | | |
248 | | // Multiple memories are for the MultiMemories proposal. |
249 | 2.92k | if (Checker.getMemories().size() > 1 && |
250 | 82 | !Conf.hasProposal(Proposal::MultiMemories)) { |
251 | 0 | spdlog::error(ErrCode::Value::MultiMemories); |
252 | 0 | spdlog::error(ErrInfo::InfoProposal(Proposal::MultiMemories)); |
253 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
254 | 0 | return Unexpect(ErrCode::Value::MultiMemories); |
255 | 0 | } |
256 | | |
257 | | // Set the validated flag. |
258 | 2.92k | const_cast<AST::Module &>(Mod).setIsValidated(); |
259 | 2.92k | return {}; |
260 | 2.92k | } |
261 | | |
262 | | // Validate Sub type. See "include/validator/validator.h". |
263 | | Expect<void> |
264 | | Validator::validate(const AST::SubType &Type, uint32_t OwnTypeIdx, |
265 | | std::vector<uint32_t> &SubTypeDepthMap, |
266 | 84.7k | const std::vector<const AST::SubType *> &TypeVec) { |
267 | 84.7k | const auto &CompType = Type.getCompositeType(); |
268 | | // A concrete heap type reference must name a defined type of this space. |
269 | 147k | auto CheckValType = [&TypeVec](const ValType &VT) -> Expect<void> { |
270 | 147k | if (VT.isRefType() && VT.getHeapTypeCode() == TypeCode::TypeIndex && |
271 | 14.3k | (VT.getTypeIndex() >= TypeVec.size() || |
272 | 14.3k | TypeVec[VT.getTypeIndex()] == nullptr)) { |
273 | 73 | spdlog::error(ErrCode::Value::InvalidFuncTypeIdx); |
274 | 73 | spdlog::error(ErrInfo::InfoForbidIndex( |
275 | 73 | ErrInfo::IndexCategory::FunctionType, VT.getTypeIndex(), |
276 | 73 | static_cast<uint32_t>(TypeVec.size()))); |
277 | 73 | return Unexpect(ErrCode::Value::InvalidFuncTypeIdx); |
278 | 73 | } |
279 | 147k | return {}; |
280 | 147k | }; |
281 | | |
282 | | // Check the validation of the composite type. |
283 | 84.7k | if (CompType.isFunc()) { |
284 | 20.9k | const auto &FType = CompType.getFuncType(); |
285 | 54.3k | for (auto &PType : FType.getParamTypes()) { |
286 | 54.3k | EXPECTED_TRY(CheckValType(PType).map_error([](auto E) { |
287 | 54.3k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Function)); |
288 | 54.3k | return E; |
289 | 54.3k | })); |
290 | 54.3k | } |
291 | 20.9k | if (unlikely(!Conf.hasProposal(Proposal::MultiValue)) && |
292 | 0 | FType.getReturnTypes().size() > 1) { |
293 | 0 | spdlog::error(ErrCode::Value::InvalidResultArity); |
294 | 0 | spdlog::error(ErrInfo::InfoProposal(Proposal::MultiValue)); |
295 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Function)); |
296 | 0 | return Unexpect(ErrCode::Value::InvalidResultArity); |
297 | 0 | } |
298 | 51.1k | for (auto &RType : FType.getReturnTypes()) { |
299 | 51.1k | EXPECTED_TRY(CheckValType(RType).map_error([](auto E) { |
300 | 51.1k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Function)); |
301 | 51.1k | return E; |
302 | 51.1k | })); |
303 | 51.1k | } |
304 | 63.8k | } else { |
305 | 63.8k | const auto &FTypes = CompType.getFieldTypes(); |
306 | 63.8k | for (auto &FieldType : FTypes) { |
307 | 42.0k | EXPECTED_TRY(CheckValType(FieldType.getStorageType())); |
308 | 42.0k | } |
309 | 63.8k | } |
310 | | |
311 | | // In the current version, the length of the type index vector will be <= 1. |
312 | 84.7k | if (Type.getSuperTypeIndices().size() > 1) { |
313 | 4 | spdlog::error(ErrCode::Value::MultipleSuperTypes); |
314 | 4 | return Unexpect(ErrCode::Value::MultipleSuperTypes); |
315 | 4 | } |
316 | | |
317 | 84.7k | for (const auto &Index : Type.getSuperTypeIndices()) { |
318 | | // A super type must be previously defined (smaller index than this sub |
319 | | // type), so OwnTypeIdx is the exclusive bound, subsuming the range check. |
320 | 4.00k | if (unlikely(Index >= OwnTypeIdx)) { |
321 | 13 | spdlog::error(ErrCode::Value::InvalidSubType); |
322 | 13 | spdlog::error(" Super type index {} must be smaller than the sub type " |
323 | 13 | "index {}."sv, |
324 | 13 | Index, OwnTypeIdx); |
325 | 13 | return Unexpect(ErrCode::Value::InvalidSubType); |
326 | 13 | } |
327 | | |
328 | 3.99k | EXPECTED_TRY( |
329 | 3.99k | checkSubtypeDepth(Index, Index, 0, SubTypeDepthMap, TypeVec) |
330 | 3.99k | .map_error([=](auto E) { |
331 | 3.99k | spdlog::error( |
332 | 3.99k | " When checking subtype hierarchy of super type {}."sv, |
333 | 3.99k | Index); |
334 | 3.99k | return E; |
335 | 3.99k | })); |
336 | | |
337 | 3.99k | if (TypeVec[Index]->isFinal()) { |
338 | 9 | spdlog::error(ErrCode::Value::InvalidSubType); |
339 | 9 | spdlog::error(" Super type should not be final."sv); |
340 | 9 | return Unexpect(ErrCode::Value::InvalidSubType); |
341 | 9 | } |
342 | 3.99k | } |
343 | 84.6k | return {}; |
344 | 84.7k | } |
345 | | |
346 | | // Validate Rec type. See "include/validator/validator.h". |
347 | | Expect<void> |
348 | | Validator::validate(Span<const AST::SubType> RecType, uint32_t BaseIdx, |
349 | | std::vector<uint32_t> &SubTypeDepthMap, |
350 | 66.3k | const std::vector<const AST::SubType *> &TypeVec) { |
351 | | // Check all the members before matching, which walks their super types. |
352 | 151k | for (uint32_t I = 0; I < RecType.size(); I++) { |
353 | 84.7k | EXPECTED_TRY(validate(RecType[I], BaseIdx + I, SubTypeDepthMap, TypeVec)); |
354 | 84.7k | } |
355 | 82.0k | for (const auto &Type : RecType) { |
356 | 82.0k | for (const auto &Index : Type.getSuperTypeIndices()) { |
357 | 2.70k | if (!AST::TypeMatcher::matchType(TypeVec, |
358 | 2.70k | TypeVec[Index]->getCompositeType(), |
359 | 2.70k | Type.getCompositeType())) { |
360 | 63 | spdlog::error(ErrCode::Value::InvalidSubType); |
361 | 63 | spdlog::error(" Super type not matched."sv); |
362 | 63 | return Unexpect(ErrCode::Value::InvalidSubType); |
363 | 63 | } |
364 | 2.70k | } |
365 | 82.0k | } |
366 | 66.1k | return {}; |
367 | 66.2k | } |
368 | | |
369 | | // Validate Limit type. See "include/validator/validator.h". |
370 | 13.5k | Expect<void> Validator::validate(const AST::Limit &Lim) { |
371 | 13.5k | if (Lim.hasMax() && Lim.getMin() > Lim.getMax()) { |
372 | 95 | spdlog::error(ErrCode::Value::InvalidLimit); |
373 | 95 | spdlog::error(ErrInfo::InfoLimit(Lim.hasMax(), Lim.getMin(), Lim.getMax())); |
374 | 95 | return Unexpect(ErrCode::Value::InvalidLimit); |
375 | 95 | } |
376 | 13.4k | if (Lim.isShared() && unlikely(!Lim.hasMax())) { |
377 | 0 | spdlog::error(ErrCode::Value::SharedMemoryNoMax); |
378 | 0 | return Unexpect(ErrCode::Value::SharedMemoryNoMax); |
379 | 0 | } |
380 | 13.4k | return {}; |
381 | 13.4k | } |
382 | | |
383 | | // Validate Table type. See "include/validator/validator.h". |
384 | 5.65k | Expect<void> Validator::validate(const AST::TableType &Tab) { |
385 | | // Validate value type. |
386 | 5.65k | EXPECTED_TRY(Checker.validate(Tab.getRefType())); |
387 | | // Validate table limits. |
388 | 5.61k | const auto &Lim = Tab.getLimit(); |
389 | 5.61k | EXPECTED_TRY(validate(Lim).map_error([](auto E) { |
390 | 5.58k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Limit)); |
391 | 5.58k | return E; |
392 | 5.58k | })); |
393 | 5.58k | uint64_t Range = getMaxAddress(Lim.getAddrType()); |
394 | 5.58k | if (Lim.getMin() > Range || (Lim.hasMax() && Lim.getMax() > Range)) { |
395 | | // Since spec test has no related error message, use this error instead. |
396 | 123 | auto Code = Conf.hasProposal(Proposal::Memory64) |
397 | 123 | ? ErrCode::Value::InvalidTableSize64 |
398 | 123 | : ErrCode::Value::InvalidLimit; |
399 | 123 | spdlog::error(Code); |
400 | 123 | spdlog::error(ErrInfo::InfoLimit(Lim.hasMax(), Lim.getMin(), Lim.getMax())); |
401 | 123 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Limit)); |
402 | 123 | return Unexpect(Code); |
403 | 123 | } |
404 | 5.46k | return {}; |
405 | 5.58k | } |
406 | | |
407 | | // Validate Memory type. See "include/validator/validator.h". |
408 | 7.96k | Expect<void> Validator::validate(const AST::MemoryType &Mem) { |
409 | | // Validate memory limits. |
410 | 7.96k | const auto &Lim = Mem.getLimit(); |
411 | 7.96k | EXPECTED_TRY(validate(Lim).map_error([](auto E) { |
412 | 7.89k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Limit)); |
413 | 7.89k | return E; |
414 | 7.89k | })); |
415 | 7.89k | if (!Conf.hasProposal(Proposal::Memory64) && Lim.is64()) { |
416 | 0 | spdlog::error(ErrCode::Value::InvalidLimit); |
417 | 0 | spdlog::error(ErrInfo::InfoProposal(Proposal::Memory64)); |
418 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Limit)); |
419 | 0 | return Unexpect(ErrCode::Value::InvalidLimit); |
420 | 0 | } |
421 | 7.89k | uint64_t Range = Lim.is32() ? (static_cast<uint64_t>(1) << 16) |
422 | 7.89k | : (static_cast<uint64_t>(1) << 48); |
423 | 7.89k | if (Lim.getMin() > Range || (Lim.hasMax() && Lim.getMax() > Range)) { |
424 | 209 | auto Code = Conf.hasProposal(Proposal::Memory64) |
425 | 209 | ? ErrCode::Value::InvalidMemPages64 |
426 | 209 | : ErrCode::Value::InvalidMemPages; |
427 | 209 | spdlog::error(Code); |
428 | 209 | spdlog::error(ErrInfo::InfoLimit(Lim.hasMax(), Lim.getMin(), Lim.getMax())); |
429 | 209 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Limit)); |
430 | 209 | return Unexpect(Code); |
431 | 209 | } |
432 | 7.68k | return {}; |
433 | 7.89k | } |
434 | | |
435 | | // Validate Global type. See "include/validator/validator.h". |
436 | 2.87k | Expect<void> Validator::validate(const AST::GlobalType &Glob) { |
437 | | // Validate value type. |
438 | 2.87k | return Checker.validate(Glob.getValType()); |
439 | 2.87k | } |
440 | | |
441 | | // Validate Table segment. See "include/validator/validator.h". |
442 | 5.21k | Expect<void> Validator::validate(const AST::TableSegment &TabSeg) { |
443 | 5.21k | if (TabSeg.getExpr().getInstrs().size() > 0) { |
444 | | // Check ref initialization is a const expression. |
445 | 158 | EXPECTED_TRY( |
446 | 158 | validateConstExpr(TabSeg.getExpr().getInstrs(), |
447 | 158 | {ValType(TabSeg.getTableType().getRefType())}) |
448 | 158 | .map_error([](auto E) { |
449 | 158 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Expression)); |
450 | 158 | return E; |
451 | 158 | })); |
452 | 5.05k | } else { |
453 | | // No init expression. Check that the reference type is nullable. |
454 | 5.05k | if (!TabSeg.getTableType().getRefType().isNullableRefType()) { |
455 | 3 | spdlog::error(ErrCode::Value::TypeCheckFailed); |
456 | 3 | spdlog::error(ErrInfo::InfoMismatch( |
457 | 3 | ValType(TypeCode::RefNull, |
458 | 3 | TabSeg.getTableType().getRefType().getHeapTypeCode(), |
459 | 3 | TabSeg.getTableType().getRefType().getTypeIndex()), |
460 | 3 | TabSeg.getTableType().getRefType())); |
461 | 3 | return Unexpect(ErrCode::Value::TypeCheckFailed); |
462 | 3 | } |
463 | 5.05k | } |
464 | | // Validate table type. |
465 | 5.20k | return validate(TabSeg.getTableType()).map_error([](auto E) { |
466 | 179 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Table)); |
467 | 179 | return E; |
468 | 179 | }); |
469 | 5.21k | } |
470 | | |
471 | | // Validate Global segment. See "include/validator/validator.h". |
472 | 2.28k | Expect<void> Validator::validate(const AST::GlobalSegment &GlobSeg) { |
473 | | // Check global initialization is a const expression. |
474 | 2.28k | EXPECTED_TRY(validateConstExpr(GlobSeg.getExpr().getInstrs(), |
475 | 1.95k | {GlobSeg.getGlobalType().getValType()}) |
476 | 1.95k | .map_error([](auto E) { |
477 | 1.95k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Expression)); |
478 | 1.95k | return E; |
479 | 1.95k | })); |
480 | | // Validate global type. |
481 | 1.95k | return validate(GlobSeg.getGlobalType()).map_error([](auto E) { |
482 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Global)); |
483 | 0 | return E; |
484 | 0 | }); |
485 | 2.28k | } |
486 | | |
487 | | // Validate Element segment. See "include/validator/validator.h". |
488 | 1.30k | Expect<void> Validator::validate(const AST::ElementSegment &ElemSeg) { |
489 | | // Check that initialization expressions are const expressions. |
490 | 2.61k | for (auto &Expr : ElemSeg.getInitExprs()) { |
491 | 2.61k | EXPECTED_TRY( |
492 | 2.61k | validateConstExpr(Expr.getInstrs(), {ValType(ElemSeg.getRefType())}) |
493 | 2.61k | .map_error([](auto E) { |
494 | 2.61k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Expression)); |
495 | 2.61k | return E; |
496 | 2.61k | })); |
497 | 2.61k | } |
498 | | |
499 | | // The reference type should be valid. |
500 | 1.21k | EXPECTED_TRY(Checker.validate(ElemSeg.getRefType())); |
501 | | |
502 | | // Passive and declarative cases are valid with a valid reference type. |
503 | 1.19k | if (ElemSeg.getMode() == AST::ElementSegment::ElemMode::Active) { |
504 | | // Check table index and reference type in context. |
505 | 542 | const auto &TableVec = Checker.getTables(); |
506 | 542 | if (ElemSeg.getIdx() >= TableVec.size()) { |
507 | 25 | spdlog::error(ErrCode::Value::InvalidTableIdx); |
508 | 25 | spdlog::error(ErrInfo::InfoForbidIndex( |
509 | 25 | ErrInfo::IndexCategory::Table, ElemSeg.getIdx(), |
510 | 25 | static_cast<uint32_t>(TableVec.size()))); |
511 | 25 | return Unexpect(ErrCode::Value::InvalidTableIdx); |
512 | 25 | } |
513 | 517 | if (!AST::TypeMatcher::matchType(Checker.getTypes(), |
514 | 517 | TableVec[ElemSeg.getIdx()].second, |
515 | 517 | ElemSeg.getRefType())) { |
516 | | // Reference type does not match. |
517 | 10 | spdlog::error(ErrCode::Value::TypeCheckFailed); |
518 | 10 | spdlog::error(ErrInfo::InfoMismatch(TableVec[ElemSeg.getIdx()].second, |
519 | 10 | ElemSeg.getRefType())); |
520 | 10 | return Unexpect(ErrCode::Value::TypeCheckFailed); |
521 | 10 | } |
522 | | // Check table initialization is a const expression. |
523 | 507 | return validateConstExpr(ElemSeg.getExpr().getInstrs(), |
524 | 507 | {ValType(TableVec[ElemSeg.getIdx()].first)}) |
525 | 507 | .map_error([](auto E) { |
526 | 10 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Expression)); |
527 | 10 | return E; |
528 | 10 | }); |
529 | 517 | } |
530 | 656 | return {}; |
531 | 1.19k | } |
532 | | |
533 | | // Validate Code segment. See "include/validator/validator.h". |
534 | | Expect<void> Validator::validate(const AST::CodeSegment &CodeSeg, |
535 | 19.5k | const uint32_t TypeIdx) { |
536 | | // Due to validation of the function section, the type at this index must |
537 | | // be a function type. |
538 | 19.5k | const auto &FuncType = |
539 | 19.5k | Checker.getTypes()[TypeIdx]->getCompositeType().getFuncType(); |
540 | | // Reset stack in FormChecker. |
541 | 19.5k | Checker.reset(); |
542 | | // Add parameters to this frame. |
543 | 49.3k | for (auto &Type : FuncType.getParamTypes()) { |
544 | | // Local passed by function parameters must have been initialized. |
545 | 49.3k | Checker.addLocal(Type, true); |
546 | 49.3k | } |
547 | | // Add locals to this frame. |
548 | 19.5k | for (auto Val : CodeSeg.getLocals()) { |
549 | 825M | for (uint32_t Cnt = 0; Cnt < Val.first; ++Cnt) { |
550 | | // The local value type should be valid. |
551 | 825M | EXPECTED_TRY(Checker.validate(Val.second)); |
552 | 825M | Checker.addLocal(Val.second, false); |
553 | 825M | } |
554 | 13.9k | } |
555 | | // Validate function body expression. |
556 | 19.5k | return Checker |
557 | 19.5k | .validate(CodeSeg.getExpr().getInstrs(), FuncType.getReturnTypes()) |
558 | 19.5k | .map_error([](auto E) { |
559 | 4.20k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Expression)); |
560 | 4.20k | return E; |
561 | 4.20k | }); |
562 | 19.5k | } |
563 | | |
564 | | // Validate Data segment. See "include/validator/validator.h". |
565 | 2.33k | Expect<void> Validator::validate(const AST::DataSegment &DataSeg) { |
566 | 2.33k | switch (DataSeg.getMode()) { |
567 | 289 | case AST::DataSegment::DataMode::Active: { |
568 | | // Check memory index in context. |
569 | 289 | const auto &MemVec = Checker.getMemories(); |
570 | 289 | if (DataSeg.getIdx() >= MemVec.size()) { |
571 | 24 | spdlog::error(ErrCode::Value::InvalidMemoryIdx); |
572 | 24 | spdlog::error(ErrInfo::InfoForbidIndex( |
573 | 24 | ErrInfo::IndexCategory::Memory, DataSeg.getIdx(), |
574 | 24 | static_cast<uint32_t>(MemVec.size()))); |
575 | 24 | return Unexpect(ErrCode::Value::InvalidMemoryIdx); |
576 | 24 | } |
577 | | // Check memory initialization is a const expression. |
578 | 265 | return validateConstExpr(DataSeg.getExpr().getInstrs(), |
579 | 265 | {ValType(MemVec[DataSeg.getIdx()])}) |
580 | 265 | .map_error([](auto E) { |
581 | 11 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Expression)); |
582 | 11 | return E; |
583 | 11 | }); |
584 | 289 | } |
585 | 2.04k | case AST::DataSegment::DataMode::Passive: |
586 | | // Passive case is always valid. |
587 | 2.04k | return {}; |
588 | 0 | default: |
589 | 0 | return {}; |
590 | 2.33k | } |
591 | 2.33k | } |
592 | | |
593 | | // Validate Import description. See "include/validator/validator.h". |
594 | 4.52k | Expect<void> Validator::validate(const AST::ImportDesc &ImpDesc) { |
595 | 4.52k | switch (ImpDesc.getExternalType()) { |
596 | | // External type and external content are ensured to match in the loader |
597 | | // phase. |
598 | 1.07k | case ExternalType::Function: { |
599 | 1.07k | const auto TId = ImpDesc.getExternalFuncTypeIdx(); |
600 | | // Function type index must exist in context and be valid. |
601 | 1.07k | if (TId >= Checker.getTypes().size()) { |
602 | 48 | spdlog::error(ErrCode::Value::InvalidFuncTypeIdx); |
603 | 48 | spdlog::error(ErrInfo::InfoForbidIndex( |
604 | 48 | ErrInfo::IndexCategory::FunctionType, TId, |
605 | 48 | static_cast<uint32_t>(Checker.getTypes().size()))); |
606 | 48 | return Unexpect(ErrCode::Value::InvalidFuncTypeIdx); |
607 | 48 | } |
608 | 1.02k | if (!Checker.getTypes()[TId]->getCompositeType().isFunc()) { |
609 | 4 | spdlog::error(ErrCode::Value::InvalidFuncTypeIdx); |
610 | 4 | spdlog::error(" Defined type index {} is not a function type."sv, TId); |
611 | 4 | return Unexpect(ErrCode::Value::InvalidFuncTypeIdx); |
612 | 4 | } |
613 | 1.02k | Checker.addFunc(TId, true); |
614 | 1.02k | return {}; |
615 | 1.02k | } |
616 | 401 | case ExternalType::Table: { |
617 | 401 | const auto &TabType = ImpDesc.getExternalTableType(); |
618 | | // Table type must be valid. |
619 | 401 | EXPECTED_TRY(validate(TabType).map_error([](auto E) { |
620 | 396 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Table)); |
621 | 396 | return E; |
622 | 396 | })); |
623 | 396 | Checker.addTable(TabType); |
624 | 396 | return {}; |
625 | 401 | } |
626 | 1.43k | case ExternalType::Memory: { |
627 | 1.43k | const auto &MemType = ImpDesc.getExternalMemoryType(); |
628 | | // Memory type must be valid. |
629 | 1.43k | EXPECTED_TRY(validate(MemType).map_error([](auto E) { |
630 | 1.43k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Memory)); |
631 | 1.43k | return E; |
632 | 1.43k | })); |
633 | 1.43k | Checker.addMemory(MemType); |
634 | 1.43k | return {}; |
635 | 1.43k | } |
636 | 741 | case ExternalType::Tag: { |
637 | 741 | const auto &T = ImpDesc.getExternalTagType(); |
638 | | // Tag type index must exist in context. |
639 | 741 | auto TagTypeIdx = T.getTypeIdx(); |
640 | 741 | if (TagTypeIdx >= Checker.getTypes().size()) { |
641 | 28 | spdlog::error(ErrCode::Value::InvalidTagIdx); |
642 | 28 | spdlog::error(ErrInfo::InfoForbidIndex( |
643 | 28 | ErrInfo::IndexCategory::TagType, TagTypeIdx, |
644 | 28 | static_cast<uint32_t>(Checker.getTypes().size()))); |
645 | 28 | return Unexpect(ErrCode::Value::InvalidTagIdx); |
646 | 28 | } |
647 | | // Tag type must be valid. |
648 | 713 | auto &CompType = Checker.getTypes()[TagTypeIdx]->getCompositeType(); |
649 | 713 | if (!CompType.isFunc()) { |
650 | 3 | spdlog::error(ErrCode::Value::InvalidTagIdx); |
651 | 3 | spdlog::error(" Defined type index {} is not a function type."sv, |
652 | 3 | TagTypeIdx); |
653 | 3 | return Unexpect(ErrCode::Value::InvalidTagIdx); |
654 | 3 | } |
655 | 710 | if (!CompType.getFuncType().getReturnTypes().empty()) { |
656 | 4 | spdlog::error(ErrCode::Value::InvalidTagResultType); |
657 | 4 | return Unexpect(ErrCode::Value::InvalidTagResultType); |
658 | 4 | } |
659 | 706 | Checker.addTag(TagTypeIdx); |
660 | 706 | return {}; |
661 | 710 | } |
662 | 874 | case ExternalType::Global: { |
663 | 874 | const auto &GlobType = ImpDesc.getExternalGlobalType(); |
664 | | // Global type must be valid. |
665 | 874 | EXPECTED_TRY(validate(GlobType).map_error([](auto E) { |
666 | 868 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Global)); |
667 | 868 | return E; |
668 | 868 | })); |
669 | 868 | Checker.addGlobal(GlobType, true); |
670 | 868 | return {}; |
671 | 874 | } |
672 | 0 | default: |
673 | 0 | return {}; |
674 | 4.52k | } |
675 | 4.52k | } |
676 | | |
677 | | // Validate Export description. See "include/validator/validator.h". |
678 | 16.3k | Expect<void> Validator::validate(const AST::ExportDesc &ExpDesc) { |
679 | 16.3k | auto Id = ExpDesc.getExternalIndex(); |
680 | 16.3k | switch (ExpDesc.getExternalType()) { |
681 | 11.8k | case ExternalType::Function: |
682 | 11.8k | if (Id >= Checker.getFunctions().size()) { |
683 | 69 | spdlog::error(ErrCode::Value::InvalidFuncIdx); |
684 | 69 | spdlog::error(ErrInfo::InfoForbidIndex( |
685 | 69 | ErrInfo::IndexCategory::Function, Id, |
686 | 69 | static_cast<uint32_t>(Checker.getFunctions().size()))); |
687 | 69 | return Unexpect(ErrCode::Value::InvalidFuncIdx); |
688 | 69 | } |
689 | 11.7k | Checker.addRef(Id); |
690 | 11.7k | return {}; |
691 | 1.53k | case ExternalType::Table: |
692 | 1.53k | if (Id >= Checker.getTables().size()) { |
693 | 43 | spdlog::error(ErrCode::Value::InvalidTableIdx); |
694 | 43 | spdlog::error(ErrInfo::InfoForbidIndex( |
695 | 43 | ErrInfo::IndexCategory::Table, Id, |
696 | 43 | static_cast<uint32_t>(Checker.getTables().size()))); |
697 | 43 | return Unexpect(ErrCode::Value::InvalidTableIdx); |
698 | 43 | } |
699 | 1.48k | return {}; |
700 | 197 | case ExternalType::Memory: |
701 | 197 | if (Id >= Checker.getMemories().size()) { |
702 | 40 | spdlog::error(ErrCode::Value::InvalidMemoryIdx); |
703 | 40 | spdlog::error(ErrInfo::InfoForbidIndex( |
704 | 40 | ErrInfo::IndexCategory::Memory, Id, |
705 | 40 | static_cast<uint32_t>(Checker.getMemories().size()))); |
706 | 40 | return Unexpect(ErrCode::Value::InvalidMemoryIdx); |
707 | 40 | } |
708 | 157 | return {}; |
709 | 65 | case ExternalType::Tag: |
710 | 65 | if (Id >= Checker.getTags().size()) { |
711 | 41 | spdlog::error(ErrCode::Value::InvalidTagIdx); |
712 | 41 | spdlog::error(ErrInfo::InfoForbidIndex( |
713 | 41 | ErrInfo::IndexCategory::Tag, Id, |
714 | 41 | static_cast<uint32_t>(Checker.getTags().size()))); |
715 | 41 | return Unexpect(ErrCode::Value::InvalidTagIdx); |
716 | 41 | } |
717 | 24 | return {}; |
718 | 2.65k | case ExternalType::Global: |
719 | 2.65k | if (Id >= Checker.getGlobals().size()) { |
720 | 56 | spdlog::error(ErrCode::Value::InvalidGlobalIdx); |
721 | 56 | spdlog::error(ErrInfo::InfoForbidIndex( |
722 | 56 | ErrInfo::IndexCategory::Global, Id, |
723 | 56 | static_cast<uint32_t>(Checker.getGlobals().size()))); |
724 | 56 | return Unexpect(ErrCode::Value::InvalidGlobalIdx); |
725 | 56 | } |
726 | 2.60k | return {}; |
727 | 0 | default: |
728 | 0 | return {}; |
729 | 16.3k | } |
730 | 16.3k | } |
731 | | |
732 | 15.3k | Expect<void> Validator::validate(const AST::TypeSection &TypeSec) { |
733 | 15.3k | const auto STypeList = TypeSec.getContent(); |
734 | 15.3k | std::vector<uint32_t> SubTypeDepthMap(STypeList.size(), Unvisited); |
735 | 15.3k | std::map<std::vector<uint32_t>, uint32_t> RecTypes; |
736 | 15.3k | uint32_t Idx = 0; |
737 | 31.7k | while (Idx < STypeList.size()) { |
738 | 16.5k | const auto &SType = STypeList[Idx]; |
739 | | // The next type to add takes this index in the type index space. |
740 | 16.5k | const uint32_t BaseIdx = static_cast<uint32_t>(Checker.getTypes().size()); |
741 | 16.5k | if (Conf.hasProposal(Proposal::GC)) { |
742 | | // With GC a type is (self-)recursive (a singleton is a rec group of 1): |
743 | | // add the whole group before validating so members can reference it. |
744 | 16.5k | const uint32_t RecSize = SType.getRecursiveInfo().RecTypeSize; |
745 | 51.8k | for (uint32_t I = Idx; I < Idx + RecSize; I++) { |
746 | 35.3k | Checker.addType(STypeList[I]); |
747 | 35.3k | } |
748 | 16.5k | EXPECTED_TRY(validate(STypeList.subspan(Idx, RecSize), BaseIdx, |
749 | 16.3k | SubTypeDepthMap, Checker.getTypes()) |
750 | 16.3k | .map_error([](auto E) { |
751 | 16.3k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Rec)); |
752 | 16.3k | return E; |
753 | 16.3k | })); |
754 | 16.3k | setCanonicalIndices(STypeList.subspan(Idx, RecSize), BaseIdx, |
755 | 16.3k | Checker.getTypes(), RecTypes); |
756 | 16.3k | Idx += RecSize; |
757 | 16.3k | } else { |
758 | | // Without GC there are no rec groups: a type may reference only earlier |
759 | | // ones, so validate it before registering. |
760 | 0 | EXPECTED_TRY( |
761 | 0 | validate(SType, BaseIdx, SubTypeDepthMap, Checker.getTypes())); |
762 | 0 | Checker.addType(SType); |
763 | 0 | setCanonicalIndices(STypeList.subspan(Idx, 1), BaseIdx, |
764 | 0 | Checker.getTypes(), RecTypes); |
765 | 0 | Idx++; |
766 | 0 | } |
767 | 16.5k | } |
768 | 15.1k | return {}; |
769 | 15.3k | } |
770 | | |
771 | | // Validate Import section. See "include/validator/validator.h". |
772 | 15.1k | Expect<void> Validator::validate(const AST::ImportSection &ImportSec) { |
773 | 15.1k | for (auto &ImportDesc : ImportSec.getContent()) { |
774 | 4.52k | EXPECTED_TRY(validate(ImportDesc).map_error([](auto E) { |
775 | 4.52k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Desc_Import)); |
776 | 4.52k | return E; |
777 | 4.52k | })); |
778 | 4.52k | } |
779 | 15.0k | return {}; |
780 | 15.1k | } |
781 | | |
782 | | // Validate Function section. See "include/validator/validator.h". |
783 | 15.0k | Expect<void> Validator::validate(const AST::FunctionSection &FuncSec) { |
784 | 15.0k | const auto &FuncVec = FuncSec.getContent(); |
785 | 15.0k | const auto &TypeVec = Checker.getTypes(); |
786 | | |
787 | | // Check whether the function type ID is valid in context. |
788 | 29.8k | for (auto &TId : FuncVec) { |
789 | 29.8k | if (TId >= TypeVec.size()) { |
790 | 35 | spdlog::error(ErrCode::Value::InvalidFuncTypeIdx); |
791 | 35 | spdlog::error( |
792 | 35 | ErrInfo::InfoForbidIndex(ErrInfo::IndexCategory::FunctionType, TId, |
793 | 35 | static_cast<uint32_t>(TypeVec.size()))); |
794 | 35 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Function)); |
795 | 35 | return Unexpect(ErrCode::Value::InvalidFuncTypeIdx); |
796 | 35 | } |
797 | 29.8k | if (!TypeVec[TId]->getCompositeType().isFunc()) { |
798 | 9 | spdlog::error(ErrCode::Value::InvalidFuncTypeIdx); |
799 | 9 | spdlog::error(" Defined type index {} is not a function type."sv, TId); |
800 | 9 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Function)); |
801 | 9 | return Unexpect(ErrCode::Value::InvalidFuncTypeIdx); |
802 | 9 | } |
803 | 29.8k | Checker.addFunc(TId); |
804 | 29.8k | } |
805 | 15.0k | return {}; |
806 | 15.0k | } |
807 | | |
808 | | // Validate Table section. See "include/validator/validator.h". |
809 | 15.0k | Expect<void> Validator::validate(const AST::TableSection &TabSec) { |
810 | 15.0k | for (auto &Tab : TabSec.getContent()) { |
811 | 5.21k | EXPECTED_TRY(validate(Tab).map_error([](auto E) { |
812 | 5.02k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Seg_Table)); |
813 | 5.02k | return E; |
814 | 5.02k | })); |
815 | 5.02k | Checker.addTable(Tab.getTableType()); |
816 | 5.02k | } |
817 | 14.8k | return {}; |
818 | 15.0k | } |
819 | | |
820 | | // Validate Memory section. See "include/validator/validator.h". |
821 | 14.8k | Expect<void> Validator::validate(const AST::MemorySection &MemSec) { |
822 | 14.8k | for (auto &Mem : MemSec.getContent()) { |
823 | 5.63k | EXPECTED_TRY(validate(Mem).map_error([](auto E) { |
824 | 5.35k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Memory)); |
825 | 5.35k | return E; |
826 | 5.35k | })); |
827 | 5.35k | Checker.addMemory(Mem); |
828 | 5.35k | } |
829 | 14.5k | return {}; |
830 | 14.8k | } |
831 | | |
832 | | // Validate Global section. See "include/validator/validator.h". |
833 | 14.5k | Expect<void> Validator::validate(const AST::GlobalSection &GlobSec) { |
834 | 14.5k | for (auto &GlobSeg : GlobSec.getContent()) { |
835 | 2.28k | EXPECTED_TRY(validate(GlobSeg).map_error([](auto E) { |
836 | 1.95k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Seg_Global)); |
837 | 1.95k | return E; |
838 | 1.95k | })); |
839 | 1.95k | Checker.addGlobal(GlobSeg.getGlobalType()); |
840 | 1.95k | } |
841 | 14.2k | return {}; |
842 | 14.5k | } |
843 | | |
844 | | // Validate Element section. See "include/validator/validator.h". |
845 | 13.8k | Expect<void> Validator::validate(const AST::ElementSection &ElemSec) { |
846 | 13.8k | for (auto &ElemSeg : ElemSec.getContent()) { |
847 | 1.30k | EXPECTED_TRY(validate(ElemSeg).map_error([](auto E) { |
848 | 1.15k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Seg_Element)); |
849 | 1.15k | return E; |
850 | 1.15k | })); |
851 | 1.15k | Checker.addElem(ElemSeg); |
852 | 1.15k | } |
853 | 13.7k | return {}; |
854 | 13.8k | } |
855 | | |
856 | | // Validate Code section. See "include/validator/validator.h". |
857 | 11.9k | Expect<void> Validator::validate(const AST::CodeSection &CodeSec) { |
858 | 11.9k | const auto &CodeVec = CodeSec.getContent(); |
859 | 11.9k | const auto &FuncVec = Checker.getFunctions(); |
860 | | |
861 | | // Validate function body. |
862 | 27.2k | for (uint32_t Id = 0; Id < static_cast<uint32_t>(CodeVec.size()); ++Id) { |
863 | | // Added functions contain imported functions. |
864 | 19.5k | uint32_t TId = Id + static_cast<uint32_t>(Checker.getNumImportFuncs()); |
865 | 19.5k | if (TId >= static_cast<uint32_t>(FuncVec.size())) { |
866 | 0 | spdlog::error(ErrCode::Value::InvalidFuncIdx); |
867 | 0 | spdlog::error( |
868 | 0 | ErrInfo::InfoForbidIndex(ErrInfo::IndexCategory::Function, TId, |
869 | 0 | static_cast<uint32_t>(FuncVec.size()))); |
870 | 0 | return Unexpect(ErrCode::Value::InvalidFuncIdx); |
871 | 0 | } |
872 | 19.5k | EXPECTED_TRY(validate(CodeVec[Id], FuncVec[TId]).map_error([](auto E) { |
873 | 19.5k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Seg_Code)); |
874 | 19.5k | return E; |
875 | 19.5k | })); |
876 | 19.5k | } |
877 | 7.69k | return {}; |
878 | 11.9k | } |
879 | | |
880 | | // Validate Data section. See "include/validator/validator.h". |
881 | 13.7k | Expect<void> Validator::validate(const AST::DataSection &DataSec) { |
882 | 13.7k | for (auto &DataSeg : DataSec.getContent()) { |
883 | 2.33k | EXPECTED_TRY(validate(DataSeg).map_error([](auto E) { |
884 | 2.30k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Seg_Data)); |
885 | 2.30k | return E; |
886 | 2.30k | })); |
887 | 2.30k | Checker.addData(DataSeg); |
888 | 2.30k | } |
889 | 13.6k | return {}; |
890 | 13.7k | } |
891 | | |
892 | | // Validate Start section. See "include/validator/validator.h". |
893 | 13.9k | Expect<void> Validator::validate(const AST::StartSection &StartSec) { |
894 | 13.9k | if (StartSec.getContent()) { |
895 | 97 | auto FId = *StartSec.getContent(); |
896 | 97 | if (FId >= Checker.getFunctions().size()) { |
897 | 51 | spdlog::error(ErrCode::Value::InvalidFuncIdx); |
898 | 51 | spdlog::error(ErrInfo::InfoForbidIndex( |
899 | 51 | ErrInfo::IndexCategory::Function, FId, |
900 | 51 | static_cast<uint32_t>(Checker.getFunctions().size()))); |
901 | 51 | return Unexpect(ErrCode::Value::InvalidFuncIdx); |
902 | 51 | } |
903 | 46 | auto TId = Checker.getFunctions()[FId]; |
904 | 46 | assuming(TId < Checker.getTypes().size()); |
905 | 46 | if (!Checker.getTypes()[TId]->getCompositeType().isFunc()) { |
906 | 0 | spdlog::error(ErrCode::Value::InvalidStartFunc); |
907 | 0 | spdlog::error(" Defined type index {} is not a function type."sv, TId); |
908 | 0 | return Unexpect(ErrCode::Value::InvalidStartFunc); |
909 | 0 | } |
910 | 46 | auto &Type = Checker.getTypes()[TId]->getCompositeType().getFuncType(); |
911 | 46 | if (Type.getParamTypes().size() != 0 || Type.getReturnTypes().size() != 0) { |
912 | | // Start function signature should be {}->{} |
913 | 9 | spdlog::error(ErrCode::Value::InvalidStartFunc); |
914 | 9 | spdlog::error(ErrInfo::InfoMismatch({}, {}, Type.getParamTypes(), |
915 | 9 | Type.getReturnTypes())); |
916 | 9 | return Unexpect(ErrCode::Value::InvalidStartFunc); |
917 | 9 | } |
918 | 46 | } |
919 | 13.8k | return {}; |
920 | 13.9k | } |
921 | | |
922 | | // Validate Export section. See "include/validator/validator.h". |
923 | 14.1k | Expect<void> Validator::validate(const AST::ExportSection &ExportSec) { |
924 | 14.1k | std::unordered_set<std::string_view, Hash::Hash> ExportNames; |
925 | 16.3k | for (auto &ExportDesc : ExportSec.getContent()) { |
926 | 16.3k | auto Result = ExportNames.emplace(ExportDesc.getExternalName()); |
927 | 16.3k | if (!Result.second) { |
928 | | // Duplicated export name. |
929 | 14 | spdlog::error(ErrCode::Value::DupExportName); |
930 | 14 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Desc_Export)); |
931 | 14 | return Unexpect(ErrCode::Value::DupExportName); |
932 | 14 | } |
933 | 16.3k | EXPECTED_TRY(validate(ExportDesc).map_error([](auto E) { |
934 | 16.3k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Desc_Export)); |
935 | 16.3k | return E; |
936 | 16.3k | })); |
937 | 16.3k | } |
938 | 13.9k | return {}; |
939 | 14.1k | } |
940 | | |
941 | | // Validate Tag section. See "include/validator/validator.h". |
942 | 14.2k | Expect<void> Validator::validate(const AST::TagSection &TagSec) { |
943 | 14.2k | const auto &TagVec = TagSec.getContent(); |
944 | 14.2k | const auto &TypeVec = Checker.getTypes(); |
945 | | |
946 | | // Check whether the tag type ID is valid in context. |
947 | 14.2k | for (auto &TagType : TagVec) { |
948 | 2.69k | auto TagTypeIdx = TagType.getTypeIdx(); |
949 | 2.69k | if (TagTypeIdx >= TypeVec.size()) { |
950 | 41 | spdlog::error(ErrCode::Value::InvalidTagIdx); |
951 | 41 | spdlog::error( |
952 | 41 | ErrInfo::InfoForbidIndex(ErrInfo::IndexCategory::TagType, TagTypeIdx, |
953 | 41 | static_cast<uint32_t>(TypeVec.size()))); |
954 | 41 | return Unexpect(ErrCode::Value::InvalidTagIdx); |
955 | 41 | } |
956 | 2.65k | auto &CompType = TypeVec[TagTypeIdx]->getCompositeType(); |
957 | 2.65k | if (!CompType.isFunc()) { |
958 | 3 | spdlog::error(ErrCode::Value::InvalidTagIdx); |
959 | 3 | spdlog::error(" Defined type index {} is not a function type."sv, |
960 | 3 | TagTypeIdx); |
961 | 3 | return Unexpect(ErrCode::Value::InvalidTagIdx); |
962 | 3 | } |
963 | 2.64k | if (!CompType.getFuncType().getReturnTypes().empty()) { |
964 | 2 | spdlog::error(ErrCode::Value::InvalidTagResultType); |
965 | 2 | return Unexpect(ErrCode::Value::InvalidTagResultType); |
966 | 2 | } |
967 | 2.64k | Checker.addTag(TagTypeIdx); |
968 | 2.64k | } |
969 | 14.1k | return {}; |
970 | 14.2k | } |
971 | | |
972 | | // Validate constant expression. See "include/validator/validator.h". |
973 | | Expect<void> Validator::validateConstExpr(AST::InstrView Instrs, |
974 | 5.82k | Span<const ValType> Returns) { |
975 | 14.6k | for (auto &Instr : Instrs) { |
976 | | // Only these instructions are accepted. |
977 | 14.6k | switch (Instr.getOpCode()) { |
978 | 104 | case OpCode::Global__get: { |
979 | | // For the initialization case, global indices must be imported globals. |
980 | 104 | auto GlobIdx = Instr.getTargetIndex(); |
981 | 104 | uint32_t ValidGlobalSize = Checker.getNumImportGlobals(); |
982 | 104 | if (Conf.hasProposal(Proposal::FunctionReferences)) { |
983 | 104 | ValidGlobalSize = static_cast<uint32_t>(Checker.getGlobals().size()); |
984 | 104 | } |
985 | 104 | if (GlobIdx >= ValidGlobalSize) { |
986 | 52 | spdlog::error(ErrCode::Value::InvalidGlobalIdx); |
987 | 52 | spdlog::error(ErrInfo::InfoForbidIndex(ErrInfo::IndexCategory::Global, |
988 | 52 | GlobIdx, ValidGlobalSize)); |
989 | 52 | spdlog::error( |
990 | 52 | ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset())); |
991 | 52 | return Unexpect(ErrCode::Value::InvalidGlobalIdx); |
992 | 52 | } |
993 | 52 | if (Checker.getGlobals()[GlobIdx].second != ValMut::Const) { |
994 | 3 | spdlog::error(ErrCode::Value::ConstExprRequired); |
995 | 3 | spdlog::error( |
996 | 3 | ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset())); |
997 | 3 | return Unexpect(ErrCode::Value::ConstExprRequired); |
998 | 3 | } |
999 | 49 | break; |
1000 | 52 | } |
1001 | 1.97k | case OpCode::Ref__func: { |
1002 | | // In a const expression, add the reference to the context. |
1003 | 1.97k | auto FuncIdx = Instr.getTargetIndex(); |
1004 | 1.97k | if (FuncIdx >= Checker.getFunctions().size()) { |
1005 | | // Function index out of range. |
1006 | 61 | spdlog::error(ErrCode::Value::InvalidFuncIdx); |
1007 | 61 | spdlog::error(ErrInfo::InfoForbidIndex( |
1008 | 61 | ErrInfo::IndexCategory::Function, FuncIdx, |
1009 | 61 | static_cast<uint32_t>(Checker.getFunctions().size()))); |
1010 | 61 | spdlog::error( |
1011 | 61 | ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset())); |
1012 | 61 | return Unexpect(ErrCode::Value::InvalidFuncIdx); |
1013 | 61 | } |
1014 | 1.91k | Checker.addRef(Instr.getTargetIndex()); |
1015 | 1.91k | break; |
1016 | 1.97k | } |
1017 | 2.23k | case OpCode::I32__const: |
1018 | 2.85k | case OpCode::I64__const: |
1019 | 3.01k | case OpCode::F32__const: |
1020 | 3.13k | case OpCode::F64__const: |
1021 | 4.76k | case OpCode::Ref__null: |
1022 | 4.91k | case OpCode::V128__const: |
1023 | 10.4k | case OpCode::End: |
1024 | 10.5k | case OpCode::Struct__new: |
1025 | 10.6k | case OpCode::Struct__new_default: |
1026 | 10.6k | case OpCode::Array__new: |
1027 | 10.7k | case OpCode::Array__new_default: |
1028 | 10.7k | case OpCode::Array__new_fixed: |
1029 | 10.7k | case OpCode::Any__convert_extern: |
1030 | 10.8k | case OpCode::Extern__convert_any: |
1031 | 10.9k | case OpCode::Ref__i31: |
1032 | 10.9k | break; |
1033 | | |
1034 | | // For the Extended-const proposal, these instructions are accepted. |
1035 | 194 | case OpCode::I32__add: |
1036 | 470 | case OpCode::I32__sub: |
1037 | 827 | case OpCode::I32__mul: |
1038 | 1.02k | case OpCode::I64__add: |
1039 | 1.29k | case OpCode::I64__sub: |
1040 | 1.49k | case OpCode::I64__mul: |
1041 | 1.49k | if (Conf.hasProposal(Proposal::ExtendedConst)) { |
1042 | 1.49k | break; |
1043 | 1.49k | } |
1044 | 0 | spdlog::error(ErrCode::Value::ConstExprRequired); |
1045 | 0 | spdlog::error(ErrInfo::InfoProposal(Proposal::ExtendedConst)); |
1046 | 0 | spdlog::error( |
1047 | 0 | ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset())); |
1048 | 0 | return Unexpect(ErrCode::Value::ConstExprRequired); |
1049 | | |
1050 | 129 | default: |
1051 | 129 | spdlog::error(ErrCode::Value::ConstExprRequired); |
1052 | 129 | spdlog::error( |
1053 | 129 | ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset())); |
1054 | 129 | return Unexpect(ErrCode::Value::ConstExprRequired); |
1055 | 14.6k | } |
1056 | 14.6k | } |
1057 | | // Validate expression with result types. |
1058 | 5.58k | Checker.reset(); |
1059 | 5.58k | return Checker.validate(Instrs, Returns); |
1060 | 5.82k | } |
1061 | | |
1062 | | } // namespace Validator |
1063 | | } // namespace WasmEdge |