Skip to content

fix: vm fatal errors - #21

Open
kp2pml30 wants to merge 3 commits into
v0.2-devfrom
pr/v0.2/fix/vm-fatal-errors
Open

fix: vm fatal errors#21
kp2pml30 wants to merge 3 commits into
v0.2-devfrom
pr/v0.2/fix/vm-fatal-errors

Conversation

@kp2pml30

@kp2pml30 kp2pml30 commented Aug 10, 2026

Copy link
Copy Markdown
Member

Auto-opened executor mirror of genlayerlabs/genvm-manager#24.

Carries the executor-side work for that manager PR. Auto-closed as merged when the manager PR lands (its pr/v0.2/fix/vm-fatal-errors branch is moved onto v0.2-dev).

Summary by CodeRabbit

  • New Features
    • Added cross-executor contract calls with permission, signer, recursion, memory, and storage controls.
    • Added deterministic fuel budgeting across nested executions and LLM calls.
    • Improved host communication with initialization data, nested requests, and coordinated completion handling.
  • Bug Fixes
    • Enforced recursion and memory limits more reliably.
    • Improved fee handling and host flushing when executions end.
    • Standardized execution result hashing and nested-call error handling.

* feat(sdk): call contracts of another major through the host ✨
* refactor(rt): bound recursion with a budget minted by the chain root ♻️
* refactor(exe): carry nested execution state as one explicit group ♻️
* fix(exe): fold the callee's small hash on the nested route 🐛🔒️
* feat(exe): refuse a crossing call while custom runners are loaded ✨
* feat(host): write caller-supplied hello bytes and drop notify_finished ✨
* fix(supervisor): load precompiled modules only for registry runners 🔒️
* fix(sdk): stop exporting nondet permission across the boundary 🐛
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The executor now supports cross-executor nested calls. It propagates nested permissions, recursion, memory, storage, signer, stack, fee, and deterministic-fuel state. Host connections support hello data, nested protocol requests, shared result hashes, and final flushing.

Changes

Nested execution and host integration

