Skip to content

fix(vm): treat unallocated tables as empty instead of panicking - #175

Open
dmitry123 wants to merge 1 commit into
develfrom
claude/rwasm-modules-panic-fix-bc2c80
Open

fix(vm): treat unallocated tables as empty instead of panicking#175
dmitry123 wants to merge 1 commit into
develfrom
claude/rwasm-modules-panic-fix-bc2c80

Conversation

@dmitry123

Copy link
Copy Markdown
Member

Fixes FLU-1097.

Problem

A verified rwasm module can reference a table index it never grows, and every table op then panicked the interpreter instead of trapping:

let code = instruction_set! { TableSize(3) Drop Return };
// new_verified -> Ok; execute ->
// panicked at src/vm/executor/table.rs:10: rwasm: unresolved table segment

Tables enter RwasmStore::tables only when the module executes TableGrow — the only op that used .entry(idx).or_default(). Verification accepts any index < N_MAX_TABLES and cannot do better, since table indices carry no declaration. Nine call sites across table.rs and control_flow.rs did .expect("rwasm: unresolved table segment") / .expect("rwasm: missing table"), so in a node a bad contract became a process crash — mid-execution, leaving the store with a dirty value stack, call stack, and last_signature.

Fix

All table lookups go through a single resolve_table helper that materializes a missing table as an empty one, so an ungrown table behaves as a zero-length table. This matches Wasm semantics more closely than trapping outright: table.size returns 0, table.fill/table.copy with n == 0 succeed, and every element access is bound-checked into TrapCode::TableOutOfBounds by TableEntity. Behaviour for translator-produced modules is unchanged — emit_table_segment emits a TableGrow per declared table, so the tables always exist.

Unresolved syscall index (4a)

Left as-is per the discussion on the ticket: an index the import linker can't resolve is a fatal error, not a trap. The params/results counts come from the linker, so an unresolved index leaves no way to know how many values to pop, and any guess would desynchronize the value stack — zkVM proving needs the same trace on every run. Restricting the reachable syscall set is the linker's job, and always_failing_syscall_handler is how a declared-but-forbidden syscall is rejected. invoke_syscall now carries a doc comment explaining this.

Tests

New tests/tables.rs — 12 cases, each building a module that passes new_verified and then executing it:

  • table.size of an ungrown table returns 0
  • table.get / table.set / table.fill / table.copy (across tables and within one) trap with TableOutOfBounds
  • zero-length table.fill / table.copy on ungrown tables succeed, per Wasm semantics
  • table.init into an ungrown table traps
  • call_indirect / return_call_indirect through an ungrown table trap
  • a grown table still reports its size (regression guard)

cargo test and cargo clippy --all-targets --all-features are clean. The two --all-features failures in tests/wasmtime.rs (test_wasmtime_disabled_f32_sqrt, test_wasmtime_disabled_f64_div) are pre-existing on devel and unrelated — they were verified to fail on a clean checkout too.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@dmitry123, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 57 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 09ee2ab0-3dff-45bc-9844-d7d0a13152da

📥 Commits

Reviewing files that changed from the base of the PR and between b8f6091 and 429f187.

📒 Files selected for processing (4)
  • src/vm/executor.rs
  • src/vm/executor/control_flow.rs
  • src/vm/executor/table.rs
  • tests/tables.rs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

Criterion results (vs baseline)


