Coverage Report

Created: 2026-11-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/lib/executor/restore.cpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
#include "executor/restore.h"
5
6
#include "loader/filemgr.h"
7
#include "loader/loader.h"
8
9
#include <algorithm>
10
#include <array>
11
#include <iterator>
12
#include <limits>
13
#include <unordered_map>
14
#include <unordered_set>
15
#include <utility>
16
#include <vector>
17
18
namespace WasmEdge {
19
namespace Coredump {
20
namespace Detail {
21
namespace {
22
23
using namespace Runtime::Instance;
24
constexpr auto Malformed = ErrCode::Value::CoredumpMalformed;
25
constexpr auto Mismatch = ErrCode::Value::CoredumpMismatch;
26
constexpr auto Unsupported = ErrCode::Value::CoredumpUnsupported;
27
28
class Reader {
29
public:
30
0
  explicit Reader(const AST::CustomSection &Section) {
31
0
    const auto Result = FM.setCode(Span<const Byte>(Section.getContent()));
32
0
    assuming(Result);
33
0
  }
34
35
0
  Expect<Byte> byte() { return FM.readByte().map_error(mapError); }
36
0
  Expect<uint32_t> u32() { return FM.readU32().map_error(mapError); }
37
0
  Expect<uint64_t> u64() { return FM.readU64().map_error(mapError); }
38
0
  Expect<void> name() {
39
0
    EXPECTED_TRY(FM.readName().map_error(mapError));
40
0
    return {};
41
0
  }
42
0
  Expect<void> end() const {
43
0
    if (FM.getRemainSize() != 0) {
44
0
      return Unexpect(Malformed);
45
0
    }
46
0
    return {};
47
0
  }
48
0
  Expect<void> version(uint32_t Expected) {
49
0
    EXPECTED_TRY(auto Actual, u32());
50
0
    if (Actual != Expected) {
51
0
      return Unexpect(Malformed);
52
0
    }
53
0
    return {};
54
0
  }
55
0
  Expect<uint32_t> count() {
56
0
    EXPECTED_TRY(auto Count, u32());
57
0
    if (Count > FM.getRemainSize()) {
58
0
      return Unexpect(Malformed);
59
0
    }
60
0
    return Count;
61
0
  }
62
0
  Expect<ValType> refType() {
63
0
    EXPECTED_TRY(auto Code, byte());
64
0
    auto RefCode = TypeCode::RefNull;
65
0
    int64_t Heap;
66
0
    if (Code == static_cast<Byte>(TypeCode::Ref) ||
67
0
        Code == static_cast<Byte>(TypeCode::RefNull)) {
68
0
      RefCode = static_cast<TypeCode>(Code);
69
0
      EXPECTED_TRY(Heap, FM.readS33().map_error(mapError));
70
0
    } else {
71
0
      if (Code < 0x40 || Code >= 0x80) {
72
0
        return Unexpect(Malformed);
73
0
      }
74
0
      Heap = static_cast<int64_t>(Code) - 128;
75
0
    }
76
0
    if (Heap >= 0) {
77
0
      return ValType(RefCode, static_cast<uint32_t>(Heap));
78
0
    }
79
0
    if (Heap < -64) {
80
0
      return Unexpect(Malformed);
81
0
    }
82
0
    const auto HeapCode = static_cast<TypeCode>(Heap & 0x7F);
83
0
    switch (HeapCode) {
84
0
    case TypeCode::FuncRef:
85
0
    case TypeCode::ExternRef:
86
0
    case TypeCode::AnyRef:
87
0
    case TypeCode::EqRef:
88
0
    case TypeCode::I31Ref:
89
0
    case TypeCode::StructRef:
90
0
    case TypeCode::ArrayRef:
91
0
    case TypeCode::NullRef:
92
0
    case TypeCode::NullFuncRef:
93
0
    case TypeCode::NullExternRef:
94
0
    case TypeCode::ExnRef:
95
0
    case TypeCode::NullExnRef:
96
0
      return ValType(RefCode, HeapCode);
97
0
    default:
98
0
      return Unexpect(Malformed);
99
0
    }
100
0
  }
101
0
  Expect<AST::Limit> limit() {
102
0
    EXPECTED_TRY(auto Flags, byte());
103
0
    if (Flags > 7 || Flags == 2 || Flags == 6) {
104
0
      return Unexpect(Malformed);
105
0
    }
106
0
    EXPECTED_TRY(auto Min, (Flags & 4) ? u64() : u32().map([](uint32_t V) {
107
0
      return static_cast<uint64_t>(V);
108
0
    }));
109
0
    AST::Limit Limit(Min, bool(Flags & 4));
110
0
    Limit.setType(static_cast<AST::Limit::LimitType>(Flags));
111
0
    if (Flags & 1) {
112
0
      EXPECTED_TRY(auto Max, (Flags & 4) ? u64() : u32().map([](uint32_t V) {
113
0
        return static_cast<uint64_t>(V);
114
0
      }));
115
0
      if (Min > Max) {
116
0
        return Unexpect(Malformed);
117
0
      }
118
0
      Limit.setMax(Max);
119
0
    }
120
0
    return Limit;
121
0
  }
122
123
private:
124
0
  static ErrCode mapError(ErrCode) { return Malformed; }
125
  FileMgr FM;
126
};
127
128
template <typename T> class Mapping {
129
public:
130
0
  explicit Mapping(size_t Count) : Instances(Count), Owners(Count) {}
Unexecuted instantiation: restore.cpp:WasmEdge::Coredump::Detail::(anonymous namespace)::Mapping<WasmEdge::Runtime::Instance::MemoryInstance>::Mapping(unsigned long)
Unexecuted instantiation: restore.cpp:WasmEdge::Coredump::Detail::(anonymous namespace)::Mapping<WasmEdge::Runtime::Instance::GlobalInstance>::Mapping(unsigned long)
Unexecuted instantiation: restore.cpp:WasmEdge::Coredump::Detail::(anonymous namespace)::Mapping<WasmEdge::Runtime::Instance::TableInstance>::Mapping(unsigned long)
131
132
0
  Expect<void> read(Reader &R, Span<T *const> Live, ModuleInstance *Owner) {
133
0
    EXPECTED_TRY(auto Count, R.count());
134
0
    if (Count != Live.size()) {
135
0
      return Unexpect(Mismatch);
136
0
    }
137
0
    for (auto *Instance : Live) {
138
0
      EXPECTED_TRY(auto Idx, R.u32());
139
0
      if (Idx >= Instances.size()) {
140
0
        return Unexpect(Malformed);
141
0
      }
142
0
      if (Instance == nullptr ||
143
0
          (Instances[Idx] != nullptr && Instances[Idx] != Instance)) {
144
0
        return Unexpect(Mismatch);
145
0
      }
146
0
      auto [It, Inserted] = Indices.emplace(Instance, Idx);
147
0
      if (!Inserted && It->second != Idx) {
148
0
        return Unexpect(Mismatch);
149
0
      }
150
0
      if (Instances[Idx] == nullptr) {
151
0
        Instances[Idx] = Instance;
152
0
        Owners[Idx] = Owner;
153
0
      }
154
0
    }
155
0
    return {};
156
0
  }
Unexecuted instantiation: restore.cpp:WasmEdge::Coredump::Detail::(anonymous namespace)::Mapping<WasmEdge::Runtime::Instance::MemoryInstance>::read(WasmEdge::Coredump::Detail::(anonymous namespace)::Reader&, cxx20::span<WasmEdge::Runtime::Instance::MemoryInstance* const, 18446744073709551615ul>, WasmEdge::Runtime::Instance::ModuleInstance*)
Unexecuted instantiation: restore.cpp:WasmEdge::Coredump::Detail::(anonymous namespace)::Mapping<WasmEdge::Runtime::Instance::GlobalInstance>::read(WasmEdge::Coredump::Detail::(anonymous namespace)::Reader&, cxx20::span<WasmEdge::Runtime::Instance::GlobalInstance* const, 18446744073709551615ul>, WasmEdge::Runtime::Instance::ModuleInstance*)
Unexecuted instantiation: restore.cpp:WasmEdge::Coredump::Detail::(anonymous namespace)::Mapping<WasmEdge::Runtime::Instance::TableInstance>::read(WasmEdge::Coredump::Detail::(anonymous namespace)::Reader&, cxx20::span<WasmEdge::Runtime::Instance::TableInstance* const, 18446744073709551615ul>, WasmEdge::Runtime::Instance::ModuleInstance*)
157
0
  Expect<void> complete() const {
158
0
    if (std::find(Instances.begin(), Instances.end(), nullptr) !=
159
0
        Instances.end()) {
160
0
      return Unexpect(Malformed);
161
0
    }
162
0
    return {};
163
0
  }
Unexecuted instantiation: restore.cpp:WasmEdge::Coredump::Detail::(anonymous namespace)::Mapping<WasmEdge::Runtime::Instance::MemoryInstance>::complete() const
Unexecuted instantiation: restore.cpp:WasmEdge::Coredump::Detail::(anonymous namespace)::Mapping<WasmEdge::Runtime::Instance::GlobalInstance>::complete() const
Unexecuted instantiation: restore.cpp:WasmEdge::Coredump::Detail::(anonymous namespace)::Mapping<WasmEdge::Runtime::Instance::TableInstance>::complete() const
164
165
  std::vector<T *> Instances;
166
  std::vector<ModuleInstance *> Owners;
167
168
private:
169
  std::unordered_map<T *, uint32_t> Indices;
170
};
171
172
0
Expect<TypeCode> bottomType(const ValType &Type, const ModuleInstance &Owner) {
173
0
  auto Heap = Type.getHeapTypeCode();
174
0
  if (Heap == TypeCode::TypeIndex) {
175
0
    const auto Def = Owner.getType(Type.getTypeIndex());
176
0
    if (!Def) {
177
0
      return Unexpect(Mismatch);
178
0
    }
179
0
    Heap = (*Def)->getCompositeType().isFunc() ? TypeCode::FuncRef
180
0
                                               : TypeCode::AnyRef;
181
0
  }
182
0
  switch (Heap) {
183
0
  case TypeCode::FuncRef:
184
0
  case TypeCode::NullFuncRef:
185
0
    return TypeCode::NullFuncRef;
186
0
  case TypeCode::ExternRef:
187
0
  case TypeCode::NullExternRef:
188
0
    return TypeCode::NullExternRef;
189
0
  case TypeCode::ExnRef:
190
0
  case TypeCode::NullExnRef:
191
0
    return TypeCode::NullExnRef;
192
0
  default:
193
0
    return TypeCode::NullRef;
194
0
  }
195
0
}
196
197
Expect<RefVariant> readRef(Reader &R, const ValType &Type,
198
                           const ModuleInstance &Owner,
199
0
                           Span<ModuleInstance *const> Modules) {
200
0
  EXPECTED_TRY(auto Tag, R.byte());
201
0
  switch (Tag) {
202
0
  case kRefNull: {
203
0
    if (!Type.isNullableRefType()) {
204
0
      return Unexpect(Mismatch);
205
0
    }
206
0
    EXPECTED_TRY(auto Bottom, bottomType(Type, Owner));
207
0
    return RefVariant(ValType(TypeCode::RefNull, Bottom));
208
0
  }
209
0
  case kRefFunction: {
210
0
    EXPECTED_TRY(auto Inst, R.u32());
211
0
    EXPECTED_TRY(auto FuncIdx, R.u32());
212
0
    if (Inst >= Modules.size()) {
213
0
      return Unexpect(Malformed);
214
0
    }
215
0
    const auto Functions = Modules[Inst]->getFunctionInstances();
216
0
    if (FuncIdx >= Functions.size() || Functions[FuncIdx] == nullptr) {
217
0
      return Unexpect(Malformed);
218
0
    }
219
0
    const auto *Func = Functions[FuncIdx];
220
0
    const auto *FuncOwner = Func->getModule();
221
0
    if (Type.getHeapTypeCode() == TypeCode::TypeIndex &&
222
0
        !Owner.getType(Type.getTypeIndex())) {
223
0
      return Unexpect(Mismatch);
224
0
    }
225
0
    if (FuncOwner == nullptr ||
226
0
        !AST::TypeMatcher::matchType(Owner.getTypeList(), Type,
227
0
                                     FuncOwner->getTypeList(),
228
0
                                     Func->getDefType())) {
229
0
      return Unexpect(Mismatch);
230
0
    }
231
0
    return RefVariant(Func->getDefType(), Func);
232
0
  }
233
0
  case kRefMissing:
234
0
    return Unexpect(Unsupported);
235
0
  default:
236
0
    return Unexpect(Malformed);
237
0
  }
238
0
}
239
240
template <typename T>
241
Expect<void> readDrops(Reader &R, Span<T *const> Live,
242
0
                       std::vector<T *> &Drops) {
243
0
  EXPECTED_TRY(auto Count, R.count());
244
0
  if (Count != Live.size()) {
245
0
    return Unexpect(Mismatch);
246
0
  }
247
0
  for (auto *Instance : Live) {
248
0
    EXPECTED_TRY(auto Dropped, R.byte());
249
0
    if (Dropped > 1) {
250
0
      return Unexpect(Malformed);
251
0
    }
252
0
    if (!Dropped && Instance->isDropped()) {
253
0
      return Unexpect(Unsupported);
254
0
    }
255
0
    if (Dropped && !Instance->isDropped()) {
256
0
      Drops.push_back(Instance);
257
0
    }
258
0
  }
259
0
  return {};
260
0
}
Unexecuted instantiation: restore.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Coredump::Detail::(anonymous namespace)::readDrops<WasmEdge::Runtime::Instance::DataInstance>(WasmEdge::Coredump::Detail::(anonymous namespace)::Reader&, cxx20::span<WasmEdge::Runtime::Instance::DataInstance* const, 18446744073709551615ul>, std::__1::vector<WasmEdge::Runtime::Instance::DataInstance*, std::__1::allocator<WasmEdge::Runtime::Instance::DataInstance*> >&)
Unexecuted instantiation: restore.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Coredump::Detail::(anonymous namespace)::readDrops<WasmEdge::Runtime::Instance::ElementInstance>(WasmEdge::Coredump::Detail::(anonymous namespace)::Reader&, cxx20::span<WasmEdge::Runtime::Instance::ElementInstance* const, 18446744073709551615ul>, std::__1::vector<WasmEdge::Runtime::Instance::ElementInstance*, std::__1::allocator<WasmEdge::Runtime::Instance::ElementInstance*> >&)
261
262
0
bool sameLimit(const AST::Limit &A, const AST::Limit &B) {
263
0
  return A.is64() == B.is64() && A.isShared() == B.isShared() &&
264
0
         A.hasMax() == B.hasMax() && (!A.hasMax() || A.getMax() == B.getMax());
265
0
}
266
267
template <typename T>
268
const ModuleInstance *typeOwner(T *Instance, const ModuleInstance *Fallback,
269
                                Span<ModuleInstance *const> Modules,
270
0
                                const ValType &Type) {
271
0
  if (Type.getHeapTypeCode() != TypeCode::TypeIndex) {
272
0
    return Fallback;
273
0
  }
274
0
  for (const auto *Module : Modules) {
275
0
    if (Module->owns(Instance)) {
276
0
      return Module;
277
0
    }
278
0
  }
279
0
  return nullptr;
280
0
}
Unexecuted instantiation: restore.cpp:WasmEdge::Runtime::Instance::ModuleInstance const* WasmEdge::Coredump::Detail::(anonymous namespace)::typeOwner<WasmEdge::Runtime::Instance::TableInstance>(WasmEdge::Runtime::Instance::TableInstance*, WasmEdge::Runtime::Instance::ModuleInstance const*, cxx20::span<WasmEdge::Runtime::Instance::ModuleInstance* const, 18446744073709551615ul>, WasmEdge::ValType const&)
Unexecuted instantiation: restore.cpp:WasmEdge::Runtime::Instance::ModuleInstance const* WasmEdge::Coredump::Detail::(anonymous namespace)::typeOwner<WasmEdge::Runtime::Instance::GlobalInstance>(WasmEdge::Runtime::Instance::GlobalInstance*, WasmEdge::Runtime::Instance::ModuleInstance const*, cxx20::span<WasmEdge::Runtime::Instance::ModuleInstance* const, 18446744073709551615ul>, WasmEdge::ValType const&)
281
282
0
bool validPlaceholder(const ValType &Type) {
283
0
  return Type == ValType(TypeCode::FuncRef) ||
284
0
         Type == ValType(TypeCode::ExternRef) ||
285
0
         Type == ValType(TypeCode::AnyRef) || Type == ValType(TypeCode::ExnRef);
286
0
}
287
288
Expect<RestorePlan> buildPlan(const RestoreRequest &Req,
289
0
                              const AST::Module &Dump) {
290
0
  const std::array<std::string_view, 5> Names = {
291
0
      "coremodules", "coreinstances", "wasmedge.tables", "wasmedge.instances",
292
0
      "wasmedge.globals"};
293
0
  std::array<const AST::CustomSection *, 5> Sections{};
294
0
  for (const auto &Section : Dump.getCustomSections()) {
295
0
    const auto Idx = static_cast<size_t>(
296
0
        std::distance(Names.begin(), std::find(Names.begin(), Names.end(),
297
0
                                               Section.getName())));
298
0
    if (Idx != Names.size()) {
299
0
      auto &Slot = Sections[Idx];
300
0
      if (Slot != nullptr) {
301
0
        return Unexpect(Malformed);
302
0
      }
303
0
      Slot = &Section;
304
0
    }
305
0
  }
306
0
  if (std::find(Sections.begin(), Sections.end(), nullptr) != Sections.end()) {
307
0
    return Unexpect(Malformed);
308
0
  }
309
0
  std::unordered_set<ModuleInstance *> Unique;
310
0
  for (auto *Module : Req.Modules) {
311
0
    if (Module == nullptr || !Unique.insert(Module).second) {
312
0
      return Unexpect(Mismatch);
313
0
    }
314
0
  }
315
0
  Reader Mods(*Sections[0]);
316
0
  EXPECTED_TRY(auto Count, Mods.count());
317
0
  if (Count != Req.Modules.size()) {
318
0
    return Unexpect(Mismatch);
319
0
  }
320
0
  for (uint32_t I = 0; I < Count; ++I) {
321
0
    EXPECTED_TRY(auto Tag, Mods.byte());
322
0
    if (Tag != 0) {
323
0
      return Unexpect(Malformed);
324
0
    }
325
0
    EXPECTED_TRY(Mods.name());
326
0
  }
327
0
  EXPECTED_TRY(Mods.end());
328
329
0
  const auto &MemTypes = Dump.getMemorySection().getContent();
330
0
  if (!Dump.getImportSection().getContent().empty() ||
331
0
      !Dump.getFunctionSection().getContent().empty() ||
332
0
      !Dump.getCodeSection().getContent().empty() ||
333
0
      !Dump.getElementSection().getContent().empty()) {
334
0
    return Unexpect(Malformed);
335
0
  }
336
0
  const auto &Globals = Dump.getGlobalSection().getContent();
337
0
  const auto &Tables = Dump.getTableSection().getContent();
338
0
  Mapping<MemoryInstance> MemMap(MemTypes.size());
339
0
  Mapping<GlobalInstance> GlobMap(Globals.size());
340
0
  Mapping<TableInstance> TabMap(Tables.size());
341
0
  Reader Core(*Sections[1]);
342
0
  EXPECTED_TRY(Count, Core.count());
343
0
  if (Count != Req.Modules.size()) {
344
0
    return Unexpect(Mismatch);
345
0
  }
346
0
  for (uint32_t I = 0; I < Count; ++I) {
347
0
    EXPECTED_TRY(auto Tag, Core.byte());
348
0
    EXPECTED_TRY(auto Idx, Core.u32());
349
0
    if (Tag != 0 || Idx != I) {
350
0
      return Unexpect(Malformed);
351
0
    }
352
0
    auto *Mod = Req.Modules[I];
353
0
    EXPECTED_TRY(MemMap.read(Core, Mod->getMemoryInstances(), Mod));
354
0
    EXPECTED_TRY(GlobMap.read(Core, Mod->getGlobalInstances(), Mod));
355
0
  }
356
0
  EXPECTED_TRY(Core.end());
357
0
  EXPECTED_TRY(MemMap.complete());
358
0
  EXPECTED_TRY(GlobMap.complete());
359
360
0
  RestorePlan Plan;
361
0
  Reader Instances(*Sections[3]);
362
0
  EXPECTED_TRY(Instances.version(kInstancesVersion));
363
0
  EXPECTED_TRY(Count, Instances.count());
364
0
  if (Count != Req.Modules.size()) {
365
0
    return Unexpect(Mismatch);
366
0
  }
367
0
  for (auto *Mod : Req.Modules) {
368
0
    EXPECTED_TRY(TabMap.read(Instances, Mod->getTableInstances(), Mod));
369
0
    EXPECTED_TRY(
370
0
        readDrops(Instances, Mod->getOwnedDataInstances(), Plan.DataDrops));
371
0
    EXPECTED_TRY(readDrops(Instances, Mod->getElemInstances(), Plan.ElemDrops));
372
0
  }
373
0
  EXPECTED_TRY(Instances.end());
374
0
  EXPECTED_TRY(TabMap.complete());
375
376
0
  for (size_t I = 0; I < MemTypes.size(); ++I) {
377
0
    auto *Mem = MemMap.Instances[I];
378
0
    const auto &Limit = MemTypes[I].getLimit();
379
0
    const auto &Live = Mem->getMemoryType().getLimit();
380
0
    if (Limit.isShared() || Live.isShared()) {
381
0
      return Unexpect(Unsupported);
382
0
    }
383
0
    if (Limit.hasMax() && Limit.getMin() > Limit.getMax()) {
384
0
      return Unexpect(Malformed);
385
0
    }
386
0
    const auto Pages = Limit.getMin();
387
0
    if (Limit.is64() != Live.is64() ||
388
0
        (Live.hasMax() && Pages > Live.getMax()) ||
389
0
        Pages > Mem->getPageLimit() ||
390
0
        Pages >
391
0
            std::numeric_limits<size_t>::max() / MemoryInstance::kPageSize ||
392
0
        (Live.is32() && Pages > MemoryInstance::kPageLimit32) ||
393
0
        Pages > MemoryInstance::kPageLimit64) {
394
0
      return Unexpect(Mismatch);
395
0
    }
396
0
    Plan.Memories.push_back({Mem, Pages, {}});
397
0
  }
398
0
  for (const auto &Segment : Dump.getDataSection().getContent()) {
399
0
    if (Segment.getMode() != AST::DataSegment::DataMode::Active ||
400
0
        Segment.getIdx() >= Plan.Memories.size()) {
401
0
      return Unexpect(Malformed);
402
0
    }
403
0
    auto &Memory = Plan.Memories[Segment.getIdx()];
404
0
    const auto &Expr = Segment.getExpr().getInstrs();
405
0
    const bool Is64 = Memory.Instance->getMemoryType().getLimit().is64();
406
0
    if (Expr.size() != 2 || Expr[1].getOpCode() != OpCode::End ||
407
0
        Expr[0].getOpCode() !=
408
0
            (Is64 ? OpCode::I64__const : OpCode::I32__const)) {
409
0
      return Unexpect(Malformed);
410
0
    }
411
0
    const uint64_t Offset = Is64 ? Expr[0].getNum().get<uint64_t>()
412
0
                                 : Expr[0].getNum().get<uint32_t>();
413
0
    const auto Size = Memory.Pages * MemoryInstance::kPageSize;
414
0
    if (Offset > Size || Segment.getData().size() > Size - Offset) {
415
0
      return Unexpect(Malformed);
416
0
    }
417
0
    Memory.Segments.emplace_back(
418
0
        Offset,
419
0
        std::vector<Byte>(Segment.getData().begin(), Segment.getData().end()));
420
0
  }
421
0
  for (auto &Memory : Plan.Memories) {
422
0
    std::stable_sort(
423
0
        Memory.Segments.begin(), Memory.Segments.end(),
424
0
        [](const auto &A, const auto &B) { return A.first < B.first; });
425
0
    uint64_t End = 0;
426
0
    for (const auto &[Offset, Bytes] : Memory.Segments) {
427
0
      if (Offset < End) {
428
0
        return Unexpect(Malformed);
429
0
      }
430
0
      End = Offset + Bytes.size();
431
0
    }
432
0
  }
433
434
0
  Reader TabReader(*Sections[2]);
435
0
  EXPECTED_TRY(TabReader.version(kTablesVersion));
436
0
  EXPECTED_TRY(Count, TabReader.count());
437
0
  if (Count != Tables.size()) {
438
0
    return Unexpect(Malformed);
439
0
  }
440
0
  for (uint32_t I = 0; I < Count; ++I) {
441
0
    EXPECTED_TRY(auto Type, TabReader.refType());
442
0
    EXPECTED_TRY(auto Limit, TabReader.limit());
443
0
    EXPECTED_TRY(auto Size, TabReader.u64());
444
0
    EXPECTED_TRY(auto RefCount, TabReader.count());
445
0
    if (RefCount != Size || Limit.getMin() != Size ||
446
0
        !sameLimit(Limit, Tables[I].getTableType().getLimit()) ||
447
0
        Tables[I].getTableType().getLimit().getMin() != Size) {
448
0
      return Unexpect(Malformed);
449
0
    }
450
0
    const auto &Live = TabMap.Instances[I]->getTableType();
451
0
    if (!validPlaceholder(Tables[I].getTableType().getRefType()) ||
452
0
        !Tables[I].getExpr().getInstrs().empty()) {
453
0
      return Unexpect(Malformed);
454
0
    }
455
0
    if (Type != Live.getRefType() || !sameLimit(Limit, Live.getLimit()) ||
456
0
        (Live.getLimit().hasMax() && Size > Live.getLimit().getMax()) ||
457
0
        Size > getMaxAddress(Live.getLimit().getAddrType())) {
458
0
      return Unexpect(Mismatch);
459
0
    }
460
0
    TablePlan Table;
461
0
    Table.Instance = TabMap.Instances[I];
462
0
    const auto *Owner =
463
0
        typeOwner(Table.Instance, TabMap.Owners[I], Req.Modules, Type);
464
0
    if (Owner == nullptr) {
465
0
      return Unexpect(Unsupported);
466
0
    }
467
0
    for (uint32_t J = 0; J < RefCount; ++J) {
468
0
      EXPECTED_TRY(auto Ref, readRef(TabReader, Type, *Owner, Req.Modules));
469
0
      Table.Refs.push_back(Ref);
470
0
    }
471
0
    Plan.Tables.push_back(std::move(Table));
472
0
  }
473
0
  EXPECTED_TRY(TabReader.end());
474
475
0
  std::vector<bool> RefSeen(Globals.size(), false);
476
0
  Reader GlobReader(*Sections[4]);
477
0
  EXPECTED_TRY(GlobReader.version(kGlobalsVersion));
478
0
  EXPECTED_TRY(Count, GlobReader.count());
479
0
  for (uint32_t I = 0; I < Count; ++I) {
480
0
    EXPECTED_TRY(auto Idx, GlobReader.u32());
481
0
    EXPECTED_TRY(auto Type, GlobReader.refType());
482
0
    EXPECTED_TRY(auto Mut, GlobReader.byte());
483
0
    if (Idx >= Globals.size() || RefSeen[Idx] || Mut > 1) {
484
0
      return Unexpect(Malformed);
485
0
    }
486
0
    auto *Global = GlobMap.Instances[Idx];
487
0
    const auto &Live = Global->getGlobalType();
488
0
    if (Type != Live.getValType() ||
489
0
        Mut != static_cast<Byte>(Live.getValMut())) {
490
0
      return Unexpect(Mismatch);
491
0
    }
492
0
    const auto *Owner =
493
0
        typeOwner(Global, GlobMap.Owners[Idx], Req.Modules, Type);
494
0
    if (Owner == nullptr) {
495
0
      return Unexpect(Unsupported);
496
0
    }
497
0
    EXPECTED_TRY(auto Ref, readRef(GlobReader, Type, *Owner, Req.Modules));
498
0
    RefSeen[Idx] = true;
499
0
    Plan.Globals.emplace_back(Global, Ref);
500
0
  }
501
0
  EXPECTED_TRY(GlobReader.end());
502
0
  for (size_t I = 0; I < Globals.size(); ++I) {
503
0
    const auto &Segment = Globals[I];
504
0
    const auto &Expr = Segment.getExpr().getInstrs();
505
0
    const auto &Type = GlobMap.Instances[I]->getGlobalType().getValType();
506
0
    if (Expr.size() != 2 || Expr[1].getOpCode() != OpCode::End) {
507
0
      return Unexpect(Malformed);
508
0
    }
509
0
    if (Type.isRefType()) {
510
0
      if (!RefSeen[I] ||
511
0
          !validPlaceholder(Segment.getGlobalType().getValType()) ||
512
0
          Expr[0].getOpCode() != OpCode::Ref__null ||
513
0
          Expr[0].getValType() != Segment.getGlobalType().getValType()) {
514
0
        return Unexpect(Malformed);
515
0
      }
516
0
      continue;
517
0
    }
518
0
    if (Type != Segment.getGlobalType().getValType()) {
519
0
      return Unexpect(Mismatch);
520
0
    }
521
0
    OpCode Expected;
522
0
    switch (Type.getCode()) {
523
0
    case TypeCode::I32:
524
0
      Expected = OpCode::I32__const;
525
0
      break;
526
0
    case TypeCode::I64:
527
0
      Expected = OpCode::I64__const;
528
0
      break;
529
0
    case TypeCode::F32:
530
0
      Expected = OpCode::F32__const;
531
0
      break;
532
0
    case TypeCode::F64:
533
0
      Expected = OpCode::F64__const;
534
0
      break;
535
0
    case TypeCode::V128:
536
0
      Expected = OpCode::V128__const;
537
0
      break;
538
0
    default:
539
0
      return Unexpect(Malformed);
540
0
    }
541
0
    if (Expr[0].getOpCode() != Expected) {
542
0
      return Unexpect(Malformed);
543
0
    }
544
0
    Plan.Globals.emplace_back(GlobMap.Instances[I], Expr[0].getNum());
545
0
  }
546
0
  return Plan;
547
0
}
548
549
} // namespace
550
551
0
Expect<RestorePlan> preflight(const RestoreRequest &Req) noexcept {
552
0
  Configure Conf;
553
0
  Conf.addProposal(Proposal::Threads);
554
0
  Loader::Loader Loader(Conf);
555
0
  auto Dump = Loader.parseModule(Req.Path);
556
0
  if (!Dump) {
557
0
    return Unexpect(Dump.error() == ErrCode::Value::IllegalPath
558
0
                        ? Dump.error()
559
0
                        : ErrCode(Malformed));
560
0
  }
561
0
  return buildPlan(Req, **Dump);
562
0
}
563
564
} // namespace Detail
565
566
0
Expect<void> restore(const RestoreRequest &Req) noexcept {
567
0
  EXPECTED_TRY(auto Plan, Detail::preflight(Req));
568
0
  std::vector<std::pair<Runtime::Instance::MemoryInstance *, uint64_t>> Grown;
569
0
  for (auto &Memory : Plan.Memories) {
570
0
    auto &Instance = *Memory.Instance;
571
0
    const auto Old = Instance.getPageSize();
572
0
    if (Memory.Pages <= Old) {
573
0
      continue;
574
0
    }
575
0
    if (!Instance.growPage(Memory.Pages - Old)) {
576
0
      for (auto &[Grew, Pages] : Grown) {
577
0
        const auto Shrunk = Grew->shrinkPage(Pages);
578
0
        assuming(Shrunk);
579
0
      }
580
0
      return Unexpect(ErrCode::Value::CoredumpAllocationFailed);
581
0
    }
582
0
    Grown.emplace_back(&Instance, Memory.Pages - Old);
583
0
  }
584
0
  for (auto &Memory : Plan.Memories) {
585
0
    auto &Instance = *Memory.Instance;
586
0
    const auto Old = Instance.getPageSize();
587
0
    if (Memory.Pages < Old) {
588
0
      const auto Shrunk = Instance.shrinkPage(Old - Memory.Pages);
589
0
      assuming(Shrunk);
590
0
    }
591
0
    uint64_t End = 0;
592
0
    for (const auto &[Offset, Bytes] : Memory.Segments) {
593
0
      const auto Cleared = Instance.fillBytes(0, End, Offset - End);
594
0
      assuming(Cleared);
595
0
      const auto Written = Instance.setBytes(Bytes, Offset, 0, Bytes.size());
596
0
      assuming(Written);
597
0
      End = Offset + Bytes.size();
598
0
    }
599
0
    const auto Cleared = Instance.fillBytes(0, End, Instance.getSize() - End);
600
0
    assuming(Cleared);
601
0
  }
602
0
  for (auto &Table : Plan.Tables) {
603
0
    auto &Instance = *Table.Instance;
604
0
    const auto Old = Instance.getSize();
605
0
    const auto Size = Table.Refs.size();
606
0
    const auto Resized = Size < Old ? Instance.shrinkTable(Old - Size)
607
0
                                    : Instance.growTable(Size - Old);
608
0
    assuming(Resized);
609
0
    const auto Written = Instance.setRefs(Table.Refs, 0, 0, Size);
610
0
    assuming(Written);
611
0
  }
612
0
  for (auto &[Global, Value] : Plan.Globals) {
613
0
    Global->setValue(Value);
614
0
  }
615
0
  for (auto *Data : Plan.DataDrops) {
616
0
    Data->clear();
617
0
  }
618
0
  for (auto *Elem : Plan.ElemDrops) {
619
0
    Elem->clear();
620
0
  }
621
0
  return {};
622
0
}
623
624
} // namespace Coredump
625
} // namespace WasmEdge