3#include <sysio/vm/allocator.hpp>
4#include <sysio/vm/constants.hpp>
5#include <sysio/vm/exceptions.hpp>
6#include <sysio/vm/leb128.hpp>
7#include <sysio/vm/options.hpp>
8#include <sysio/vm/sections.hpp>
9#include <sysio/vm/types.hpp>
10#include <sysio/vm/utils.hpp>
11#include <sysio/vm/vector.hpp>
12#include <sysio/vm/debug_info.hpp>
21namespace sysio {
namespace vm {
25 static constexpr unsigned get_size_for_type(
uint8_t type) {
37 template<
typename Options,
typename Enable =
void>
42 template<
typename Options>
45 template<
typename Options>
47 static_assert(std::is_unsigned_v<std::decay_t<max_mutable_globals_t<Options>>>,
"max_mutable_globals must be an unsigned integer type");
49 unsigned size = get_size_for_type(type);
51 SYS_VM_ASSERT(_counter <= options.max_mutable_global_bytes && _counter >= size, wasm_parse_exception,
"mutable globals exceeded limit");
53 std::decay_t<max_mutable_globals_t<Options>> _counter = 0;
56#define PARSER_OPTION(name, default_, type) \
57 template<typename Options> \
58 type get_ ## name(const Options& options, long) { (void)options; return default_; } \
59 template<typename Options> \
60 auto get_ ## name(const Options& options, int) -> decltype(options.name) { \
61 return options.name; \
63 template<typename Options> \
64 type get_ ## name(const Options& options) { return detail::get_ ## name(options, 0); }
66#define MAX_ELEMENTS(name, default_)\
67 PARSER_OPTION(name, default_, std::uint32_t)
86 template<typename Options, typename Enable =
void>
89 void on_local(
const Options&, std::uint8_t,
const std::uint32_t) {}
94 static constexpr bool is_defined =
false;
96 template<
typename Options>
100 for(std::uint32_t i = 0; i < ft.
param_types.size(); ++i) {
106 unsigned size = get_size_for_type(type);
108 SYS_VM_ASSERT(_count <= options.max_func_local_bytes && _count >= size, wasm_parse_exception,
"local variable limit exceeded");
112 uint64_t size = get_size_for_type(type);
115 SYS_VM_ASSERT(_count <= options.max_func_local_bytes && _count >= size, wasm_parse_exception,
"local variable limit exceeded");
118 std::decay_t<decltype(std::declval<Options>().max_func_local_bytes)> _count = 0;
121 template<
typename Options>
124 template<
typename Options>
127 template<
typename Options,
typename Enable =
void>
137 this->_count -= get_size_for_type(type);
148 template<
typename Options>
150 get_max_func_local_bytes_no_stack_c<Options>(0)>> {
163 template<typename Options, typename Enable =
void>
168 template<
typename Options>
172 SYS_VM_ASSERT(_count <=
options.sysio_max_nested_structures, wasm_parse_exception,
"Nested depth exceeded");
175 if(_count == 0) ++_count;
178 std::decay_t<decltype(std::declval<Options>().sysio_max_nested_structures)> _count = 0;
201 template <
typename Writer,
typename Options = default_options,
typename DebugInfo = null_debug_info>
206 template <
typename T>
222 unsigned char ch = *code++;
225 }
else if(ch < 0xE0) {
226 SYS_VM_ASSERT((ch & 0xC0) == 0xC0, wasm_parse_exception,
"invalid utf8 encoding");
227 unsigned char b2 = *code++;
228 SYS_VM_ASSERT((b2 & 0xC0) == 0x80, wasm_parse_exception,
"invalid utf8 encoding");
230 (
static_cast<uint32_t>(ch - 0xC0u) << 6u) +
231 (
static_cast<uint32_t>(b2 - 0x80u));
232 SYS_VM_ASSERT(0x80 <= code_point && code_point < 0x800, wasm_parse_exception,
"invalid utf8 encoding");
234 }
else if(ch < 0xF0) {
235 unsigned char b2 = *code++;
236 SYS_VM_ASSERT((b2 & 0xC0) == 0x80, wasm_parse_exception,
"invalid utf8 encoding");
237 unsigned char b3 = *code++;
238 SYS_VM_ASSERT((b3 & 0xC0) == 0x80, wasm_parse_exception,
"invalid utf8 encoding");
240 (
static_cast<uint32_t>(ch - 0xE0u) << 12u) +
241 (
static_cast<uint32_t>(b2 - 0x80u) << 6u) +
242 (
static_cast<uint32_t>(b3 - 0x80u));
243 SYS_VM_ASSERT((0x800 <= code_point && code_point < 0xD800) ||
244 (0xE000 <= code_point && code_point < 0x10000),
245 wasm_parse_exception,
"invalid utf8 encoding");
247 }
else if (ch < 0xF8) {
248 unsigned char b2 = *code++;
249 SYS_VM_ASSERT((b2 & 0xC0) == 0x80, wasm_parse_exception,
"invalid utf8 encoding");
250 unsigned char b3 = *code++;
251 SYS_VM_ASSERT((b3 & 0xC0) == 0x80, wasm_parse_exception,
"invalid utf8 encoding");
252 unsigned char b4 = *code++;
253 SYS_VM_ASSERT((b4 & 0xC0) == 0x80, wasm_parse_exception,
"invalid utf8 encoding");
255 (
static_cast<uint32_t>(ch - 0xF0u) << 18u) +
256 (
static_cast<uint32_t>(b2 - 0x80u) << 12u) +
257 (
static_cast<uint32_t>(b3 - 0x80u) << 6u) +
258 (
static_cast<uint32_t>(b4 - 0x80u));
259 SYS_VM_ASSERT((0x10000 <= code_point && code_point < 0x110000),
260 wasm_parse_exception,
"invalid utf8 encoding");
263 SYS_VM_ASSERT(
false, wasm_parse_exception,
"invalid utf8 encoding");
275 auto guard = code.scoped_shrink_bounds(
len);
277 result.copy(code.raw(),
len);
284 static_assert(std::is_arithmetic_v<T>,
"Can only read builtin types");
285 auto guard = code.scoped_shrink_bounds(
sizeof(
T));
287 memcpy(&result, code.raw(),
sizeof(
T));
292 inline module& parse_module(wasm_code& code, module& mod, DebugInfo& debug) {
293 wasm_code_ptr cp(code.data(), code.size());
298 inline module& parse_module2(wasm_code_ptr& code_ptr, size_t sz, module& mod, DebugInfo& debug) {
299 parse_module(code_ptr, sz, mod, debug);
305 SYS_VM_ASSERT(parse_magic(code_ptr) == constants::magic, wasm_parse_exception,
"magic number did not match");
306 SYS_VM_ASSERT(parse_version(code_ptr) == constants::version, wasm_parse_exception,
307 "version number did not match");
308 uint8_t highest_section_id = 0;
310 if (code_ptr.
offset() == sz)
312 auto id = parse_section_id(code_ptr);
313 auto len = parse_section_payload_len(code_ptr);
315 SYS_VM_ASSERT(
id == 0 ||
id > highest_section_id, wasm_parse_exception,
"section out of order");
316 highest_section_id = std::max(highest_section_id,
id);
321 case section_id::custom_section: parse_custom(code_ptr);
break;
322 case section_id::type_section: parse_section<section_id::type_section>(code_ptr, mod.
types);
break;
323 case section_id::import_section: parse_section<section_id::import_section>(code_ptr, mod.
imports);
break;
324 case section_id::function_section:
325 parse_section<section_id::function_section>(code_ptr, mod.
functions);
328 case section_id::table_section: parse_section<section_id::table_section>(code_ptr, mod.
tables);
break;
329 case section_id::memory_section:
330 parse_section<section_id::memory_section>(code_ptr, mod.
memories);
332 case section_id::global_section: parse_section<section_id::global_section>(code_ptr, mod.
globals);
break;
333 case section_id::export_section:
334 parse_section<section_id::export_section>(code_ptr, mod.
exports);
337 case section_id::start_section: parse_section<section_id::start_section>(code_ptr, mod.
start);
break;
338 case section_id::element_section:
339 parse_section<section_id::element_section>(code_ptr, mod.
elements);
341 case section_id::code_section: parse_section<section_id::code_section>(code_ptr, mod.
code);
break;
342 case section_id::data_section: parse_section<section_id::data_section>(code_ptr, mod.
data);
break;
343 default:
SYS_VM_ASSERT(
false, wasm_parse_exception,
"error invalid section id");
346 SYS_VM_ASSERT(_mod->code.size() == _mod->functions.size(), wasm_parse_exception,
"code section must have the same size as the function section" );
348 debug.set(std::move(imap));
349 debug.relocate(_allocator.get_code_start());
353 return parse_raw<uint32_t>(code);
356 return parse_raw<uint32_t>(code);
360 return parse_varuint32(code);
364 auto section_name = parse_utf8_string(code, 0xFFFFFFFFu);
365 if(detail::get_parse_custom_section_name(_options) &&
366 section_name.size() == 4 && std::memcmp(section_name.raw(),
"name", 4) == 0) {
367 parse_name_section(code);
370 code += code.bounds() - code.offset();
375 for(
uint32_t i = 0; i < map.size(); ++i) {
376 map[i].idx = parse_varuint32(code);
377 map[i].name = parse_utf8_string(code, 0xFFFFFFFFu);
384 if(code.bounds() == code.offset())
return;
387 auto subsection_guard = code.scoped_consume_items(parse_varuint32(code));
391 if(code.bounds() == code.offset())
return;
394 auto subsection_guard = code.scoped_consume_items(parse_varuint32(code));
395 uint32_t size = parse_varuint32(code);
398 parse_name_map(code, *_mod->names->function_names);
400 if(code.bounds() == code.offset())
return;
403 auto subsection_guard = code.scoped_consume_items(parse_varuint32(code));
404 uint32_t size = parse_varuint32(code);
407 for(
uint32_t i = 0; i < size; ++i) {
408 auto& [idx,namemap] = (*_mod->names->local_names)[i];
409 idx = parse_varuint32(code);
410 uint32_t local_size = parse_varuint32(code);
412 parse_name_map(code, namemap);
415 if(code.bounds() == code.offset())
return;
416 SYS_VM_ASSERT(
false, wasm_parse_exception,
"Invalid subsection Id");
420 entry.
module_str = parse_utf8_string(code, detail::get_max_symbol_bytes(_options));
421 entry.
field_str = parse_utf8_string(code, detail::get_max_symbol_bytes(_options));
423 auto type = parse_varuint32(code);
425 case external_kind::Function:
429 default:
SYS_VM_ASSERT(
false, wasm_unsupported_import_exception,
"only function imports are supported");
434 if (detail::get_allow_u32_limits_flags(_options)) {
435 return parse_varuint32(code) & 0x1;
437 SYS_VM_ASSERT(*code == 0x0 || *code == 0x1, wasm_parse_exception,
"invalid flags");
451 SYS_VM_ASSERT(tt.
limits.
initial <= detail::get_max_table_elements(_options), wasm_parse_exception,
"table size exceeds limit");
459 SYS_VM_ASSERT(ct == types::i32 || ct == types::i64 || ct == types::f32 || ct == types::f64,
460 wasm_parse_exception,
"invalid global content type");
464 on_mutable_global(ct);
465 parse_init_expr(code, gv.
init, ct);
484 entry.
field_str = parse_utf8_string(code, detail::get_max_symbol_bytes(_options));
486 entry.
index = parse_varuint32(code);
488 case external_kind::Function:
SYS_VM_ASSERT(entry.
index < _mod->get_functions_total(), wasm_parse_exception,
"function export out of range");
break;
489 case external_kind::Table:
SYS_VM_ASSERT(entry.
index < _mod->tables.size(), wasm_parse_exception,
"table export out of range");
break;
490 case external_kind::Memory:
SYS_VM_ASSERT(entry.
index < _mod->memories.size(), wasm_parse_exception,
"memory export out of range");
break;
491 case external_kind::Global:
492 SYS_VM_ASSERT(entry.
index < _mod->globals.size(), wasm_parse_exception,
"global export out of range");
493 SYS_VM_ASSERT(!detail::get_forbid_export_mutable_globals(_options) || !_mod->globals.at(entry.
index).type.mutability,
494 wasm_parse_exception,
"cannot export mutable globals");
496 default:
SYS_VM_ASSERT(
false, wasm_parse_exception,
"Unknown export kind");
break;
503 decltype(ft.
param_types) param_types = { _allocator, parse_varuint32(code) };
504 for (
size_t i = 0; i < param_types.size(); i++) {
506 param_types.at(i) = pt;
507 SYS_VM_ASSERT(pt == types::i32 || pt == types::i64 || pt == types::f32 || pt == types::f64,
508 wasm_parse_exception,
"invalid function param type");
516 SYS_VM_ASSERT(rt == types::i32 || rt == types::i64 || rt == types::f32 || rt == types::f64,
517 wasm_parse_exception,
"invalid function return type");
523 for (std::size_t i = 0; i < _mod->tables.size(); i++) {
524 if (_mod->tables[i].element_type == types::anyfunc)
525 tt = &(_mod->tables[i]);
527 SYS_VM_ASSERT(tt !=
nullptr, wasm_parse_exception,
"table not declared");
528 es.
index = parse_varuint32(code);
529 SYS_VM_ASSERT(es.
index == 0, wasm_parse_exception,
"only table index of 0 is supported");
530 parse_init_expr(code, es.
offset, types::i32);
531 uint32_t size = parse_varuint32(code);
532 SYS_VM_ASSERT(size <= detail::get_max_element_segment_elements(_options), wasm_parse_exception,
"elem segment too large");
533 decltype(es.
elems) elems = { _allocator, size };
534 for (
uint32_t i = 0; i < size; i++) {
535 uint32_t index = parse_varuint32(code);
537 SYS_VM_ASSERT(index < _mod->get_functions_total(), wasm_parse_exception,
"elem for undefined function");
540 if (
static_cast<uint64_t>(size) + offset <= tt->table.size() ) {
541 std::memcpy(tt->
table.raw() + offset, elems.raw(), size *
sizeof(
uint32_t));
543 _mod->error =
"elem out of range";
545 es.
elems = std::move(elems);
551 case opcodes::i32_const:
552 ie.
value.
i32 = parse_varint32(code);
553 SYS_VM_ASSERT(type == types::i32, wasm_parse_exception,
"expected i32 initializer");
555 case opcodes::i64_const:
556 ie.
value.
i64 = parse_varint64(code);
557 SYS_VM_ASSERT(type == types::i64, wasm_parse_exception,
"expected i64 initializer");
559 case opcodes::f32_const:
560 ie.
value.
f32 = parse_raw<uint32_t>(code);
561 SYS_VM_ASSERT(type == types::f32, wasm_parse_exception,
"expected f32 initializer");
563 case opcodes::f64_const:
564 ie.
value.
f64 = parse_raw<uint64_t>(code);
565 SYS_VM_ASSERT(type == types::f64, wasm_parse_exception,
"expected f64 initializer");
569 "initializer expression can only acception i32.const, i64.const, f32.const and f64.const");
571 SYS_VM_ASSERT((*code++) == opcodes::end, wasm_parse_exception,
"no end op found");
575 fb.
size = parse_varuint32(code);
576 SYS_VM_ASSERT(fb.
size <= detail::get_max_code_bytes(_options), wasm_parse_exception,
"Function body too large");
577 const auto& before = code.offset();
578 const auto& local_cnt = parse_varuint32(code);
579 _current_function_index++;
580 SYS_VM_ASSERT(local_cnt <= detail::get_max_local_sets(_options), wasm_parse_exception,
"Number of local sets exceeds limit");
582 func_type& ft = _mod->types.at(_mod->functions.at(idx));
585 for (
size_t i = 0; i < local_cnt; i++) {
586 auto count = parse_varuint32(code);
588 if (detail::get_allow_invalid_empty_local_set(_options) &&
count == 0) type = types::i32;
589 SYS_VM_ASSERT(type == types::i32 || type == types::i64 || type == types::f32 || type == types::f64,
590 wasm_parse_exception,
"invalid local type");
597 fb.
size -= code.offset() - before;
598 auto guard = code.scoped_shrink_bounds(fb.
size);
599 _function_bodies.emplace_back(
wasm_code_ptr{code.raw(), fb.
size}, local_checker);
602 SYS_VM_ASSERT(detail::get_allow_code_after_function_end(_options) || *code == 0x0B,
603 wasm_parse_exception,
"failed parsing function body, expected 'end'");
613 using label_t =
decltype(std::declval<Writer>().emit_end());
614 using branch_t =
decltype(std::declval<Writer>().emit_if());
620 std::variant<label_t, std::vector<branch_t>> relocations;
623 static constexpr uint8_t any_type = 0x82;
627 std::vector<uint8_t>
state = { scope_tag };
628 static constexpr uint8_t unreachable_tag = 0x80;
629 static constexpr uint8_t scope_tag = 0x81;
635 assert(type != unreachable_tag && type != scope_tag);
636 assert(type == types::i32 || type == types::i64 || type == types::f32 || type == types::f64 || type == any_type);
637 SYS_VM_ASSERT(operand_depth < std::numeric_limits<uint32_t>::max(), wasm_parse_exception,
"integer overflow in operand depth");
639 maximum_operand_depth = std::max(operand_depth, maximum_operand_depth);
640 state.push_back(type);
644 assert(expected != unreachable_tag && expected != scope_tag);
645 if(expected == types::pseudo)
return;
647 if (
state.back() != unreachable_tag) {
656 if (
state.back() == unreachable_tag)
672 while(!
state.empty() &&
state.back() != scope_tag) {
673 if (
state.back() != unreachable_tag)
678 state.push_back(unreachable_tag);
681 state.push_back(scope_tag);
686 if (
state.back() == unreachable_tag) {
697 if (!
state.empty() &&
state.back() == unreachable_tag) {
707 _ft(ft), _locals(locals_arg) {
709 _boundaries.push_back(
count);
713 count += locals_arg[i].count;
718 SYS_VM_ASSERT(local_idx < _boundaries.back(), wasm_parse_exception,
"undefined local");
719 auto pos = std::upper_bound(_boundaries.begin(), _boundaries.end(), local_idx);
720 if (pos == _boundaries.begin())
721 return _ft.param_types[local_idx];
723 return _locals[pos - _boundaries.begin() - 1].type;
726 uint64_t total = _boundaries.back();
727 return total - _ft.param_types.size();
731 std::vector<uint32_t> _boundaries;
739 std::vector<pc_element_t> pc_stack{{
744 std::vector<branch_t>{}}};
752 std::visit(
overloaded{ [&](
label_t target) { code_writer.fix_branch(address, target); },
753 [&](std::vector<branch_t>& relocations) { relocations.push_back(address); } },
771 result |= 0x80000000;
778 auto exit_scope = [&]() {
780 SYS_VM_ASSERT(pc_stack.size(), wasm_parse_exception,
"unexpected end instruction");
782 SYS_VM_ASSERT(!pc_stack.back().is_if || pc_stack.back().expected_result == types::pseudo, wasm_parse_exception,
"wrong type");
783 auto end_pos = code_writer.emit_end();
784 if(
auto* relocations = std::get_if<std::vector<branch_t>>(&pc_stack.back().relocations)) {
785 for(
auto branch_op : *relocations) {
786 code_writer.fix_branch(branch_op, end_pos);
789 op_stack.pop_scope(pc_stack.back().expected_result);
793 auto check_in_bounds = [&]{
794 SYS_VM_ASSERT(!detail::get_allow_code_after_function_end(_options) || !pc_stack.empty(),
795 wasm_parse_exception,
"code after function end");
798 while (code.offset() < bounds) {
799 SYS_VM_ASSERT(pc_stack.size() <= detail::get_max_nested_structures(_options), wasm_parse_exception,
800 "nested structures validation failure");
802 imap.on_instr_start(code_writer.get_addr(), code.raw());
805 case opcodes::unreachable: check_in_bounds(); code_writer.emit_unreachable(); op_stack.start_unreachable();
break;
806 case opcodes::nop: code_writer.emit_nop();
break;
810 SYS_VM_ASSERT(detail::get_allow_code_after_function_end(_options) ||
811 !pc_stack.empty() || code.offset() == bounds, wasm_parse_exception,
"function too short");
812 _nested_checker.on_end(_options);
815 case opcodes::return_: {
818 auto branch = code_writer.emit_return(compute_depth_change(
label));
819 handle_branch_target(
label, branch);
820 op_stack.start_unreachable();
822 case opcodes::block: {
829 "Invalid type code in block");
831 code_writer.emit_block();
832 op_stack.push_scope();
833 _nested_checker.on_control(_options);
835 case opcodes::loop: {
842 "Invalid type code in loop");
843 auto pos = code_writer.emit_loop();
844 pc_stack.push_back({op_stack.depth(),
expected_result, types::pseudo,
false, pos});
845 op_stack.push_scope();
846 _nested_checker.on_control(_options);
856 "Invalid type code in if");
857 auto branch = code_writer.emit_if();
858 op_stack.pop(types::i32);
860 op_stack.push_scope();
861 _nested_checker.on_control(_options);
863 case opcodes::else_: {
865 auto& old_index = pc_stack.back();
866 SYS_VM_ASSERT(old_index.is_if, wasm_parse_exception,
"else outside if");
867 auto& relocations = std::get<std::vector<branch_t>>(old_index.relocations);
869 op_stack.pop(old_index.expected_result);
870 op_stack.pop_scope();
871 op_stack.push_scope();
875 relocations[0] = code_writer.emit_else(relocations[0]);
876 old_index.is_if =
false;
877 _nested_checker.on_control(_options);
883 auto branch = code_writer.emit_br(compute_depth_change(
label));
884 handle_branch_target(
label, branch);
885 op_stack.start_unreachable();
887 case opcodes::br_if: {
890 op_stack.pop(types::i32);
891 auto branch = code_writer.emit_br_if(compute_depth_change(
label));
892 handle_branch_target(
label, branch);
894 case opcodes::br_table: {
896 size_t table_size = parse_varuint32(code);
897 SYS_VM_ASSERT(table_size <= detail::get_max_br_table_elements(_options), wasm_parse_exception,
"Too many labels in br_table");
899 op_stack.pop(types::i32);
900 auto handler = code_writer.emit_br_table(table_size);
901 for (
size_t i = 0; i < table_size; i++) {
903 auto branch = handler.emit_case(compute_depth_change(
label));
904 handle_branch_target(
label, branch);
905 uint8_t one_result = pc_stack[pc_stack.size() -
label - 1].label_result;
907 result_type = one_result;
909 SYS_VM_ASSERT(result_type == one_result, wasm_parse_exception,
"br_table labels must have the same type");
913 auto branch = handler.emit_default(compute_depth_change(
label));
914 handle_branch_target(
label, branch);
915 SYS_VM_ASSERT(table_size == 0 || result_type == pc_stack[pc_stack.size() -
label - 1].label_result,
916 wasm_parse_exception,
"br_table labels must have the same type");
917 op_stack.start_unreachable();
919 case opcodes::call: {
921 uint32_t funcnum = parse_varuint32(code);
922 const func_type& ft = _mod->get_function_type(funcnum);
928 code_writer.emit_call(ft, funcnum);
930 case opcodes::call_indirect: {
932 uint32_t functypeidx = parse_varuint32(code);
933 const func_type& ft = _mod->types.at(functypeidx);
934 SYS_VM_ASSERT(_mod->tables.size() > 0, wasm_parse_exception,
"call_indirect requires a table");
935 op_stack.pop(types::i32);
941 code_writer.emit_call_indirect(ft, functypeidx);
942 SYS_VM_ASSERT(*code == 0, wasm_parse_exception,
"call_indirect must end with 0x00.");
946 case opcodes::drop: check_in_bounds(); code_writer.emit_drop(); op_stack.pop();
break;
947 case opcodes::select: {
949 code_writer.emit_select();
950 op_stack.pop(types::i32);
953 SYS_VM_ASSERT(t0 == t1 || t0 == any_type || t1 == any_type, wasm_parse_exception,
"incorrect types for select");
954 op_stack.push(t0 != any_type? t0 : t1);
956 case opcodes::get_local: {
957 uint32_t local_idx = parse_varuint32(code);
958 op_stack.push(local_types[local_idx]);
959 code_writer.emit_get_local(local_idx);
961 case opcodes::set_local: {
963 uint32_t local_idx = parse_varuint32(code);
964 op_stack.pop(local_types[local_idx]);
965 code_writer.emit_set_local(local_idx);
967 case opcodes::tee_local: {
969 uint32_t local_idx = parse_varuint32(code);
970 op_stack.top(local_types[local_idx]);
971 code_writer.emit_tee_local(local_idx);
973 case opcodes::get_global: {
974 uint32_t global_idx = parse_varuint32(code);
975 op_stack.push(_mod->globals.at(global_idx).type.content_type);
976 code_writer.emit_get_global(global_idx);
978 case opcodes::set_global: {
980 uint32_t global_idx = parse_varuint32(code);
981 SYS_VM_ASSERT(_mod->globals.at(global_idx).type.mutability, wasm_parse_exception,
"cannot set const global");
982 op_stack.pop(_mod->globals.at(global_idx).type.content_type);
983 code_writer.emit_set_global(global_idx);
985#define LOAD_OP(op_name, max_align, type) \
986 case opcodes::op_name: { \
988 SYS_VM_ASSERT(_mod->memories.size() > 0, wasm_parse_exception, "load requires memory"); \
989 uint32_t alignment = parse_varuint32(code); \
990 uint32_t offset = parse_varuint32(code); \
991 SYS_VM_ASSERT(alignment <= uint32_t(max_align), wasm_parse_exception, "alignment cannot be greater than size."); \
992 SYS_VM_ASSERT(offset <= detail::get_max_memory_offset(_options), wasm_parse_exception, "load offset too large."); \
993 op_stack.pop(types::i32); \
994 op_stack.push(types::type); \
995 code_writer.emit_ ## op_name( alignment, offset ); \
1015#define STORE_OP(op_name, max_align, type) \
1016 case opcodes::op_name: { \
1017 check_in_bounds(); \
1018 SYS_VM_ASSERT(_mod->memories.size() > 0, wasm_parse_exception, "store requires memory"); \
1019 uint32_t alignment = parse_varuint32(code); \
1020 uint32_t offset = parse_varuint32(code); \
1021 SYS_VM_ASSERT(alignment <= uint32_t(max_align), wasm_parse_exception, "alignment cannot be greater than size."); \
1022 SYS_VM_ASSERT(offset <= detail::get_max_memory_offset(_options), wasm_parse_exception, "store offset too large."); \
1023 op_stack.pop(types::type); \
1024 op_stack.pop(types::i32); \
1025 code_writer.emit_ ## op_name( alignment, offset ); \
1040 case opcodes::current_memory:
1041 SYS_VM_ASSERT(_mod->memories.size() != 0, wasm_parse_exception,
"memory.size requires memory");
1042 op_stack.push(types::i32);
1043 SYS_VM_ASSERT(*code == 0, wasm_parse_exception,
"memory.size must end with 0x00");
1045 code_writer.emit_current_memory();
1047 case opcodes::grow_memory:
1049 SYS_VM_ASSERT(_mod->memories.size() != 0, wasm_parse_exception,
"memory.grow requires memory");
1050 op_stack.pop(types::i32);
1051 op_stack.push(types::i32);
1052 SYS_VM_ASSERT(*code == 0, wasm_parse_exception,
"memory.grow must end with 0x00");
1054 code_writer.emit_grow_memory();
1056 case opcodes::i32_const: code_writer.emit_i32_const( parse_varint32(code) ); op_stack.push(types::i32);
break;
1057 case opcodes::i64_const: code_writer.emit_i64_const( parse_varint64(code) ); op_stack.push(types::i64);
break;
1058 case opcodes::f32_const: {
1059 code_writer.emit_f32_const( parse_raw<float>(code) );
1060 op_stack.push(types::f32);
1062 case opcodes::f64_const: {
1063 code_writer.emit_f64_const( parse_raw<double>(code) );
1064 op_stack.push(types::f64);
1067#define UNOP(opname) \
1068 case opcodes::opname: check_in_bounds(); code_writer.emit_ ## opname(); op_stack.pop(types::A); op_stack.push(types::R); break;
1069#define BINOP(opname) \
1070 case opcodes::opname: check_in_bounds(); code_writer.emit_ ## opname(); op_stack.pop(types::A); op_stack.pop(types::A); op_stack.push(types::R); break;
1071#define CASTOP(dst, opname, src) \
1072 case opcodes::dst ## _ ## opname ## _ ## src: check_in_bounds(); code_writer.emit_ ## dst ## _ ## opname ## _ ## src(); op_stack.pop(types::src); op_stack.push(types::dst); break;
1194 CASTOP(i32, trunc_s, f32)
1195 CASTOP(i32, trunc_u, f32)
1196 CASTOP(i32, trunc_s, f64)
1197 CASTOP(i32, trunc_u, f64)
1198 CASTOP(i64, extend_s, i32)
1199 CASTOP(i64, extend_u, i32)
1200 CASTOP(i64, trunc_s, f32)
1201 CASTOP(i64, trunc_u, f32)
1202 CASTOP(i64, trunc_s, f64)
1203 CASTOP(i64, trunc_u, f64)
1204 CASTOP(f32, convert_s, i32)
1205 CASTOP(f32, convert_u, i32)
1206 CASTOP(f32, convert_s, i64)
1207 CASTOP(f32, convert_u, i64)
1209 CASTOP(f64, convert_s, i32)
1210 CASTOP(f64, convert_u, i32)
1211 CASTOP(f64, convert_s, i64)
1212 CASTOP(f64, convert_u, i64)
1213 CASTOP(f64, promote, f32)
1214 CASTOP(i32, reinterpret, f32)
1215 CASTOP(i64, reinterpret, f64)
1216 CASTOP(f32, reinterpret, i32)
1217 CASTOP(f64, reinterpret, i64)
1222 default:
SYS_VM_ASSERT(
false, wasm_parse_exception,
"Illegal instruction");
1225 SYS_VM_ASSERT( pc_stack.empty(), wasm_parse_exception,
"function body too long" );
1226 _mod->maximum_stack = std::max(_mod->maximum_stack,
static_cast<uint64_t>(op_stack.maximum_operand_depth) + local_types.
locals_count());
1230 SYS_VM_ASSERT(_mod->memories.size() != 0, wasm_parse_exception,
"data requires memory");
1231 ds.index = parse_varuint32(code);
1232 parse_init_expr(code, ds.offset, types::i32);
1233 auto len = parse_varuint32(code);
1234 SYS_VM_ASSERT(
len <= detail::get_max_data_segment_bytes(_options), wasm_parse_exception,
"data segment too large.");
1236 wasm_parse_exception,
"out-of-bounds data section");
1237 auto guard = code.scoped_shrink_bounds(
len);
1238 ds.data =
decltype(ds.data){ _allocator,
len};
1239 ds.data.copy(code.raw(),
len);
1243 template <
typename Elem,
typename ParseFunc>
1245 auto count = parse_varuint32(code);
1246 SYS_VM_ASSERT(
count <= max_elements, wasm_parse_exception,
"number of section elements exceeded limit");
1248 for (
size_t i = 0; i <
count; i++) { elem_parse(code, elems.
at(i), i); }
1251 template <u
int8_t
id>
1253 vec<
typename std::enable_if_t<id == section_id::type_section, func_type>>& elems) {
1254 parse_section_impl(code, elems, detail::get_max_type_section_elements(_options),
1257 template <u
int8_t
id>
1259 vec<
typename std::enable_if_t<id == section_id::import_section, import_entry>>& elems) {
1260 parse_section_impl(code, elems, detail::get_max_import_section_elements(_options),
1263 template <u
int8_t
id>
1265 vec<
typename std::enable_if_t<id == section_id::function_section, uint32_t>>& elems) {
1266 parse_section_impl(code, elems, detail::get_max_function_section_elements(_options),
1269 template <u
int8_t
id>
1271 vec<
typename std::enable_if_t<id == section_id::table_section, table_type>>& elems) {
1272 parse_section_impl(code, elems, 1,
1275 template <u
int8_t
id>
1277 vec<
typename std::enable_if_t<id == section_id::memory_section, memory_type>>& elems) {
1279 SYS_VM_ASSERT(idx == 0, wasm_parse_exception,
"only one memory is permitted");
1280 parse_memory_type(code, mt);
1283 template <u
int8_t
id>
1286 vec<
typename std::enable_if_t<id == section_id::global_section, global_variable>>& elems) {
1287 parse_section_impl(code, elems, detail::get_max_global_section_elements(_options),
1290 template <u
int8_t
id>
1292 vec<
typename std::enable_if_t<id == section_id::export_section, export_entry>>& elems) {
1293 parse_section_impl(code, elems, detail::get_max_export_section_elements(_options),
1296 template <u
int8_t
id>
1298 typename std::enable_if_t<id == section_id::start_section, uint32_t>& start) {
1299 start = parse_varuint32(code);
1300 const func_type& ft = _mod->get_function_type(start);
1303 template <u
int8_t
id>
1306 vec<
typename std::enable_if_t<id == section_id::element_section, elem_segment>>& elems) {
1307 parse_section_impl(code, elems, detail::get_max_element_section_elements(_options),
1310 template <u
int8_t
id>
1312 vec<
typename std::enable_if_t<id == section_id::code_section, function_body>>& elems) {
1313 const void* code_start = code.raw() - code.offset();
1314 parse_section_impl(code, elems, detail::get_max_function_section_elements(_options),
1316 SYS_VM_ASSERT( elems.size() == _mod->functions.size(), wasm_parse_exception,
"code section must have the same size as the function section" );
1317 Writer code_writer(_allocator, code.bounds() - code.offset(), *_mod);
1318 imap.on_code_start(code_writer.get_base_addr(), code_start);
1319 for (
size_t i = 0; i < _function_bodies.size(); i++) {
1321 func_type& ft = _mod->types.at(_mod->functions.at(i));
1323 imap.on_function_start(code_writer.get_addr(), _function_bodies[i].first.raw());
1324 code_writer.emit_prologue(ft, fb.
locals, i);
1325 parse_function_body_code(_function_bodies[i].first, fb.
size, _function_bodies[i].second, code_writer, ft, local_types);
1326 code_writer.emit_epilogue(ft, fb.
locals, i);
1327 code_writer.finalize(fb);
1329 imap.on_code_end(code_writer.get_addr(), code.raw());
1331 template <u
int8_t
id>
1333 vec<
typename std::enable_if_t<id == section_id::data_section, data_segment>>& elems) {
1334 parse_section_impl(code, elems, detail::get_max_data_section_elements(_options),
1341 result.set(code, index);
1348 result.set(code, index);
1353 _globals_checker.on_mutable_global(_options, type);
1357 std::vector<const guarded_vector<uint8_t>*> export_names;
1358 export_names.reserve(_mod->exports.size());
1359 for (
uint32_t i = 0; i < _mod->exports.size(); ++i) {
1360 export_names.push_back(&_mod->exports[i].field_str);
1362 std::sort(export_names.begin(), export_names.end(), [](
auto* lhs,
auto* rhs) {
1363 return std::lexicographical_compare(lhs->raw(), lhs->raw() + lhs->size(), rhs->raw(), rhs->raw() + rhs->size());
1365 auto it = std::adjacent_find(export_names.begin(), export_names.end(), [](
auto* lhs,
auto* rhs) {
1366 return lhs->size() == rhs->size() && std::equal(lhs->raw(), lhs->raw() + lhs->size(), rhs->raw());
1368 SYS_VM_ASSERT(it == export_names.end(), wasm_parse_exception,
"duplicate export name");
1375 int64_t _current_function_index = -1;
1376 uint64_t _maximum_function_stack_usage = 0;
1377 std::vector<std::pair<wasm_code_ptr, detail::max_func_local_bytes_stack_checker<Options>>> _function_bodies;
1380 typename DebugInfo::builder imap;
varint< N > parse_varint(const wasm_code &code, size_t index)
uint8_t parse_flags(wasm_code_ptr &code)
void parse_elem_segment(wasm_code_ptr &code, elem_segment &es)
void on_mutable_global(uint8_t type)
uint32_t parse_magic(wasm_code_ptr &code)
decltype(std::declval< Writer >().emit_if()) branch_t
void parse_name_section(wasm_code_ptr &code)
void parse_function_body(wasm_code_ptr &code, function_body &fb, std::size_t idx)
void parse_section(wasm_code_ptr &code, vec< typename std::enable_if_t< id==section_id::table_section, table_type > > &elems)
int validate_utf8_code_point(wasm_code_ptr &code)
static uint8_t parse_varuint7(wasm_code_ptr &code)
void parse_module(wasm_code_ptr &code_ptr, size_t sz, module &mod, DebugInfo &debug)
void validate_utf8_string(wasm_code_ptr &code, uint32_t bytes)
void parse_import_entry(wasm_code_ptr &code, import_entry &entry)
void parse_export_entry(wasm_code_ptr &code, export_entry &entry)
void parse_section(wasm_code_ptr &code, vec< typename std::enable_if_t< id==section_id::code_section, function_body > > &elems)
void parse_section(wasm_code_ptr &code, vec< typename std::enable_if_t< id==section_id::export_section, export_entry > > &elems)
decltype(std::declval< Writer >().emit_end()) label_t
void parse_section(wasm_code_ptr &code, vec< typename std::enable_if_t< id==section_id::function_section, uint32_t > > &elems)
static int8_t parse_varint7(wasm_code_ptr &code)
T parse_raw(wasm_code_ptr &code)
void parse_section(wasm_code_ptr &code, vec< typename std::enable_if_t< id==section_id::element_section, elem_segment > > &elems)
static uint32_t parse_varuint32(wasm_code_ptr &code)
void parse_section(wasm_code_ptr &code, vec< typename std::enable_if_t< id==section_id::memory_section, memory_type > > &elems)
static int32_t parse_varint32(wasm_code_ptr &code)
void parse_section(wasm_code_ptr &code, vec< typename std::enable_if_t< id==section_id::data_section, data_segment > > &elems)
void parse_section(wasm_code_ptr &code, typename std::enable_if_t< id==section_id::start_section, uint32_t > &start)
void parse_init_expr(wasm_code_ptr &code, init_expr &ie, uint8_t type)
uint32_t parse_version(wasm_code_ptr &code)
varuint< N > parse_varuint(const wasm_code &code, size_t index)
void parse_function_body_code(wasm_code_ptr &code, size_t bounds, const detail::max_func_local_bytes_stack_checker< Options > &local_bytes_checker, Writer &code_writer, const func_type &ft, const local_types_t &local_types)
static uint8_t parse_varuint1(wasm_code_ptr &code)
void parse_section(wasm_code_ptr &code, vec< typename std::enable_if_t< id==section_id::type_section, func_type > > &elems)
void validate_exports() const
void parse_table_type(wasm_code_ptr &code, table_type &tt)
static int64_t parse_varint64(wasm_code_ptr &code)
void parse_memory_type(wasm_code_ptr &code, memory_type &mt)
void parse_section(wasm_code_ptr &code, vec< typename std::enable_if_t< id==section_id::global_section, global_variable > > &elems)
void parse_data_segment(wasm_code_ptr &code, data_segment &ds)
void parse_section(wasm_code_ptr &code, vec< typename std::enable_if_t< id==section_id::import_section, import_entry > > &elems)
guarded_vector< uint8_t > parse_utf8_string(wasm_code_ptr &code, std::uint32_t max_size)
binary_parser(growable_allocator &alloc, const Options &options=Options{})
void parse_custom(wasm_code_ptr &code)
void parse_section_impl(wasm_code_ptr &code, vec< Elem > &elems, std::uint32_t max_elements, ParseFunc &&elem_parse)
void parse_name_map(wasm_code_ptr &code, guarded_vector< name_assoc > &map)
void parse_func_type(wasm_code_ptr &code, func_type &ft)
module & parse_module(wasm_code &code, module &mod, DebugInfo &debug)
uint32_t parse_section_payload_len(wasm_code_ptr &code)
void parse_global_variable(wasm_code_ptr &code, global_variable &gv)
uint8_t parse_section_id(wasm_code_ptr &code)
constexpr void push_back(U &&val)
constexpr T & at(size_t i)
constexpr size_t size() const
constexpr auto get_max_func_local_bytes_no_stack_c(int) -> std::enable_if_t< std::is_pointer_v< decltype(&Options::max_func_local_bytes_flags)>, bool >
decltype(std::declval< Options >().max_mutable_global_bytes) max_mutable_globals_t
std::vector< uint8_t > wasm_code
max_func_local_bytes_flags_t
#define T(meth, val, expected)
float32_t f32_add(float32_t a, float32_t b)
float32_t f32_div(float32_t a, float32_t b)
bool f32_eq(float32_t a, float32_t b)
bool f32_le(float32_t a, float32_t b)
bool f32_lt(float32_t a, float32_t b)
float32_t f32_mul(float32_t a, float32_t b)
float32_t f32_sqrt(float32_t a)
float32_t f32_sub(float32_t a, float32_t b)
float64_t f64_add(float64_t a, float64_t b)
float64_t f64_div(float64_t a, float64_t b)
bool f64_eq(float64_t a, float64_t b)
bool f64_le(float64_t a, float64_t b)
bool f64_lt(float64_t a, float64_t b)
float64_t f64_mul(float64_t a, float64_t b)
float64_t f64_sqrt(float64_t a)
float64_t f64_sub(float64_t a, float64_t b)
const unsigned char expected_result[]
unsigned __int64 uint64_t
local_types_t(const func_type &ft, const guarded_vector< local_entry > &locals_arg)
uint8_t operator[](uint32_t local_idx) const
uint64_t locals_count() const
void top(uint8_t expected)
void pop(uint8_t expected)
operand_stack_type_tracker(const detail::max_func_local_bytes_stack_checker< Options > local_bytes_checker, const Options &options)
void pop_scope(uint8_t expected_result=types::pseudo)
std::variant< label_t, std::vector< branch_t > > relocations
void on_local(const Options &options, std::uint8_t type, std::uint32_t count)
max_func_local_bytes_checker(const Options &options, const func_type &ft)
void on_type(const Options &options, std::uint8_t type)
max_func_local_bytes_checker(const Options &, const func_type &)
static constexpr bool is_defined
void on_local(const Options &, std::uint8_t, const std::uint32_t)
void pop_stack(std::uint8_t)
void push_stack(const Options &, std::uint8_t)
void pop_stack(const Options &, std::uint8_t)
void push_stack(const Options &, std::uint8_t)
constexpr max_func_local_bytes_stack_checker(const max_func_local_bytes_checker< Options > &)
void push_stack(const Options &options, std::uint8_t type)
std::uint32_t unreachable_depth
constexpr max_func_local_bytes_stack_checker(const max_func_local_bytes_checker< Options > &base)
void pop_stack(const Options &options, std::uint8_t type)
void on_mutable_global(const Options &options, uint8_t type)
constexpr void on_mutable_global(const Options &, uint8_t)
void on_end(const Options &options)
void on_control(const Options &options)
void on_control(const Options &)
void on_end(const Options &)
guarded_vector< uint32_t > elems
guarded_vector< uint8_t > field_str
guarded_vector< value_type > param_types
guarded_vector< local_entry > locals
auto scoped_consume_items(std::size_t n)
guarded_vector< uint8_t > module_str
guarded_vector< uint8_t > field_str
guarded_vector< uint32_t > functions
guarded_vector< import_entry > imports
guarded_vector< global_variable > globals
guarded_vector< elem_segment > elements
guarded_vector< export_entry > exports
guarded_vector< table_type > tables
guarded_vector< memory_type > memories
guarded_vector< func_type > types
guarded_vector< function_body > code
guarded_vector< data_segment > data
guarded_vector< uint32_t > table
#define SYS_VM_ASSERT(expr, exc_type, msg)
#define CASTOP(dst, opname, src)
#define MAX_ELEMENTS(name, default_)
#define PARSER_OPTION(name, default_, type)
yubihsm_pkcs11_object_template template
memcpy((char *) pInfo->slotDescription, s, l)