running 75 tests
test compiler::compiled_expr::tests::compiledexpr_eval_const_returns_none_for_global_or_funcref ... ignored
test compiler::compiled_expr::tests::compiledexpr_from_const_roundtrips ... ignored
test compiler::compiled_expr::tests::compiledexpr_funcref_and_global_introspection ... ignored
test compiler::compiled_expr::tests::compiledexpr_new_global_get_uses_context ... ignored
test compiler::compiled_expr::tests::compiledexpr_new_i32_add_mixed_const_and_global ... ignored
test compiler::compiled_expr::tests::compiledexpr_new_i32_add_mixed_global_and_funcref ... ignored
test compiler::compiled_expr::tests::compiledexpr_new_i32_add_wraps ... ignored
test compiler::compiled_expr::tests::compiledexpr_new_i32_const ... ignored
test compiler::compiled_expr::tests::compiledexpr_new_i32_sub_wraps ... ignored
test compiler::compiled_expr::tests::compiledexpr_new_i64_const ... ignored
test compiler::compiled_expr::tests::compiledexpr_new_i64_mul_wraps ... ignored
test compiler::compiled_expr::tests::compiledexpr_new_ref_func_uses_context ... ignored
test compiler::compiled_expr::tests::compiledexpr_zero_is_zero ... ignored
test compiler::compiled_expr::tests::constop_eval_returns_value ... ignored
test compiler::compiled_expr::tests::empty_eval_context_always_none ... ignored
test compiler::compiled_expr::tests::eval_with_context_reads_globals_and_funcs ... ignored
test compiler::compiled_expr::tests::expr_op_combines_operands_and_propagates_none ... ignored
test compiler::compiled_expr::tests::funcrefop_reads_from_context ... ignored
test compiler::compiled_expr::tests::globalop_maps_value_kinds_correctly ... ignored
test compiler::compiled_expr::tests::op_clone_panics_for_expr_variant - should panic ... ignored
test compiler::compiled_expr::tests::op_clone_works_for_non_expr_variants ... ignored
test compiler::compiled_expr::tests::op_constant_encodes_f32_f64_bits ... ignored
test compiler::compiled_expr::tests::op_constant_encodes_funcref_externref_ids ... ignored
test compiler::compiled_expr::tests::op_constant_encodes_i32_i64 ... ignored
test compiler::drop_keep::tests::test_drop_keep_translation ... ignored
test compiler::func_type_registry::tests::deduplicates_matching_signatures ... ignored
test compiler::func_type_registry::tests::index_lookup_is_stable ... ignored
test compiler::func_type_registry::tests::resolves_unique_signatures_correctly ... ignored
test compiler::parser::tests::unsupported_component_model_returns_error ... ignored
test module::tests::test_decode_exact_rejects_trailing_garbage ... ignored
test module::tests::test_decode_module_wo_source_pc ... ignored
test module::tests::test_decode_rejects_partial_source_pc ... ignored
test module::tests::test_endianness ... ignored
test module::tests::test_module_encoding ... ignored
test module::verification::tests::accepts_verified_encoded_module ... ignored
test module::verification::tests::regular_construction_does_not_verify ... ignored
test module::verification::tests::regular_decode_does_not_verify ... ignored
test module::verification::tests::rejects_branch_target_outside_code_section ... ignored
test module::verification::tests::rejects_call_target_outside_code_section ... ignored
test module::verification::tests::rejects_missing_table_index_payload ... ignored
test module::verification::tests::rejects_section_index_outside_limits ... ignored
test module::verification::tests::rejects_source_pc_outside_code_section ... ignored
test module::verification::tests::rejects_zero_local_depth ... ignored
test strategy::types::tests::checked_memory_range_end_rejects_overflow ... ignored
test types::nan_preserving_float::tests::test_neg_nan_f32 ... ignored
test types::nan_preserving_float::tests::test_neg_nan_f64 ... ignored
test types::nan_preserving_float::tests::test_ops_f32 ... ignored
test types::nan_preserving_float::tests::test_ops_f64 ... ignored
test types::opcode::tests::test_fpu_opcode_encoding_uses_offset ... ignored
test types::opcode::tests::test_opcode_code_values ... ignored
test types::opcode::tests::test_opcode_encoding ... ignored
test types::opcode::tests::test_opcode_encoding_uses_explicit_code ... ignored
test types::opcode::tests::test_opcode_size ... ignored
test types::units::tests::bytes_new16 ... ignored
test types::units::tests::bytes_new32 ... ignored
test types::units::tests::bytes_new64 ... ignored
test types::units::tests::pages_checked_add ... ignored
test types::units::tests::pages_checked_sub ... ignored
test types::units::tests::pages_max ... ignored
test types::units::tests::pages_new ... ignored
test types::units::tests::pages_to_bytes ... ignored
test types::value::copysign_regression_works ... ignored
test types::value::wasm_float_max_regression_works ... ignored
test types::value::wasm_float_min_regression_works ... ignored
test vm::store::tests::clamps_runtime_memory_limit_to_global_maximum ... ignored
test wasmtime::tests::test_call_with_charging_linear_wasmtime ... ignored
test wasmtime::tests::test_call_with_charging_param_overflow_wasmtime ... ignored
test wasmtime::tests::test_call_with_charging_quadratic_wasmtime ... ignored
test wasmtime::tests::test_wasmtime_caller_memory_read_into_vec_checks_bounds_before_allocating ... ignored
test wasmtime::tests::test_wasmtime_caller_missing_memory_returns_trap ... ignored
test wasmtime::tests::test_wasmtime_executor_memory_read_into_vec_checks_bounds_before_allocating ... ignored
test wasmtime::tests::test_wasmtime_executor_missing_entrypoint_returns_trap ... ignored
test wasmtime::tests::test_wasmtime_snapshot_missing_memory_returns_trap ... ignored
test wasmtime::types::tests::maps_unknown_wasmtime_error_to_illegal_opcode ... ignored
test wasmtime::types::tests::maps_wasmtime_traps_to_rwasm_traps ... ignored

test result: ok. 0 passed; 0 failed; 75 ignored; 0 measured; 0 filtered out; finished in 0.00s

Comparisons/bench_native
                        time:   [5.5344 ns 5.6643 ns 5.8053 ns]
Found 103 outliers among 1000 measurements (10.30%)
  25 (2.50%) high mild
  78 (7.80%) high severe
Comparisons/bench_strategy_wasmtime
                        time:   [20.845 µs 21.430 µs 22.065 µs]
Found 153 outliers among 1000 measurements (15.30%)
  101 (10.10%) high mild
  52 (5.20%) high severe
Comparisons/bench_strategy_rwasm
                        time:   [14.124 µs 14.444 µs 14.799 µs]
Found 124 outliers among 1000 measurements (12.40%)
  55 (5.50%) high mild
  69 (6.90%) high severe

Heads-up: runner perf is noisy; treat deltas as a smoke check.

@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.75000% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/vm/executor/table.rs 92.85% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant