Skip to content

Conversation

@Yaxuan-w
Copy link
Member

@Yaxuan-w Yaxuan-w commented Dec 14, 2025

In lind-wasm, execution needs to re-enter Wasmtime from outside the current instance or call stack. Two common cases are:

  1. Process-like operations (fork / exec / exit): RawPOSIX may need to create, clone, or tear down Wasm instances and then return control back into Wasmtime. The cage performing these operations is not necessarily the same cage or grate that issued the syscall.
  2. Grate calls: Grate calls require cross-module execution transfers, where control jumps from one cage into another cage or grate.

Both cases require an explicit way to recover the correct Wasmtime runtime state (store + instance) for an arbitrary (cage_id, thread_id) pair. Relying on an implicit “current instance” is not sufficient.

Implementation

This PR moves a low-level runtime-state lookup mechanism based on Wasmtime’s VMContext pointer from 3i to wasmtime:

  • lind-wasm guarantees that each thread executes in its own store / instance pair.
  • During module instantiation, the VMContext pointer is extracted and stored in a global table keyed by (cage_id, thread_id).
  • When control needs to transfer to another cage or grate, lind-3i retrieves the corresponding VMContext pointer and uses Wasmtime-internal mechanisms to recover the correct store and instance.

The table stores raw pointers rather than typed handles.

VMContext storage refactoring and execution semantics

This PR resolves #538. Wasmtime stores a single VMContext pointer per (cage_id, thread_id). When a grate call is executed, the VMContext pointer is retrieved and copied using Rust’s Copy semantics. The copied wrapper is used for the duration of the call and then immediately dropped.

Conceptually, this behaves like a single-element context pool with immediate acquire-and-release, but without introducing additional storage or bookkeeping.

Wasmtime instantiation extensions

To minimize divergence from upstream Wasmtime code:

  • new_started_impl remains unchanged.
  • new_started_impl_with_lind is used for cage creation and performs lind-specific memory initialization.
  • A new helper, new_started_impl_with_lind_thread, is introduced for thread creation. Threads do not require cage-level memory initialization, but they do require returning an InstanceId so the VMContext can be registered.

3i runtime-agnostic grate dispatch

This PR clarifies the execution boundary between 3i and runtimes:

  • Each cage is associated with a runtime ID.
  • Each runtime registers a trampoline function that knows how to execute grate calls in that runtime.
  • _call_grate_func resolves the runtime and delegates execution entirely to the runtime-provided trampoline.

In the current implementation, the Wasmtime runtime registers grate_callback_trampoline (in wasmtime/run.rs), which:

  • retrieves the VMContext for the target grate,
  • re-enters the correct Wasmtime runtime context,
  • and dispatches to the unified Wasm entry function.

3i itself remains runtime-agnostic and does not execute grate code directly.

Test

New tests for multiple register_handler calls.

@github-actions
Copy link
Contributor

End-to-End Test Report

Test Preview

Test Report

Deterministic Tests

Summary

MetricCount
Total Test Cases81
Number of Successes81
Number of Failures0
Number of Compilation Failure Native0
Number of Runtime Failure Native0
Number of Segmentation Fault Native0
Number of Timeout During Native0
Number of Lind Wasm Compile Failure0
Number of Lind Wasm Runtime Failure0
Number of Lind Wasm Segmentation Failure0
Number of Timeout During Lind Wasm run0
Number of Unknown Failure0
Number of C Compiler and Wasm Output mismatch0
Number of Fail Test: Native Succeeded (Should Fail)0
Number of Fail Test: Wasm Succeeded (Should Fail)0
Number of Fail Test: Both Native and Wasm Succeeded (Should Fail)0
Number of Fail Test: Native Compilation Failure (Should Succeed)0
Number of Fail Test: Wasm Compilation Failure (Should Succeed)0

Test Results by Category