Layer / File(s) Summary
Shared interfaces and result hashing
executor/crates/common/*, executor/crates/modules-interfaces/Cargo.toml, executor/src/host/mod.rs, executor/src/rt/vm/mod.rs
The common crate re-exports host-function interfaces from genvm_modules_interfaces. Result hashing uses the shared implementation.
Nested runtime state and limits
executor/src/exe/run.rs, executor/src/lib.rs, executor/src/rt/*, executor/crates/common/src/expr/*
Nested permissions, fee buckets, memory limits, storage-write access, recursion, signer data, stacks, and deterministic fuel budgets are propagated through runtime setup. Spawn errors preserve boxed VM state.
Host connection and nested protocol
executor/src/host/mod.rs, executor/src/exe/run.rs
Hosts receive hello data, resolve nested executors, process length-prefixed nested requests, and flush through MultiHost. Unix-socket tests cover the protocol.
SDK routing and deterministic fuel
executor/src/wasi/genlayer_sdk.rs
CallContract routes nested calls across executors, validates replies, propagates metadata, and accounts for LLM fuel through shared deterministic-fuel state.
Supporting safety and lint updates
executor/crates/common/src/io.rs, executor/crates/sdk-rs/abi/consts.rs, executor/src/domain/fees.rs
Raw descriptor safety requirements and targeted lint allowances were updated.

Estimated code review effort: 5 (Critical) | ~90 minutes

Sequence Diagram(s)

sequenceDiagram
  participant VM
  participant CallContract
  participant Host
  participant NestedExecutor
  VM->>CallContract: CallContract request
  CallContract->>Host: resolve_callcontract_executor
  Host-->>CallContract: executor address
  CallContract->>Host: run_nested envelope
  Host->>NestedExecutor: nested request
  NestedExecutor-->>Host: NestedRunReply
  Host-->>CallContract: decoded reply
  CallContract-->>VM: validated nested result
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title identifies VM fatal-error fixes, which is directly related to the executor changes despite not describing all implementation details.
Docstring Coverage ✅ Passed Docstring coverage is 96.43% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pr/v0.2/fix/vm-fatal-errors

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
executor/src/rt/mod.rs (1)

145-162: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a test for the non-nested budget.

The test covers only DetFuelBudget::new(Some(..)). The None case is the top-level production path: remaining must return the host value unchanged, and consume must not change that.

🧪 Proposed additional test
     #[tokio::test]
     async fn imported_deterministic_fuel_is_the_initial_budget() {
         let budget = DetFuelBudget::new(Some(primitive_types::U256::from(10)));
         assert_eq!(
             budget.remaining(primitive_types::U256::from(20)).await,
             primitive_types::U256::from(10)
         );
         budget.consume(primitive_types::U256::from(3)).await;
         assert_eq!(
             budget.remaining(primitive_types::U256::from(20)).await,
             primitive_types::U256::from(7)
         );
     }
+
+    #[tokio::test]
+    async fn absent_budget_passes_host_fuel_through() {
+        let budget = DetFuelBudget::new(None);
+        assert_eq!(
+            budget.remaining(primitive_types::U256::from(20)).await,
+            primitive_types::U256::from(20)
+        );
+        budget.consume(primitive_types::U256::from(5)).await;
+        assert_eq!(
+            budget.remaining(primitive_types::U256::from(20)).await,
+            primitive_types::U256::from(20)
+        );
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@executor/src/rt/mod.rs` around lines 145 - 162, Add a test alongside
imported_deterministic_fuel_is_the_initial_budget covering
DetFuelBudget::new(None); verify remaining returns the supplied host value
unchanged before and after consume, confirming consume has no effect for the
non-nested budget.
executor/src/exe/run.rs (1)

316-317: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

memory_limit is accepted from the envelope without a local ceiling.

remaining_recursion in executor/src/lib.rs (Lines 401-405) clamps the supplied value with .min(public_abi::top_limits::VM_RECURSION), and the comment there states that a budget minted elsewhere is a remainder, not an authority. memory_limit takes the envelope value directly.

The current effect is bounded, because the non-nested default is already u32::MAX. A caller can therefore only fail to lower the limit, not raise it beyond today's top-level default. Apply the same clamp so the two limits follow one rule if a local memory ceiling is introduced later.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@executor/src/exe/run.rs` around lines 316 - 317, Clamp the envelope-provided
memory_limit in the run setup to public_abi::top_limits::VM_MEMORY (or the
established memory top-limit symbol), matching the remaining_recursion handling
in remaining_recursion. Preserve the existing nested lookup and u32::MAX default
while ensuring supplied values cannot exceed the local ceiling.
executor/src/wasi/genlayer_sdk.rs (1)

821-838: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Both CallContract paths perform work the other path discards.

resolve_callcontract_executor (Lines 826-831) runs on every CallContract, including the common same-executor call where routing_payload is None. That adds one host round trip to the in-process path.

check_major_and_resolve_code_slot (Lines 833-838) also runs before the routing branch. It is a storage read, and on the routed path its code_slot feeds only vm_data.conf.topmost_runner_id, which the routed branch never uses — the envelope sends the literal NestedRunnerId("contract") instead.

Move check_major_and_resolve_code_slot and the vm_data construction into the routing_payload.is_none() branch. Each path then pays only for the work it uses.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@executor/src/wasi/genlayer_sdk.rs` around lines 821 - 838, The CallContract
flow performs unnecessary host and storage work before branching. Keep
resolve_callcontract_executor for routing determination, but move
check_major_and_resolve_code_slot and the dependent vm_data construction into
the routing_payload.is_none() branch; ensure the routed branch continues using
its existing literal NestedRunnerId("contract") envelope value.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@executor/src/lib.rs`:
- Around line 193-212: Update convert_nested_permissions to reject forbidden
NestedPermissions bits before constructing wasi::base::Config, matching the
validation performed by handle in executor/src/exe/run.rs. Ensure WRITE_STORAGE
and SEND_MESSAGES are rejected for nested calls rather than mapped into the
child configuration, while preserving conversion of allowed permissions.

In `@executor/src/wasi/genlayer_sdk.rs`:
- Line 986: The nested memory limit currently crosses the executor boundary
without a shared ceiling. In executor/src/wasi/genlayer_sdk.rs:986, send a
reduced memory share or document the manager-side aggregate ceiling that makes
the full remaining value safe; in executor/src/exe/run.rs:316-317, clamp
n.memory_limit to a local maximum before passing it to create_supervisor,
following the existing remaining_recursion clamp pattern.

---

Nitpick comments:
In `@executor/src/exe/run.rs`:
- Around line 316-317: Clamp the envelope-provided memory_limit in the run setup
to public_abi::top_limits::VM_MEMORY (or the established memory top-limit
symbol), matching the remaining_recursion handling in remaining_recursion.
Preserve the existing nested lookup and u32::MAX default while ensuring supplied
values cannot exceed the local ceiling.

In `@executor/src/rt/mod.rs`:
- Around line 145-162: Add a test alongside
imported_deterministic_fuel_is_the_initial_budget covering
DetFuelBudget::new(None); verify remaining returns the supplied host value
unchanged before and after consume, confirming consume has no effect for the
non-nested budget.

In `@executor/src/wasi/genlayer_sdk.rs`:
- Around line 821-838: The CallContract flow performs unnecessary host and
storage work before branching. Keep resolve_callcontract_executor for routing
determination, but move check_major_and_resolve_code_slot and the dependent
vm_data construction into the routing_payload.is_none() branch; ensure the
routed branch continues using its existing literal NestedRunnerId("contract")
envelope value.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 978c5b0c-4890-46d0-bf21-98fb5927df0a

📥 Commits

Reviewing files that changed from the base of the PR and between 1c8126d and 27ef6a0.

⛔ Files ignored due to path filters (314)
  • executor/Cargo.lock is excluded by !**/*.lock, !**/*.lock
  • executor/crates/common/Cargo.lock is excluded by !**/*.lock, !**/*.lock
  • executor/crates/modules-interfaces/Cargo.lock is excluded by !**/*.lock, !**/*.lock
  • tests/integration/prompt/json_random/json_random.jsonnet is excluded by !tests/**
  • tests/integration/stable/agentic/wasi/environ_args/environ_args.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/agentic/wasi/environ_args/environ_args.jsonnet is excluded by !tests/**
  • tests/integration/stable/agentic/wasi/hash_random/hash_random.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/agentic/wasi/hash_random/hash_random.jsonnet is excluded by !tests/**
  • tests/integration/stable/agentic/wasi/id_repr/id_repr.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/agentic/wasi/id_repr/id_repr.jsonnet is excluded by !tests/**
  • tests/integration/stable/agentic/wasi/set_order/set_order.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/agentic/wasi/set_order/set_order.jsonnet is excluded by !tests/**
  • tests/integration/stable/agentic/wasi/wasi_clock/wasi_clock.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/agentic/wasi/wasi_clock/wasi_clock.jsonnet is excluded by !tests/**
  • tests/integration/stable/agentic/wasi/wasi_random/wasi_random.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/agentic/wasi/wasi_random/wasi_random.jsonnet is excluded by !tests/**
  • tests/integration/stable/bench/read_tree_map.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/bench/read_tree_map.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/bench/read_tree_map.jsonnet is excluded by !tests/**
  • tests/integration/stable/exploits/call_wasi_extra.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/exploits/call_wasi_extra.jsonnet is excluded by !tests/**
  • tests/integration/stable/exploits/disagree_in_sandbox.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/exploits/disagree_in_sandbox.jsonnet is excluded by !tests/**
  • tests/integration/stable/exploits/flt.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/exploits/flt.jsonnet is excluded by !tests/**
  • tests/integration/stable/exploits/fork_bomb.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/exploits/fork_bomb.jsonnet is excluded by !tests/**
  • tests/integration/stable/exploits/inf-loop.jsonnet is excluded by !tests/**
  • tests/integration/stable/exploits/method_init.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/exploits/method_init.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/exploits/method_init.jsonnet is excluded by !tests/**
  • tests/integration/stable/exploits/method_private.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/exploits/method_private.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/exploits/method_private.jsonnet is excluded by !tests/**
  • tests/integration/stable/exploits/oom.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/exploits/oom.jsonnet is excluded by !tests/**
  • tests/integration/stable/exploits/rec.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/exploits/rec.jsonnet is excluded by !tests/**
  • tests/integration/stable/exploits/rec_1023.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/exploits/rec_1023.jsonnet is excluded by !tests/**
  • tests/integration/stable/exploits/rec_1024.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/exploits/rec_1024.jsonnet is excluded by !tests/**
  • tests/integration/stable/exploits/rec_tail.jsonnet is excluded by !tests/**
  • tests/integration/stable/exploits/storage_rw_long.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/exploits/storage_rw_long.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/exploits/storage_rw_long.jsonnet is excluded by !tests/**
  • tests/integration/stable/exploits/unreachable.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/exploits/unreachable.jsonnet is excluded by !tests/**
  • tests/integration/stable/nondet/leader_errors/leader_no_nondet.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/leader_errors/leader_no_nondet.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/leader_errors/leader_no_nondet.jsonnet is excluded by !tests/**
  • tests/integration/stable/nondet/leader_errors/simple_leader.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/leader_errors/simple_leader.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/leader_errors/simple_leader.jsonnet is excluded by !tests/**
  • tests/integration/stable/nondet/leader_errors/simple_valid_err_err.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/leader_errors/simple_valid_err_err.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/leader_errors/simple_valid_err_err.jsonnet is excluded by !tests/**
  • tests/integration/stable/nondet/leader_errors/simple_valid_err_exit.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/leader_errors/simple_valid_err_exit.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/leader_errors/simple_valid_err_exit.jsonnet is excluded by !tests/**
  • tests/integration/stable/nondet/leader_errors/simple_valid_err_exit_wrong_err.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/leader_errors/simple_valid_err_exit_wrong_err.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/leader_errors/simple_valid_err_exit_wrong_err.jsonnet is excluded by !tests/**
  • tests/integration/stable/nondet/leader_errors/simple_valid_err_nerr.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/leader_errors/simple_valid_err_nerr.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/leader_errors/simple_valid_err_nerr.jsonnet is excluded by !tests/**
  • tests/integration/stable/nondet/metod_det_get_webpage.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/metod_det_get_webpage.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/metod_det_get_webpage.jsonnet is excluded by !tests/**
  • tests/integration/stable/nondet/trivial.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/trivial.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/trivial.jsonnet is excluded by !tests/**
  • tests/integration/stable/nondet/validator/rollback_agree.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/validator/rollback_agree.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/validator/rollback_agree.jsonnet is excluded by !tests/**
  • tests/integration/stable/nondet/validator/rollback_disagree.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/validator/rollback_disagree.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/validator/rollback_disagree.jsonnet is excluded by !tests/**
  • tests/integration/stable/nondet/validator/rollback_imm.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/validator/rollback_imm.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/validator/rollback_imm.1.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/validator/rollback_imm.1_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/validator/rollback_imm.jsonnet is excluded by !tests/**
  • tests/integration/stable/nondet/validator/sync.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/validator/sync.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/validator/sync.jsonnet is excluded by !tests/**
  • tests/integration/stable/nondet/validator/sync_err.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/validator/sync_err.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/nondet/validator/sync_err.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/balances/balance.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/balances/balance.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/balances/balance_eth.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/balances/balance_eth.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/balances/sandbox_overspend.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/balances/sandbox_overspend.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/balances/sandbox_overspend_2.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/balances/sandbox_overspend_2.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/balances/undefined_all.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/balances/undefined_all.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/balances/undefined_all.0_0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/balances/undefined_all.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/balances/undefined_method.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/balances/undefined_method.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/balances/undefined_method.0_0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/balances/undefined_method.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/balances/undefined_method_payable.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/balances/undefined_method_payable.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/balances/undefined_method_payable.0_0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/balances/undefined_method_payable.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/balances/undefined_receive.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/balances/undefined_receive.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/balances/undefined_receive.0_0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/balances/undefined_receive.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/embeddings/simple.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/embeddings/simple.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/embeddings/simple.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/embeddings/simple_det.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/embeddings/simple_det.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/embeddings/simple_det.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/embeddings/simple_tokenizer.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/embeddings/simple_tokenizer.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/embeddings/simple_tokenizer.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/embeddings/vecdb.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/embeddings/vecdb.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/events/post_event.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/events/post_event.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/intercontract/call_view.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/intercontract/call_view.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/intercontract/call_view.0_0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/intercontract/call_view.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/intercontract/call_view_iface.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/intercontract/call_view_iface.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/intercontract/call_view_iface.0_0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/intercontract/call_view_iface.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/intercontract/deploy.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/intercontract/deploy.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/intercontract/deploy_salt.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/intercontract/deploy_salt.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/intercontract/send_message.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/intercontract/send_message.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/intercontract/send_message_eth.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/intercontract/send_message_eth.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/intercontract/send_message_on.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/intercontract/send_message_on.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/intercontract/send_message_on.0_0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/intercontract/send_message_on.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/other/meth/method_init.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/meth/method_init.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/other/meth/method_init_wrong_name.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/meth/method_init_wrong_name.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/other/meth/method_public.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/meth/method_public.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/meth/method_public.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/other/meth/method_retn.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/meth/method_retn.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/meth/method_retn.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/other/meth/method_retn_view.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/meth/method_retn_view.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/meth/method_retn_view.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/other/meth/method_rollback.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/meth/method_rollback.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/meth/method_rollback.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/other/ret/returns.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/ret/returns.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/ret/returns.1.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/ret/returns.1_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/ret/returns.2.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/ret/returns.2_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/ret/returns.3.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/ret/returns.3_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/ret/returns.4.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/ret/returns.4_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/ret/returns.5.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/ret/returns.5_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/ret/returns.6.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/ret/returns.6_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/ret/returns.7.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/ret/returns.7_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/ret/returns.8.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/ret/returns.8_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/ret/returns.9.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/ret/returns.9_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/other/ret/returns.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/pitfalls/error_msg.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/pitfalls/error_msg.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/pitfalls/error_msg.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/pitfalls/error_msg_overridden.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/pitfalls/error_msg_overridden.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/pitfalls/error_msg_overridden.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/pitfalls/multi_contract.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/pitfalls/multi_contract.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/pitfalls/pub_ctor.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/pitfalls/pub_ctor.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/pitfalls/store_proxy.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/pitfalls/store_proxy.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/rollbacks/call_view.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/rollbacks/call_view.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/rollbacks/call_view.0_0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/rollbacks/call_view.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/rollbacks/nondet.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/rollbacks/nondet.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/rollbacks/simple.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/rollbacks/simple.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/sandbox/det/s/assign-json.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/sandbox/det/s/assign-json.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/sandbox/det/s/assign-json.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/sandbox/det/s/exit.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/sandbox/det/s/exit.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/sandbox/det/s/exit.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/sandbox/det/s/print.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/sandbox/det/s/print.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/sandbox/det/s/print.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/sandbox/det/s/rollback.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/sandbox/det/s/rollback.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/sandbox/det/s/rollback.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/sandbox/det/s/sandbox.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/sandbox/det/s/sandbox.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/sandbox/det/s/sandbox.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/sandbox/det/sandbox_write.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/sandbox/det/sandbox_write.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/sandbox/non-det/s/assign-json.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/sandbox/non-det/s/assign-json.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/sandbox/non-det/s/assign-json.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/sandbox/non-det/s/exit.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/sandbox/non-det/s/exit.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/sandbox/non-det/s/exit.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/sandbox/non-det/s/print.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/sandbox/non-det/s/print.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/sandbox/non-det/s/print.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/sandbox/non-det/s/rollback.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/sandbox/non-det/s/rollback.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/sandbox/non-det/s/rollback.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/schemas/complex_types.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/schemas/complex_types.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/schemas/complex_types.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/schemas/prim_types.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/schemas/prim_types.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/schemas/prim_types.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/schemas/ret-float.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/schemas/ret-float.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/schemas/ret-float.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/schemas/ret-tuple.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/schemas/ret-tuple.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/schemas/ret-tuple.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/schemas/ret.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/schemas/ret.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/schemas/ret.jsonnet is excluded by !tests/**
  • tests/integration/stable/py/schemas/trivial.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/schemas/trivial.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/py/schemas/trivial.jsonnet is excluded by !tests/**
  • tests/integration/stable/runners/dup-dependency.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/runners/dup-dependency.jsonnet is excluded by !tests/**
  • tests/integration/stable/runners/env-template.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/runners/env-template.jsonnet is excluded by !tests/**
  • tests/integration/stable/runners/lock/lock.jsonnet is excluded by !tests/**
  • tests/integration/stable/runners/malformed_runner.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/runners/malformed_runner.jsonnet is excluded by !tests/**
  • tests/integration/stable/runners/multi-file/contract/multi-file.jsonnet is excluded by !tests/**
  • tests/integration/stable/runners/no_runner.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/runners/no_runner.jsonnet is excluded by !tests/**
  • tests/integration/stable/runners/zip/no-zip.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/runners/zip/no-zip.jsonnet is excluded by !tests/**
  • tests/integration/stable/runners/zip/zip.jsonnet is excluded by !tests/**
  • tests/integration/stable/self-run/datetime.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/self-run/datetime.jsonnet is excluded by !tests/**
  • tests/integration/stable/self-run/floats.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/self-run/floats.jsonnet is excluded by !tests/**
  • tests/integration/stable/self-run/formats.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/self-run/formats.jsonnet is excluded by !tests/**
  • tests/integration/stable/self-run/issue_163.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/self-run/issue_163.jsonnet is excluded by !tests/**
  • tests/integration/stable/self-run/module/np.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/self-run/module/np.jsonnet is excluded by !tests/**
  • tests/integration/stable/self-run/module/pil.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/self-run/module/pil.jsonnet is excluded by !tests/**
  • tests/integration/stable/self-run/re.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/self-run/re.jsonnet is excluded by !tests/**
  • tests/integration/stable/self-run/typing_is_ok.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/self-run/typing_is_ok.jsonnet is excluded by !tests/**
  • tests/integration/stable/storage/alloc_generic.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/storage/alloc_generic.jsonnet is excluded by !tests/**
  • tests/integration/stable/storage/alloc_generic_err.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/storage/alloc_generic_err.jsonnet is excluded by !tests/**
  • tests/integration/stable/storage/base.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/storage/base.jsonnet is excluded by !tests/**
  • tests/integration/stable/storage/floats.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/storage/floats.jsonnet is excluded by !tests/**
  • tests/integration/stable/storage/gvm-89.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/storage/gvm-89.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/storage/gvm-89.jsonnet is excluded by !tests/**
  • tests/integration/stable/storage/locking/default-frozen.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/storage/locking/default-frozen.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/storage/locking/default-frozen.0_0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/storage/locking/default-frozen.jsonnet is excluded by !tests/**
  • tests/integration/stable/storage/locking/modify_ctor.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/storage/locking/modify_ctor.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/storage/locking/modify_ctor.jsonnet is excluded by !tests/**
  • tests/integration/stable/storage/locking/modify_later.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/storage/locking/modify_later.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/storage/locking/modify_later.0_0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/storage/locking/modify_later.jsonnet is excluded by !tests/**
  • tests/integration/stable/storage/np.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/storage/np.jsonnet is excluded by !tests/**
  • tests/integration/stable/storage/persists.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/storage/persists.0_0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/storage/persists.jsonnet is excluded by !tests/**
  • tests/integration/stable/storage/read_nondet.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/storage/read_nondet.jsonnet is excluded by !tests/**
  • tests/integration/stable/storage/storage_tree_map.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/storage/storage_tree_map.jsonnet is excluded by !tests/**
  • tests/integration/stable/storage/to_str.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/storage/to_str.jsonnet is excluded by !tests/**
  • tests/integration/stable/storage/tree_map_nested.0.hash is excluded by !**/*.hash, !tests/**
  • tests/integration/stable/storage/tree_map_nested.jsonnet is excluded by !tests/**
📒 Files selected for processing (13)
  • executor/codegen/data/host-fns.json
  • executor/crates/common/Cargo.toml
  • executor/crates/common/src/host_fns.rs
  • executor/crates/common/src/lib.rs
  • executor/crates/modules-interfaces/Cargo.toml
  • executor/src/exe/run.rs
  • executor/src/host/mod.rs
  • executor/src/lib.rs
  • executor/src/rt/memlimiter.rs
  • executor/src/rt/mod.rs
  • executor/src/rt/supervisor/mod.rs
  • executor/src/rt/vm/mod.rs
  • executor/src/wasi/genlayer_sdk.rs
💤 Files with no reviewable changes (2)
  • executor/crates/common/src/host_fns.rs
  • executor/codegen/data/host-fns.json

Comment thread executor/src/lib.rs
Comment on lines +193 to +212
fn convert_nested_permissions(
permissions: genvm_modules_interfaces::NestedPermissions,
state_mode: public_abi::StorageType,
topmost_runner_id: runners::Id,
) -> wasi::base::Config {
use genvm_modules_interfaces::NestedPermissions as P;

wasi::base::Config {
needs_error_fingerprint: true,
is_deterministic: permissions.contains(P::DETERMINISTIC),
can_read_storage: permissions.contains(P::READ_STORAGE),
can_write_storage: permissions.contains(P::WRITE_STORAGE),
can_spawn_nondet: permissions.contains(P::SPAWN_NONDET),
can_send_messages: permissions.contains(P::SEND_MESSAGES),
can_call_others: permissions.contains(P::CALL_OTHERS),
can_register_runners: permissions.contains(P::REGISTER_RUNNERS),
state_mode,
topmost_runner_id,
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find every caller of run_with / run_with_impl / create_supervisor to see which ones
# validate nested permissions first.
set -euo pipefail

rg -nP --type=rust -C 5 '\b(run_with|run_with_impl|create_supervisor)\s*\('
rg -nP --type=rust -C 3 'check_nested_permissions'

Repository: genlayerlabs/genvm-executor

Length of output: 165


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "tracked rust files:"
git ls-files '*.rs' | sed -n '1,120p'

echo
echo "try locate files:"
fd -a 'lib\.rs|run\.rs' . | sed -n '1,120p'

Repository: genlayerlabs/genvm-executor

Length of output: 4807


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "executor/src/lib.rs relevant section:"
sed -n '163,220p' executor/src/lib.rs | nl -ba -v163

echo
echo "executor/src/exe/run.rs relevant section:"
sed -n '1,90p' executor/src/exe/run.rs | nl -ba -v1

echo
echo "run_with/create_supervisor references in tracked Rust files:"
rg -n -C 4 'run_with|run_with_impl|create_supervisor|check_nested_permissions|convert_nested_permissions|NestedPermissions' executor

echo
echo "candidate module outlines:"
for f in executor/src/lib.rs executor/src/exe/run.rs executor/src/rt/supervisor/mod.rs executor/src/wasi/base.rs; do
  echo "----- $f"
  wc -l "$f"
  ast-grep outline "$f" --view condensed | sed -n '1,180p' || true
done

Repository: genlayerlabs/genvm-executor

Length of output: 243


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "executor/src/lib.rs relevant section:"
sed -n '163,220p' executor/src/lib.rs | awk '{printf "%4d  %s\n", NR+162, $0}'

echo
echo "executor/src/exe/run.rs relevant section:"
sed -n '1,90p' executor/src/exe/run.rs | awk '{printf "%4d  %s\n", NR, $0}'

echo
echo "run_with/create_supervisor/check_nested_permissions/relevant references in tracked Rust files:"
rg -n -C 4 'run_with|run_with_impl|create_supervisor|check_nested_permissions|convert_nested_permissions|NestedPermissions|StorageRead' executor

echo
echo "file lengths:"
wc -l executor/src/lib.rs executor/src/exe/run.rs executor/crates/sdk-rs/src/abi/entry.rs executor/src/rt/supervisor/mod.rs executor/src/wasi/base.rs

Repository: genlayerlabs/genvm-executor

Length of output: 16346


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "executor/src/lib.rs create_supervisor/locked-slot/run_with sections:"
sed -n '60,145p' executor/src/lib.rs | awk '{printf "%4d  %s\n", NR+59, $0}'
printf '\n---\n'
sed -n '282,470p' executor/src/lib.rs | awk '{printf "%4d  %s\n", NR+281, $0}'
printf '\n---\n'
sed -n '720,960p' executor/src/wasi/genlayer_sdk.rs | awk '{printf "%4d  %s\n", NR+719, $0}'
printf '\n---\n'
sed -n '285,340p' executor/src/exe/run.rs | awk '{printf "%4d  %s\n", NR+284, $0}'

Repository: genlayerlabs/genvm-executor

Length of output: 27412


Reject forbidden nested permission bits before converting VM config.

executor/src/lib.rs:193-206 maps WRITE_STORAGE and SEND_MESSAGES straight into wasi::base::Config, while executor/src/exe/run.rs:32-50 rejects them only in handle. A nested CallContract then passes the parent config to CallContract children, so the rejected permission can authorize message-sending/other capabilities in one direction. Add the same permission check in convert_nested_permissions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@executor/src/lib.rs` around lines 193 - 212, Update
convert_nested_permissions to reject forbidden NestedPermissions bits before
constructing wasi::base::Config, matching the validation performed by handle in
executor/src/exe/run.rs. Ensure WRITE_STORAGE and SEND_MESSAGES are rejected for
nested calls rather than mapped into the child configuration, while preserving
conversion of allowed permissions.

topmost_runner_id: NestedRunnerId("contract".to_owned()),
remaining_recursion: vm_data.remaining_recursion,
remaining_det_fuel,
memory_limit: supervisor.limiter.get(true).get_remaining_memory(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

The nested memory budget crosses the executor boundary with no shared or clamped ceiling. The producer sends its full remaining deterministic memory, and the consumer installs that value as an independent limiter, so a routed chain is no longer bounded by one budget the way the in-process Limiter::derived() path is.

  • executor/src/wasi/genlayer_sdk.rs#L986-L986: send a reduced share instead of supervisor.limiter.get(true).get_remaining_memory(), or document the manager-side aggregate ceiling that makes the full value safe.
  • executor/src/exe/run.rs#L316-L317: clamp n.memory_limit against a local maximum before passing it to create_supervisor, matching how remaining_recursion is clamped with .min(public_abi::top_limits::VM_RECURSION) in executor/src/lib.rs.
📍 Affects 2 files
  • executor/src/wasi/genlayer_sdk.rs#L986-L986 (this comment)
  • executor/src/exe/run.rs#L316-L317
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@executor/src/wasi/genlayer_sdk.rs` at line 986, The nested memory limit
currently crosses the executor boundary without a shared ceiling. In
executor/src/wasi/genlayer_sdk.rs:986, send a reduced memory share or document
the manager-side aggregate ceiling that makes the full remaining value safe; in
executor/src/exe/run.rs:316-317, clamp n.memory_limit to a local maximum before
passing it to create_supervisor, following the existing remaining_recursion
clamp pattern.

@kp2pml30
kp2pml30 force-pushed the pr/v0.2/fix/vm-fatal-errors branch from 27ef6a0 to 4eb17a2 Compare August 10, 2026 11:14

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
executor/src/rt/mod.rs (1)

145-163: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the remaining fuel-budget branches.

The new test covers an imported budget that caps host fuel and decreases after consumption. Add cases for no imported budget, host fuel below the imported budget, and consumption beyond the remaining budget. These cases protect the None, min, and saturating_sub behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@executor/src/rt/mod.rs` around lines 145 - 163, Extend the tests around
DetFuelBudget::new, remaining, and consume to cover no imported budget, host
fuel lower than the imported budget, and consumption exceeding the remaining
budget. Assert that None uses the host fuel, remaining applies the minimum of
host and imported fuel, and over-consumption saturates at zero while preserving
the existing imported-budget assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@executor/crates/common/src/expr/value.rs`:
- Around line 213-217: Add a terminal failed variant to ThunkState that stores
the original EvalError, and update the force logic around deferred() to cache
ordinary evaluation errors in that variant instead of leaving the thunk
InProgress. Ensure subsequent force calls return the cached error directly,
while preserving Forced behavior for successful evaluations and reserving
recursion reporting for an actually re-entered InProgress thunk.

In `@executor/crates/common/src/io.rs`:
- Around line 224-226: Update the safety contract for FdPairStream::from_raw_fds
to require source_fd and sink_fd are distinct valid open descriptors, and
document that successful construction transfers ownership of both descriptors to
the returned wrapper.

In `@executor/crates/sdk-rs/src/abi/consts.rs`:
- Line 3: Update the module-level lint attribute in consts.rs to remove
clippy::all and restore the narrower clippy::redundant_static_lifetimes
allowance, keeping dead_code suppression unchanged for the generated module.

---

Nitpick comments:
In `@executor/src/rt/mod.rs`:
- Around line 145-163: Extend the tests around DetFuelBudget::new, remaining,
and consume to cover no imported budget, host fuel lower than the imported
budget, and consumption exceeding the remaining budget. Assert that None uses
the host fuel, remaining applies the minimum of host and imported fuel, and
over-consumption saturates at zero while preserving the existing imported-budget
assertions.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6b2a5bee-28b7-4864-98ab-e92c9914a1a2

📥 Commits

Reviewing files that changed from the base of the PR and between 27ef6a0 and 4eb17a2.

📒 Files selected for processing (10)
  • executor/crates/common/src/expr/evaluator.rs
  • executor/crates/common/src/expr/value.rs
  • executor/crates/common/src/io.rs
  • executor/crates/sdk-rs/src/abi/consts.rs
  • executor/src/domain/fees.rs
  • executor/src/rt/mod.rs
  • executor/src/rt/supervisor/actions.rs
  • executor/src/rt/supervisor/mod.rs
  • executor/src/rt/vm/mod.rs
  • executor/src/wasi/genlayer_sdk.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • executor/src/rt/supervisor/mod.rs
  • executor/src/rt/vm/mod.rs
  • executor/src/wasi/genlayer_sdk.rs

Comment on lines +213 to 217
// On error it stays `InProgress`: a failed computation is not retried, and
// any later force surfaces the recursion/error path consistently.
if let Ok(v) = &result {
*state = ThunkState::Forced(v.clone());
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Cache failed evaluations separately from InProgress.

When deferred() returns an ordinary EvalError, these lines leave the thunk in ThunkState::InProgress. The next force() then reports infinite recursion while forcing a lazy value, even when no recursion occurred. The original error is lost, and the binding becomes permanently unusable.

Add a terminal failed state that preserves the original error. Do not use InProgress as the error cache.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@executor/crates/common/src/expr/value.rs` around lines 213 - 217, Add a
terminal failed variant to ThunkState that stores the original EvalError, and
update the force logic around deferred() to cache ordinary evaluation errors in
that variant instead of leaving the thunk InProgress. Ensure subsequent force
calls return the cached error directly, while preserving Forced behavior for
successful evaluations and reserving recursion reporting for an actually
re-entered InProgress thunk.

Comment on lines +224 to +226
///
/// # Safety
/// Both fds must be valid open file descriptors that no one else closes or owns

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
rg -n -C 8 'FdPairStream::from_raw_fds|from_raw_fds\s*\(' executor --glob '*.rs'

Repository: genlayerlabs/genvm-executor

Length of output: 1478


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
sed -n '1,280p' executor/crates/common/src/io.rs

Repository: genlayerlabs/genvm-executor

Length of output: 8745


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
src = Path("executor/crates/common/src/io.rs").read_text()
needle = "unsafe fn from_raw_fds("
idx = src.index(needle)
end = src.index("pub struct FdPairStream", idx)
chunk = src[:end]
checks = {
    "contains_own_raw_fds": "std::os::fd::OwnedFd::from_raw_fd(source_fd)" in chunk and "std::os::fd::OwnedFd::from_raw_fd(sink_fd)" in chunk,
    "no_source_sink_inequality": "source_fd != sink_fd" not in chunk and "source_fd == sink_fd" not in chunk and "must not be the same" not in chunk.lower() and "distinct" not in chunk and "double-close" not in chunk.lower(),
    "document_safety_unsafe": "# Safety" in chunk and "Both fds must be valid open file descriptors that no one else closes or owns" in chunk,
}
for k, v in checks.items():
    print(f"{k}: {v}")
PY

Repository: genlayerlabs/genvm-executor

Length of output: 270


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import re
src = Path("executor/crates/common/src/io.rs").read_text()

# Extract FdPairStream safe doc and from_raw_fds body/preceding safety section only.
body = re.search(r"(impl FdPairStream \{[\s\S]*?\n\})", src)
if not body:
    raise SystemExit("FdPairStream impl not found")
sect = body.group(1)

print("contains_from_raw_fds:", "unsafe fn from_raw_fds(" in sect)
print("builds_two_owned_fds:", "OwnedFd::from_raw_fd(source_fd)" in sect and "OwnedFd::from_raw_fd(sink_fd)" in sect)
print("checks_source_sink_inequality:", bool(re.match(r".* source_fd != sink_fd .*", sect, re.S)))
print("uses_same_raw_fd_in_both_constructors:", re.search(r"OwnedFd::from_raw_fd\(source_fd\).*OwnedFd::from_raw_fd\(sink_fd\)|OwnedFd::from_raw_fd\(sink_fd\).*OwnedFd::from_raw_fd\(source_fd\)", sect))

needle = "unsafe fn from_raw_fds("
idx = sect.index(needle)
prev = sect[:idx]
after = sect[idx:sect.find("impl tokio::io::AsyncRead", idx)]
# Safety sentence(s) are immediately before pub unsafe fn; find last # Safety block in that prefix.
last_safety_start = prev.rfind("# Safety")
safety = prev[last_safety_start:] if last_safety_start >= 0 else ""
print("current_safety_text:")
print(safety.strip())
PY

Repository: genlayerlabs/genvm-executor

Length of output: 424


Require distinct raw descriptors.

FdPairStream::from_raw_fds independently constructs an OwnedFd from each raw value. Passing the same raw descriptor for source_fd and sink_fd lets the wrapper drop two owned descriptors for one descriptor and can double-close it. Require source_fd != sink_fd in the safety contract and document that successful construction takes ownership of both descriptors.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@executor/crates/common/src/io.rs` around lines 224 - 226, Update the safety
contract for FdPairStream::from_raw_fds to require source_fd and sink_fd are
distinct valid open descriptors, and document that successful construction
transfers ownership of both descriptors to the returned wrapper.

// This file is auto-generated. Do not edit!

#![allow(dead_code, clippy::redundant_static_lifetimes)]
#![allow(dead_code, clippy::all)]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
rg -n -C 2 'clippy::all|clippy::redundant_static_lifetimes' executor/crates/sdk-rs --glob '*.rs'

Repository: genlayerlabs/genvm-executor

Length of output: 498


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf 'Tracked files under abi:\n'
git ls-files executor/crates/sdk-rs/src/abi | sed -n '1,80p'

printf '\nconsts.rs first 40 lines:\n'
cat -n executor/crates/sdk-rs/src/abi/consts.rs | sed -n '1,40p'

printf '\nGenerator references to abi/consts.rs or consts generation:\n'
rg -n 'consts\.rs|abi::consts|abi/consts|generator|generate' executor/crates/sdk-rs --glob '*.rs' --glob '*.rs.in' --glob '*.toml' | sed -n '1,160p'

Repository: genlayerlabs/genvm-executor

Length of output: 2135


Keep clippy::all off this generated module.

clippy::all suppresses the full default Clippy lint group for generated Rust code. Restore the narrower clippy:redundant_static_lifetimes allowance, or use a target-specific Clippy setting for this generated module.

Proposed fix
-#![allow(dead_code, clippy::all)]
+#![allow(dead_code, clippy::redundant_static_lifetimes)]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#![allow(dead_code, clippy::all)]
#![allow(dead_code, clippy::redundant_static_lifetimes)]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@executor/crates/sdk-rs/src/abi/consts.rs` at line 3, Update the module-level
lint attribute in consts.rs to remove clippy::all and restore the narrower
clippy::redundant_static_lifetimes allowance, keeping dead_code suppression
unchanged for the generated module.

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