wasmer_wasix/syscalls/wasix/
proc_fork_env.rs

1use crate::{WasiEnv, WasiError, WasiVFork};
2use wasmer::{FunctionEnvMut, Memory, MemorySize, WasmPtr};
3use wasmer_wasix_types::wasi::{Errno, Pid};
4
5/// ### `proc_fork_env()`
6/// Forks the environment of the current process into a new child process.
7/// The child process will start with the same memory and execution context
8/// as the parent process, similar to `fork()`.
9///
10/// Most syscalls are undefined behavior in the child process, except for
11/// `proc_exit2()` and `proc_exec3()`. `proc_exit2()` will terminate the
12/// child process and set the environment back to the parent process.
13/// `proc_exec3()` will exec the module in the child process and promote
14/// the child process to a real process. Then it will return with the
15/// environment back to the parent process.
16///
17/// This function differs from a traditional `vfork` in that it does not
18/// modify the control flow of the program. Instead, it only forks the
19/// WasiEnv, but leaves everything else (memory, call stack, store etc.)
20/// untouched.
21///
22/// This function is intended to be used in conjunction with
23/// setjmp/longjmp to build a lightweight implementation of vforking.
24///
25/// The value at child_pid_ptr will only be modified by a successful
26/// call to this function. It will contain the process id of the
27/// child process.
28#[tracing::instrument(level = "trace", skip_all, fields(pid = ctx.data().process.pid().raw()), ret)]
29pub fn proc_fork_env<M: MemorySize>(
30    mut ctx: FunctionEnvMut<'_, WasiEnv>,
31    child_pid_ptr: WasmPtr<Pid, M>,
32) -> Result<Errno, WasiError> {
33    WasiEnv::do_pending_operations(&mut ctx)?;
34
35    let env = ctx.data();
36
37    if let Some(vfork) = env.vfork.as_ref() {
38        tracing::warn!("Nested vforks are not supported");
39        return Ok(Errno::Notsup);
40    }
41
42    // Fork the environment which will copy all the open file handlers
43    // and associate a new context but otherwise shares things like the
44    // file system interface. The handle to the forked process is stored
45    // in the parent process context
46    let (mut child_env, child_handle) = match env.fork() {
47        Ok(p) => p,
48        Err(err) => {
49            tracing::error!("Could not fork process: {err}");
50            // TODO: evaluate the appropriate error code, document it in the spec.
51            return Ok(Errno::Perm);
52        }
53    };
54
55    // Write the child's PID to the provided pointer
56    let memory = unsafe { env.memory_view(&ctx) };
57    wasi_try_mem_ok!(child_pid_ptr.write(&memory, child_env.pid().raw()));
58
59    let parent_env = ctx.data_mut();
60
61    // Add the child to the parent's list of children
62    parent_env
63        .process
64        .lock()
65        .children
66        .push(child_env.process.clone());
67    // Swap the current environment with the child environment
68    child_env.swap_inner(parent_env);
69    std::mem::swap(parent_env, &mut child_env);
70
71    let previous_vfork = parent_env.vfork.replace(WasiVFork {
72        asyncify: None,
73        env: Box::new(child_env),
74        handle: child_handle,
75    });
76    assert!(previous_vfork.is_none()); // Already checked at the start of the function
77
78    Ok(Errno::Success)
79}