Test CaseStatusError TypeOutput
File Tests
chmod.cSuccessNone
Success
close.cSuccessNone
Success
doubleclose.cSuccessNone
Success
dupwrite.cSuccessNone
Success
fchmod.cSuccessNone
Success
fcntl.cSuccessNone
Success
fdatasync.cSuccessNone
Success
filetest.cSuccessNone
Success
filetest1000.cSuccessNone
Success
fstat.cSuccessNone
Success
fsync.cSuccessNone
Success
ftruncate.cSuccessNone
Success
ioctl.cSuccessNone
Success
link.cSuccessNone
Success
lseek.cSuccessNone
Success
mkdir_rmdir.cSuccessNone
Success
open.cSuccessNone
Success
pread_pwrite.cSuccessNone
Success
printf.cSuccessNone
Success
readbytes.cSuccessNone
Success
readlink.cSuccessNone
Success
rename.cSuccessNone
Success
stat.cSuccessNone
Success
sync_file_range.cSuccessNone
Success
truncate.cSuccessNone
Success
unlink.cSuccessNone
Success
unlinkat.cSuccessNone
Success
write.cSuccessNone
Success
writeloop.cSuccessNone
Success
writepartial.cSuccessNone
Success
writev.cSuccessNone
Success
Memory Tests
brk.cSuccessNone
Success
malloc.cSuccessNone
Success
memcpy.cSuccessNone
Success
memory_error_test.cSuccessNone
Success
mmap.cSuccessNone
Success
mmap_aligned.cSuccessNone
Success
mmap_file.cSuccessNone
Success
mprotect.cSuccessNone
Success
mprotect_boundary.cSuccessNone
Success
mprotect_end_region.cSuccessNone
Success
mprotect_middle_region.cSuccessNone
Success
mprotect_multiple_times.cSuccessNone
Success
mprotect_same_value.cSuccessNone
Success
mprotect_spanning_regions.cSuccessNone
Success
sbrk.cSuccessNone
Success
shmtest.cSuccessNone
Success
Networking Tests
gethostname.cSuccessNone
Success
makepipe.cSuccessNone
Success
pipepong.cSuccessNone
Success
poll.cSuccessNone
Success
recvfrom-sendto.cSuccessNone
Success
simple_epoll.cSuccessNone
Success
socket.cSuccessNone
Success
socket_cloexec.cSuccessNone
Success
socketpair.cSuccessNone
Success
tcp_connect_single.cSuccessNone
Success
Process Tests
chain_thread.cSuccessNone
Success
exit.cSuccessNone
Success
forkexecv-arg.cSuccessNone
Success
forkexecv.cSuccessNone
Success
function-ptr.cSuccessNone
Success
getppid.cSuccessNone
Success
hello-arg.cSuccessNone
Success
hello.cSuccessNone
Success
longjmp.cSuccessNone
Success
mutex.cSuccessNone
Success
noforkfiles.cSuccessNone
Success
sem_forks.cSuccessNone
Success
setsid.cSuccessNone
Success
thread-test.cSuccessNone
Success
thread.cSuccessNone
Success
wait.cSuccessNone
Success
waitpid_wnohang.cSuccessNone
Success
Signal Tests
alarm.cSuccessNone
Success
setitimer.cSuccessNone
Success
sigalrm.cSuccessNone
Success
sigchld.cSuccessNone
Success
signal-fork.cSuccessNone
Success
signal-simple.cSuccessNone
Success
sigprocmask.cSuccessNone
Success
Non Deterministic Tests

Summary

MetricCount
Total Test Cases40
Number of Successes40
Number of Failures0
Number of Compilation Failure Native0
Number of Runtime Failure Native0
Number of Segmentation Fault Native0
Number of Timeout During Native0
Number of Lind Wasm Compile Failure0
Number of Lind Wasm Runtime Failure0
Number of Lind Wasm Segmentation Failure0
Number of Timeout During Lind Wasm run0
Number of Unknown Failure0
Number of C Compiler and Wasm Output mismatch0
Number of Fail Test: Native Succeeded (Should Fail)0
Number of Fail Test: Wasm Succeeded (Should Fail)0
Number of Fail Test: Both Native and Wasm Succeeded (Should Fail)0
Number of Fail Test: Native Compilation Failure (Should Succeed)0
Number of Fail Test: Wasm Compilation Failure (Should Succeed)0

Test Results by Category

