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