Test CaseStatusError TypeOutput
File Tests
chdir_getcwd.cSuccessNone
Success
clock_gettime_highlevel.cSuccessNone
Success
clock_gettime_simple.cSuccessNone
Success
dup.cSuccessNone
Success
fchdir.cSuccessNone
Success
fstatfs.cSuccessNone
Success
getcwd.cSuccessNone
Success
read.cSuccessNone
Success
statfs.cSuccessNone
Success
Memory Tests
malloc_large.cSuccessNone
Success
mmap_complicated.cSuccessNone
Success
mmap_shared.cSuccessNone
Success
segfault.cSuccessNone
Success
shm.cSuccessNone
Success
vtable.cSuccessNone
Success
Networking Tests
dnstest.cSuccessNone
Success
getifaddrs.cSuccessNone
Success
pipe.cSuccessNone
Success
pipe2.cSuccessNone
Success
pipeinput.cSuccessNone
Success
pipeinput2.cSuccessNone
Success
pipeonestring.cSuccessNone
Success
pipewrite.cSuccessNone
Success
shutdown_fork.cSuccessNone
Success
simple-select.cSuccessNone
Success
Process Tests
fork2malloc.cSuccessNone
Success
fork_simple.cSuccessNone
Success
forkandopen.cSuccessNone
Success
forkfiles.cSuccessNone
Success
forkmalloc.cSuccessNone
Success
forknodup.cSuccessNone
Success
getpid.cSuccessNone
Success
Signal Tests
signal.cSuccessNone
Success
signal_SIGCHLD.cSuccessNone
Success
signal_fork.cSuccessNone
Success
signal_int_thread.cSuccessNone
Success
signal_longjmp.cSuccessNone
Success
signal_nodefer.cSuccessNone
Success
signal_procmask.cSuccessNone
Success
signal_sa_mask.cSuccessNone
Success
Fail Tests

Summary

MetricCount
Total Test Cases1
Number of Successes1
Number of Failures0
Number of Compilation Failure Native0
Number of Runtime Failure Native0
Number of Segmentation Fault Native0
Number of Timeout During Native0
Number of Lind Wasm Compile Failure0
Number of Lind Wasm Runtime Failure0
Number of Lind Wasm Segmentation Failure0
Number of Timeout During Lind Wasm run0
Number of Unknown Failure0
Number of C Compiler and Wasm Output mismatch0
Number of Fail Test: Native Succeeded (Should Fail)0
Number of Fail Test: Wasm Succeeded (Should Fail)0
Number of Fail Test: Both Native and Wasm Succeeded (Should Fail)0
Number of Fail Test: Native Compilation Failure (Should Succeed)0
Number of Fail Test: Wasm Compilation Failure (Should Succeed)0

Test Results by Category

Test CaseStatusError TypeOutput
Memory Tests
mmap-negative1.cSuccessNone
Success

// for functions that require a fixed number of parameters but do not utilize
// all of them.
use wasmtime_lind_3i::take_gratefn_wasm;
// use wasmtime_lind_3i::take_gratefn_wasm;
Copy link
Contributor

Choose a reason for hiding this comment

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

commented code

/// Used in the `state` field of `GrateFnEntry` of wasmtime 3i.
pub const STATE_DEAD: u8 = 2;
/// The value is expected to be globally unique among all runtimes registered with 3i
pub const RUNTIME_WASMTIME: u64 = 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

probably worth using a number other than 1 here for debug purposes. RUNTIME_TYPE_WASMTIME is a bit clearer but possibly could be an enum?

/// Used by [`lind-common::register_handler`] to fetch the previously staged entry and pass its pointer into 3i.
/// Performs a read-locked lookup of `(cageid, 0)` in `GRATE_FN_WASM_TABLE`
/// `get_vmctx` looks up the `VMContext` associated with a given cage and thread.
/// It returns a copy of the stored `VmCtxWrapper`, or `None` if no entry exists.
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm still a bit unclear about the terminology here and am not sure whats going on, can we try rewriting this in as plain language as possible? This part is probably important that we all understand/get right.

Copy link
Contributor

Choose a reason for hiding this comment

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

Like from my understanding when we were talking about ctx pooling last week we wanted separate deep copies of the ctx's. This seems to say the opposite? Or is this a separate issue?

false, /* this is not the main thread */
);

// The main challenge in enabling dynamic syscall interposition between grates and 3i lies in Rust’s
Copy link
Contributor

Choose a reason for hiding this comment

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

i feel like we reiterate in the comments about the "lifetime challenge a lot" not sure we need to be so verbose about this

if ctx.is_null() {
return threei_const::GRATE_ERR;
}
let threadid = 1; //always 1. Only fork requires threadid passing, and fork is handled specially
Copy link
Contributor

Choose a reason for hiding this comment

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

this seems weird, or at least the comment seems misdirecting?

Copy link
Contributor

@rennergade rennergade left a comment

Choose a reason for hiding this comment

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

Left a few questions here.

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.

3i: panic caused by multiple grate context registrations in single grate

3 participants