wasmer_wasix/syscalls/
mod.rs

1#![allow(
2    unused,
3    clippy::too_many_arguments,
4    clippy::cognitive_complexity,
5    clippy::result_large_err
6)]
7
8pub mod types {
9    pub use wasmer_wasix_types::{types::*, wasi};
10}
11
12#[cfg(any(
13    target_os = "freebsd",
14    target_os = "linux",
15    target_os = "android",
16    target_vendor = "apple"
17))]
18pub mod unix;
19#[cfg(target_family = "wasm")]
20pub mod wasm;
21#[cfg(target_os = "windows")]
22pub mod windows;
23
24pub mod journal;
25pub mod wasi;
26pub mod wasix;
27
28use bincode::config;
29use bytes::{Buf, BufMut};
30use futures::{
31    Future,
32    future::{BoxFuture, LocalBoxFuture},
33};
34use tracing::instrument;
35use virtual_mio::block_on;
36pub use wasi::*;
37pub use wasix::*;
38use wasmer_journal::SnapshotTrigger;
39use wasmer_wasix_types::wasix::ThreadStartType;
40
41pub mod legacy;
42
43pub(crate) use std::{
44    borrow::{Borrow, Cow},
45    cell::RefCell,
46    collections::{HashMap, HashSet, hash_map::Entry},
47    convert::{Infallible, TryInto},
48    io::{self, Read, Seek, Write},
49    mem::transmute,
50    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
51    num::NonZeroU64,
52    ops::{Deref, DerefMut},
53    path::Path,
54    pin::Pin,
55    sync::{
56        Arc, Condvar, Mutex,
57        atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering},
58        mpsc,
59    },
60    task::{Context, Poll},
61    thread::LocalKey,
62    time::Duration,
63};
64use std::{io::IoSlice, marker::PhantomData, mem::MaybeUninit, task::Waker, time::Instant};
65
66pub(crate) use bytes::{Bytes, BytesMut};
67pub(crate) use cooked_waker::IntoWaker;
68pub use journal::*;
69pub(crate) use sha2::Sha256;
70pub(crate) use tracing::{debug, error, trace, warn};
71#[cfg(any(
72    target_os = "freebsd",
73    target_os = "linux",
74    target_os = "android",
75    target_vendor = "apple"
76))]
77pub use unix::*;
78#[cfg(target_family = "wasm")]
79pub use wasm::*;
80
81pub(crate) use virtual_fs::{
82    AsyncSeekExt, AsyncWriteExt, DuplexPipe, FileSystem, FsError, VirtualFile,
83};
84pub(crate) use virtual_net::StreamSecurity;
85pub(crate) use wasmer::{
86    AsStoreMut, AsStoreRef, Extern, Function, FunctionEnv, FunctionEnvMut, Global, Instance,
87    Memory, Memory32, Memory64, MemoryAccessError, MemoryError, MemorySize, MemoryView, Module,
88    OnCalledAction, Pages, RuntimeError, Store, TypedFunction, Value, WasmPtr, WasmSlice,
89};
90pub(crate) use wasmer_wasix_types::{asyncify::__wasi_asyncify_t, wasi::EventUnion};
91#[cfg(target_os = "windows")]
92pub use windows::*;
93
94pub(crate) use self::types::{
95    wasi::{
96        Addressfamily, Advice, Clockid, Dircookie, Dirent, DlFlags, DlHandle, Errno, Event,
97        EventFdReadwrite, Eventrwflags, Eventtype, ExitCode, Fd as WasiFd, Fdflags, Fdflagsext,
98        Fdstat, Filesize, Filestat, Filetype, Fstflags, Linkcount, Longsize, OptionFd, Pid,
99        Prestat, ProcSpawnFdOp, Rights, SignalDisposition, Snapshot0Clockid, Sockoption,
100        Sockstatus, Socktype, StackSnapshot, StdioMode as WasiStdioMode, Streamsecurity,
101        Subscription, SubscriptionFsReadwrite, Tid, Timestamp, TlKey, TlUser, TlVal, Tty, Whence,
102    },
103    *,
104};
105use self::{
106    state::{WasiInstanceGuardMemory, conv_env_vars},
107    utils::WasiDummyWaker,
108};
109pub(crate) use crate::os::task::{
110    process::{WasiProcessId, WasiProcessWait},
111    thread::{WasiThread, WasiThreadId},
112};
113use crate::{
114    DeepSleepWork, RewindPostProcess, RewindState, RewindStateOption, SpawnError, WasiInodes,
115    WasiResult, WasiRuntimeError,
116    fs::{
117        Fd, FdInner, InodeVal, Kind, MAX_SYMLINKS, fs_error_into_wasi_err,
118        virtual_file_type_to_wasi_file_type,
119    },
120    journal::{DynJournal, DynReadableJournal, DynWritableJournal, JournalEffector},
121    os::task::{
122        process::{MaybeCheckpointResult, WasiProcessCheckpoint},
123        thread::{RewindResult, RewindResultType},
124    },
125    utils::store::StoreSnapshot,
126};
127pub(crate) use crate::{
128    Runtime, VirtualTaskManager, WasiEnv, WasiError, WasiFunctionEnv, WasiModuleTreeHandles,
129    WasiVFork,
130    bin_factory::spawn_exec_module,
131    import_object_for_all_wasi_versions, mem_error_to_wasi,
132    net::{
133        read_ip_port,
134        socket::{InodeHttpSocketType, InodeSocket, InodeSocketKind},
135        write_ip_port,
136    },
137    runtime::SpawnType,
138    state::{
139        self, InodeGuard, InodeWeakGuard, PollEvent, PollEventBuilder, WasiFutex, WasiState,
140        iterate_poll_events,
141    },
142    utils::{self, map_io_err},
143};
144pub(crate) use crate::{net::net_error_into_wasi_err, utils::WasiParkingLot};
145
146pub(crate) fn to_offset<M: MemorySize>(offset: usize) -> Result<M::Offset, Errno> {
147    let ret: M::Offset = offset.try_into().map_err(|_| Errno::Inval)?;
148    Ok(ret)
149}
150
151pub(crate) fn from_offset<M: MemorySize>(offset: M::Offset) -> Result<usize, Errno> {
152    let ret: usize = offset.try_into().map_err(|_| Errno::Inval)?;
153    Ok(ret)
154}
155
156pub(crate) fn write_bytes_inner<T: Write, M: MemorySize>(
157    mut write_loc: T,
158    memory: &MemoryView,
159    iovs_arr_cell: WasmSlice<__wasi_ciovec_t<M>>,
160) -> Result<usize, Errno> {
161    let mut bytes_written = 0usize;
162    for iov in iovs_arr_cell.iter() {
163        let iov_inner = iov.read().map_err(mem_error_to_wasi)?;
164        let bytes = WasmPtr::<u8, M>::new(iov_inner.buf)
165            .slice(memory, iov_inner.buf_len)
166            .map_err(mem_error_to_wasi)?;
167        let bytes = bytes.read_to_vec().map_err(mem_error_to_wasi)?;
168        write_loc.write_all(&bytes).map_err(map_io_err)?;
169
170        bytes_written += from_offset::<M>(iov_inner.buf_len)?;
171    }
172    Ok(bytes_written)
173}
174
175pub(crate) fn write_bytes<T: Write, M: MemorySize>(
176    mut write_loc: T,
177    memory: &MemoryView,
178    iovs_arr: WasmSlice<__wasi_ciovec_t<M>>,
179) -> Result<usize, Errno> {
180    let result = write_bytes_inner::<_, M>(&mut write_loc, memory, iovs_arr);
181    write_loc.flush();
182    result
183}
184
185pub(crate) fn copy_from_slice<M: MemorySize>(
186    mut read_loc: &[u8],
187    memory: &MemoryView,
188    iovs_arr: WasmSlice<__wasi_iovec_t<M>>,
189) -> Result<usize, Errno> {
190    let mut bytes_read = 0usize;
191
192    let iovs_arr = iovs_arr.access().map_err(mem_error_to_wasi)?;
193    for iovs in iovs_arr.iter() {
194        let mut buf = WasmPtr::<u8, M>::new(iovs.buf)
195            .slice(memory, iovs.buf_len)
196            .map_err(mem_error_to_wasi)?
197            .access()
198            .map_err(mem_error_to_wasi)?;
199
200        let to_read = from_offset::<M>(iovs.buf_len)?;
201        let to_read = to_read.min(read_loc.len());
202        if to_read == 0 {
203            break;
204        }
205        let (left, right) = read_loc.split_at(to_read);
206        let amt = buf.copy_from_slice_min(left);
207        if amt != to_read {
208            return Ok(bytes_read + amt);
209        }
210
211        read_loc = right;
212        bytes_read += to_read;
213    }
214    Ok(bytes_read)
215}
216
217pub(crate) fn read_bytes<T: Read, M: MemorySize>(
218    mut reader: T,
219    memory: &MemoryView,
220    iovs_arr: WasmSlice<__wasi_iovec_t<M>>,
221) -> Result<usize, Errno> {
222    let mut bytes_read = 0usize;
223
224    let iovs_arr = iovs_arr.access().map_err(mem_error_to_wasi)?;
225    for iovs in iovs_arr.iter() {
226        let mut buf = WasmPtr::<u8, M>::new(iovs.buf)
227            .slice(memory, iovs.buf_len)
228            .map_err(mem_error_to_wasi)?
229            .access()
230            .map_err(mem_error_to_wasi)?;
231
232        let to_read = buf.len();
233        let has_read = reader.read(buf.as_mut()).map_err(map_io_err)?;
234
235        bytes_read += has_read;
236        if has_read != to_read {
237            return Ok(bytes_read);
238        }
239    }
240    Ok(bytes_read)
241}
242
243// TODO: remove allow once inodes are refactored (see comments on [`WasiState`])
244/// Writes data to the stderr
245#[allow(clippy::await_holding_lock)]
246pub unsafe fn stderr_write<'a>(
247    ctx: &FunctionEnvMut<'_, WasiEnv>,
248    buf: &[u8],
249) -> LocalBoxFuture<'a, Result<(), Errno>> {
250    let env = ctx.data();
251    let (memory, state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(ctx, 0) };
252
253    let buf = buf.to_vec();
254    let mut stderr = WasiInodes::stderr_mut(&state.fs.fd_map).map_err(fs_error_into_wasi_err);
255    Box::pin(async move { stderr?.write_all(&buf).await.map_err(map_io_err) })
256}
257
258fn block_on_with_timeout<T, Fut>(
259    tasks: &Arc<dyn VirtualTaskManager>,
260    timeout: Option<Duration>,
261    work: Fut,
262) -> WasiResult<T>
263where
264    Fut: Future<Output = WasiResult<T>>,
265{
266    let mut nonblocking = false;
267    if timeout == Some(Duration::ZERO) {
268        nonblocking = true;
269    }
270    let timeout = async {
271        if let Some(timeout) = timeout {
272            if !nonblocking {
273                tasks.sleep_now(timeout).await
274            } else {
275                InfiniteSleep::default().await
276            }
277        } else {
278            InfiniteSleep::default().await
279        }
280    };
281
282    let work = async move {
283        tokio::select! {
284            // The main work we are doing
285            res = work => res,
286            // Optional timeout
287            _ = timeout => Ok(Err(Errno::Timedout)),
288        }
289    };
290
291    // Fast path
292    if nonblocking {
293        let waker = WasiDummyWaker.into_waker();
294        let mut cx = Context::from_waker(&waker);
295        let mut pinned_work = Box::pin(work);
296        if let Poll::Ready(res) = pinned_work.as_mut().poll(&mut cx) {
297            return res;
298        }
299        return Ok(Err(Errno::Again));
300    }
301
302    // Slow path, block on the work and process process
303    block_on(work)
304}
305
306/// Asyncify takes the current thread and blocks on the async runtime associated with it
307/// thus allowed for asynchronous operations to execute. It has built in functionality
308/// to (optionally) timeout the IO, force exit the process, callback signals and pump
309/// synchronous IO engine
310pub(crate) fn __asyncify<T, Fut>(
311    ctx: &mut FunctionEnvMut<'_, WasiEnv>,
312    timeout: Option<Duration>,
313    work: Fut,
314) -> WasiResult<T>
315where
316    T: 'static,
317    Fut: std::future::Future<Output = Result<T, Errno>>,
318{
319    let mut env = ctx.data();
320
321    // Check if we need to exit the asynchronous loop
322    if let Some(exit_code) = env.should_exit() {
323        return Err(WasiError::Exit(exit_code));
324    }
325
326    // This poller will process any signals when the main working function is idle
327    struct SignalPoller<'a, 'b, Fut, T>
328    where
329        Fut: Future<Output = Result<T, Errno>>,
330    {
331        ctx: &'a mut FunctionEnvMut<'b, WasiEnv>,
332        pinned_work: Pin<Box<Fut>>,
333    }
334    impl<Fut, T> Future for SignalPoller<'_, '_, Fut, T>
335    where
336        Fut: Future<Output = Result<T, Errno>>,
337    {
338        type Output = Result<Fut::Output, WasiError>;
339        fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
340            if let Poll::Ready(res) = Pin::new(&mut self.pinned_work).poll(cx) {
341                return Poll::Ready(Ok(res));
342            }
343            if let Some(signals) = self.ctx.data().thread.pop_signals_or_subscribe(cx.waker()) {
344                if let Err(err) = WasiEnv::process_signals_internal(self.ctx, signals) {
345                    return Poll::Ready(Err(err));
346                }
347                return Poll::Ready(Ok(Err(Errno::Intr)));
348            }
349            Poll::Pending
350        }
351    }
352
353    // Block on the work
354    let mut pinned_work = Box::pin(work);
355    let tasks = env.tasks().clone();
356    let poller = SignalPoller { ctx, pinned_work };
357    block_on_with_timeout(&tasks, timeout, poller)
358}
359
360/// Future that will be polled by asyncify methods
361/// (the return value is what will be returned in rewind
362///  or in the instant response)
363pub type AsyncifyFuture = dyn Future<Output = Bytes> + Send + Sync + 'static;
364
365// This poller will process any signals when the main working function is idle
366struct AsyncifyPoller<'a, 'b, 'c, T, Fut>
367where
368    Fut: Future<Output = T> + Send + Sync + 'static,
369{
370    ctx: &'b mut FunctionEnvMut<'c, WasiEnv>,
371    work: &'a mut Pin<Box<Fut>>,
372}
373impl<T, Fut> Future for AsyncifyPoller<'_, '_, '_, T, Fut>
374where
375    Fut: Future<Output = T> + Send + Sync + 'static,
376{
377    type Output = Result<T, WasiError>;
378
379    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
380        if let Poll::Ready(res) = self.work.as_mut().poll(cx) {
381            return Poll::Ready(Ok(res));
382        }
383
384        if let Err(err) = WasiEnv::do_pending_link_operations(self.ctx, false) {
385            return Poll::Ready(Err(err));
386        }
387
388        let env = self.ctx.data();
389        if let Some(forced_exit) = env.thread.try_join() {
390            return Poll::Ready(Err(WasiError::Exit(forced_exit.unwrap_or_else(|err| {
391                tracing::debug!("exit runtime error - {}", err);
392                Errno::Child.into()
393            }))));
394        }
395        if env.thread.has_signals_or_subscribe(cx.waker()) {
396            let has_exit = {
397                let signals = env.thread.signals().lock().unwrap();
398                signals
399                    .0
400                    .iter()
401                    .filter_map(|sig| {
402                        if *sig == Signal::Sigint
403                            || *sig == Signal::Sigquit
404                            || *sig == Signal::Sigkill
405                            || *sig == Signal::Sigabrt
406                        {
407                            Some(env.thread.set_or_get_exit_code_for_signal(*sig))
408                        } else {
409                            None
410                        }
411                    })
412                    .next()
413            };
414
415            return match WasiEnv::process_signals_and_exit(self.ctx) {
416                Ok(Ok(_)) => {
417                    if let Some(exit_code) = has_exit {
418                        Poll::Ready(Err(WasiError::Exit(exit_code)))
419                    } else {
420                        // Re-subscribe so we get woken up for further signals as well
421                        self.ctx.data().thread.signals_subscribe(cx.waker());
422                        // Retry after Sigwakeup drain: dl ops may have started after the check above.
423                        if let Err(err) = WasiEnv::do_pending_link_operations(self.ctx, false) {
424                            return Poll::Ready(Err(err));
425                        }
426                        Poll::Pending
427                    }
428                }
429                Ok(Err(err)) => Poll::Ready(Err(WasiError::Exit(ExitCode::from(err)))),
430                Err(err) => Poll::Ready(Err(err)),
431            };
432        }
433        Poll::Pending
434    }
435}
436
437pub enum AsyncifyAction<'a, R> {
438    /// Indicates that asyncify callback finished and the
439    /// caller now has ownership of the ctx again
440    Finish(FunctionEnvMut<'a, WasiEnv>, R),
441    /// Indicates that asyncify should unwind by immediately exiting
442    /// the current function
443    Unwind,
444}
445
446/// Exponentially increasing backoff of CPU usage
447///
448/// Under certain conditions the process will exponentially backoff
449/// using waits that either put the thread into a low usage state
450/// or even underload the thread completely when deep sleep is enabled
451///
452/// The use-case for this is to handle rogue WASM processes that
453/// generate excessively high CPU usage and need to be artificially
454/// throttled
455///
456pub(crate) fn maybe_backoff<M: MemorySize>(
457    mut ctx: FunctionEnvMut<'_, WasiEnv>,
458) -> Result<Result<FunctionEnvMut<'_, WasiEnv>, Errno>, WasiError> {
459    let env = ctx.data();
460
461    // Fast path that exits this high volume call if we do not have
462    // exponential backoff enabled
463    if env.enable_exponential_cpu_backoff.is_none() {
464        return Ok(Ok(ctx));
465    }
466
467    // Determine if we need to do a backoff, if so lets do one
468    if let Some(backoff) = env.process.acquire_cpu_backoff_token(env.tasks()) {
469        tracing::trace!("exponential CPU backoff {:?}", backoff.backoff_time());
470        if let AsyncifyAction::Finish(mut ctx, _) =
471            __asyncify_with_deep_sleep::<M, _, _>(ctx, backoff)?
472        {
473            Ok(Ok(ctx))
474        } else {
475            Ok(Err(Errno::Success))
476        }
477    } else {
478        Ok(Ok(ctx))
479    }
480}
481
482/// Asyncify takes the current thread and blocks on the async runtime associated with it
483/// thus allowed for asynchronous operations to execute. It has built in functionality
484/// to (optionally) timeout the IO, force exit the process, callback signals and pump
485/// synchronous IO engine
486///
487/// This will either return the `ctx` as the asyncify has completed successfully
488/// or it will return an WasiError which will exit the WASM call using asyncify
489/// and instead process it on a shared task
490///
491pub(crate) fn __asyncify_with_deep_sleep<M: MemorySize, T, Fut>(
492    mut ctx: FunctionEnvMut<'_, WasiEnv>,
493    work: Fut,
494) -> Result<AsyncifyAction<'_, T>, WasiError>
495where
496    T: serde::Serialize + serde::de::DeserializeOwned,
497    Fut: Future<Output = T> + Send + Sync + 'static,
498{
499    // Determine the deep sleep time
500    let deep_sleep_time = match ctx.data().enable_journal {
501        true => Duration::from_micros(100),
502        false => Duration::from_millis(50),
503    };
504
505    // Box up the trigger
506    let mut trigger = Box::pin(work);
507
508    // Define the work
509    let tasks = ctx.data().tasks().clone();
510    let work = async move {
511        let env = ctx.data();
512
513        // Create the deep sleeper
514        // Deep sleep breaks the linker completely, as it expects other modules to catch the
515        // signal for DL ops
516        let tasks_for_deep_sleep = if env.enable_deep_sleep && env.inner().linker().is_none() {
517            Some(env.tasks().clone())
518        } else {
519            None
520        };
521
522        let deep_sleep_wait = async {
523            if let Some(tasks) = tasks_for_deep_sleep {
524                tasks.sleep_now(deep_sleep_time).await
525            } else {
526                InfiniteSleep::default().await
527            }
528        };
529
530        Ok(tokio::select! {
531            // Inner wait with finializer
532            res = AsyncifyPoller {
533                ctx: &mut ctx,
534                work: &mut trigger,
535            } => {
536                let result = res?;
537                AsyncifyAction::Finish(ctx, result)
538            },
539            // Determines when and if we should go into a deep sleep
540            _ = deep_sleep_wait => {
541                let pid = ctx.data().pid();
542                let tid = ctx.data().tid();
543
544                // We put thread into a deep sleeping state and
545                // notify anyone who is waiting for that
546                let thread = ctx.data().thread.clone();
547                thread.set_deep_sleeping(true);
548                ctx.data().process.inner.1.notify_one();
549
550                tracing::trace!(%pid, %tid, "thread entering deep sleep");
551                deep_sleep::<M>(ctx, Box::pin(async move {
552                    // After this wakes the background work or waking
553                    // event has triggered and its time to result
554                    let result = trigger.await;
555                    tracing::trace!(%pid, %tid, "thread leaving deep sleep");
556                    thread.set_deep_sleeping(false);
557                    bincode::serde::encode_to_vec(&result, config::legacy()).unwrap().into()
558                }))?;
559                AsyncifyAction::Unwind
560            },
561        })
562    };
563
564    // Block until the work is finished or until we
565    // unload the thread using asyncify
566    block_on(work)
567}
568
569/// Asyncify takes the current thread and blocks on the async runtime associated with it
570/// thus allowed for asynchronous operations to execute. It has built in functionality
571/// to (optionally) timeout the IO, force exit the process, callback signals and pump
572/// synchronous IO engine
573pub(crate) fn __asyncify_light<T, Fut>(
574    env: &WasiEnv,
575    _timeout: Option<Duration>,
576    work: Fut,
577) -> WasiResult<T>
578where
579    T: 'static,
580    Fut: Future<Output = Result<T, Errno>>,
581{
582    let snapshot_wait = wait_for_snapshot(env);
583
584    // Block until the work is finished or until we
585    // unload the thread using asyncify
586    Ok(block_on(work))
587}
588
589// This should be compiled away, it will simply wait forever however its never
590// used by itself, normally this is passed into asyncify which will still abort
591// the operating on timeouts, signals or other work due to a select! around the await
592#[derive(Default)]
593pub struct InfiniteSleep {}
594impl std::future::Future for InfiniteSleep {
595    type Output = ();
596    fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
597        Poll::Pending
598    }
599}
600
601/// Performs an immutable operation on the socket while running in an asynchronous runtime
602/// This has built in signal support
603pub(crate) fn __sock_asyncify<T, F, Fut>(
604    env: &WasiEnv,
605    sock: WasiFd,
606    rights: Rights,
607    actor: F,
608) -> Result<T, Errno>
609where
610    F: FnOnce(crate::net::socket::InodeSocket, Fd) -> Fut,
611    Fut: std::future::Future<Output = Result<T, Errno>>,
612{
613    let fd_entry = __sock_check_rights(env, sock, rights)?;
614
615    let mut work = {
616        let inode = fd_entry.inode.clone();
617        let tasks = env.tasks().clone();
618        let mut guard = inode.write();
619        match guard.deref_mut() {
620            Kind::Socket { socket } => {
621                // Clone the socket and release the lock
622                let socket = socket.clone();
623                drop(guard);
624
625                // Start the work using the socket
626                actor(socket, fd_entry)
627            }
628            _ => {
629                return Err(Errno::Notsock);
630            }
631        }
632    };
633
634    // Block until the work is finished or until we
635    // unload the thread using asyncify
636    block_on(work)
637}
638
639pub(crate) fn __sock_check_rights(
640    env: &WasiEnv,
641    sock: WasiFd,
642    rights: Rights,
643) -> Result<Fd, Errno> {
644    let fd_entry = env.state.fs.get_fd(sock)?;
645    if !rights.is_empty() && !fd_entry.inner.rights.contains(rights) {
646        return Err(Errno::Access);
647    }
648    Ok(fd_entry)
649}
650
651/// Performs mutable work on a socket under an asynchronous runtime with
652/// built in signal processing
653pub(crate) fn __sock_asyncify_mut<T, F, Fut>(
654    ctx: &'_ mut FunctionEnvMut<'_, WasiEnv>,
655    sock: WasiFd,
656    rights: Rights,
657    actor: F,
658) -> Result<T, Errno>
659where
660    F: FnOnce(crate::net::socket::InodeSocket, Fd) -> Fut,
661    Fut: std::future::Future<Output = Result<T, Errno>>,
662{
663    let env = ctx.data();
664    let tasks = env.tasks().clone();
665
666    let fd_entry = env.state.fs.get_fd(sock)?;
667    if !rights.is_empty() && !fd_entry.inner.rights.contains(rights) {
668        return Err(Errno::Access);
669    }
670
671    let inode = fd_entry.inode.clone();
672    let mut guard = inode.write();
673    match guard.deref_mut() {
674        Kind::Socket { socket } => {
675            // Clone the socket and release the lock
676            let socket = socket.clone();
677            drop(guard);
678
679            // Start the work using the socket
680            let mut work = actor(socket, fd_entry);
681
682            // Otherwise we block on the work and process it
683            // using an asynchronou context
684            block_on(work)
685        }
686        _ => Err(Errno::Notsock),
687    }
688}
689
690/// Performs an immutable operation on the socket while running in an asynchronous runtime
691/// This has built in signal support
692pub(crate) fn __sock_actor<T, F>(
693    ctx: &mut FunctionEnvMut<'_, WasiEnv>,
694    sock: WasiFd,
695    rights: Rights,
696    actor: F,
697) -> Result<T, Errno>
698where
699    T: 'static,
700    F: FnOnce(crate::net::socket::InodeSocket, Fd) -> Result<T, Errno>,
701{
702    let env = ctx.data();
703    let tasks = env.tasks().clone();
704
705    let fd_entry = env.state.fs.get_fd(sock)?;
706    if !rights.is_empty() && !fd_entry.inner.rights.contains(rights) {
707        return Err(Errno::Access);
708    }
709
710    let inode = fd_entry.inode.clone();
711
712    let tasks = env.tasks().clone();
713    let mut guard = inode.write();
714    match guard.deref_mut() {
715        Kind::Socket { socket } => {
716            // Clone the socket and release the lock
717            let socket = socket.clone();
718            drop(guard);
719
720            // Start the work using the socket
721            actor(socket, fd_entry)
722        }
723        _ => Err(Errno::Notsock),
724    }
725}
726
727/// Performs mutable work on a socket under an asynchronous runtime with
728/// built in signal processing
729pub(crate) fn __sock_actor_mut<T, F>(
730    ctx: &mut FunctionEnvMut<'_, WasiEnv>,
731    sock: WasiFd,
732    rights: Rights,
733    actor: F,
734) -> Result<T, Errno>
735where
736    T: 'static,
737    F: FnOnce(crate::net::socket::InodeSocket, Fd) -> Result<T, Errno>,
738{
739    let env = ctx.data();
740    let tasks = env.tasks().clone();
741
742    let fd_entry = env.state.fs.get_fd(sock)?;
743    if !rights.is_empty() && !fd_entry.inner.rights.contains(rights) {
744        return Err(Errno::Access);
745    }
746
747    let inode = fd_entry.inode.clone();
748    let mut guard = inode.write();
749    match guard.deref_mut() {
750        Kind::Socket { socket } => {
751            // Clone the socket and release the lock
752            let socket = socket.clone();
753            drop(guard);
754
755            // Start the work using the socket
756            actor(socket, fd_entry)
757        }
758        _ => Err(Errno::Notsock),
759    }
760}
761
762/// Replaces a socket with another socket in under an asynchronous runtime.
763/// This is used for opening sockets or connecting sockets which changes
764/// the fundamental state of the socket to another state machine
765pub(crate) fn __sock_upgrade<'a, F, Fut>(
766    ctx: &'a mut FunctionEnvMut<'_, WasiEnv>,
767    sock: WasiFd,
768    rights: Rights,
769    actor: F,
770) -> Result<(), Errno>
771where
772    F: FnOnce(crate::net::socket::InodeSocket, Fdflags) -> Fut,
773    Fut: std::future::Future<Output = Result<Option<crate::net::socket::InodeSocket>, Errno>> + 'a,
774{
775    let env = ctx.data();
776    let fd_entry = env.state.fs.get_fd(sock)?;
777    if !rights.is_empty() && !fd_entry.inner.rights.contains(rights) {
778        tracing::warn!(
779            "wasi[{}:{}]::sock_upgrade(fd={}, rights={:?}) - failed - no access rights to upgrade",
780            ctx.data().pid(),
781            ctx.data().tid(),
782            sock,
783            rights
784        );
785        return Err(Errno::Access);
786    }
787
788    let tasks = env.tasks().clone();
789    {
790        let inode = fd_entry.inode;
791        let mut guard = inode.write();
792        match guard.deref_mut() {
793            Kind::Socket { socket } => {
794                let socket = socket.clone();
795                drop(guard);
796
797                // Start the work using the socket
798                let work = actor(socket, fd_entry.inner.flags);
799
800                // Block on the work and process it
801                let res = block_on(work);
802                let new_socket = res?;
803
804                if let Some(mut new_socket) = new_socket {
805                    let mut guard = inode.write();
806                    match guard.deref_mut() {
807                        Kind::Socket { socket, .. } => {
808                            std::mem::swap(socket, &mut new_socket);
809                        }
810                        _ => {
811                            tracing::warn!(
812                                "wasi[{}:{}]::sock_upgrade(fd={}, rights={:?}) - failed - not a socket",
813                                ctx.data().pid(),
814                                ctx.data().tid(),
815                                sock,
816                                rights
817                            );
818                            return Err(Errno::Notsock);
819                        }
820                    }
821                }
822            }
823            _ => {
824                tracing::warn!(
825                    "wasi[{}:{}]::sock_upgrade(fd={}, rights={:?}) - failed - not a socket",
826                    ctx.data().pid(),
827                    ctx.data().tid(),
828                    sock,
829                    rights
830                );
831                return Err(Errno::Notsock);
832            }
833        }
834    }
835
836    Ok(())
837}
838
839#[must_use]
840pub(crate) fn write_buffer_array<M: MemorySize>(
841    memory: &MemoryView,
842    from: &[Vec<u8>],
843    ptr_buffer: WasmPtr<WasmPtr<u8, M>, M>,
844    buffer: WasmPtr<u8, M>,
845) -> Errno {
846    let ptrs = wasi_try_mem!(ptr_buffer.slice(memory, wasi_try!(to_offset::<M>(from.len()))));
847
848    let mut current_buffer_offset = 0usize;
849    for ((i, sub_buffer), ptr) in from.iter().enumerate().zip(ptrs.iter()) {
850        let mut buf_offset = buffer.offset();
851        buf_offset += wasi_try!(to_offset::<M>(current_buffer_offset));
852        let new_ptr = WasmPtr::new(buf_offset);
853        wasi_try_mem!(ptr.write(new_ptr));
854
855        let data =
856            wasi_try_mem!(new_ptr.slice(memory, wasi_try!(to_offset::<M>(sub_buffer.len()))));
857        wasi_try_mem!(data.write_slice(sub_buffer));
858        wasi_try_mem!(
859            wasi_try_mem!(new_ptr.add_offset(wasi_try!(to_offset::<M>(sub_buffer.len()))))
860                .write(memory, 0)
861        );
862
863        current_buffer_offset += sub_buffer.len() + 1;
864    }
865
866    Errno::Success
867}
868
869pub(crate) fn read_string_array<M: MemorySize>(
870    memory: &MemoryView,
871    ptrs: WasmPtr<WasmPtr<u8, M>, M>,
872    count: M::Offset,
873) -> Result<Vec<String>, Errno> {
874    if ptrs.is_null() || count == M::ZERO {
875        return Ok(vec![]);
876    }
877
878    let ptr_slice = ptrs.slice(memory, count).map_err(mem_error_to_wasi)?;
879    let capacity = from_offset::<M>(count)?;
880    let mut result = Vec::with_capacity(capacity);
881    for ptr in ptr_slice.access().map_err(mem_error_to_wasi)?.iter() {
882        let s = ptr
883            .read_utf8_string_with_nul(memory)
884            .map_err(mem_error_to_wasi)?;
885        result.push(s);
886    }
887    Ok(result)
888}
889
890pub(crate) fn parse_env_entries(envs: Vec<String>) -> Result<Vec<(String, String)>, Errno> {
891    envs.into_iter()
892        .map(|env| {
893            let (key, value) = env.split_once('=').ok_or(Errno::Inval)?;
894            Ok((key.to_string(), value.to_string()))
895        })
896        .collect()
897}
898
899pub(crate) fn parse_delimited_string_list(s: &str) -> Vec<String> {
900    s.split(&['\n', '\r'])
901        .map(|a| a.to_string())
902        .filter(|a| !a.is_empty())
903        .collect()
904}
905
906pub(crate) fn parse_delimited_exec_args(s: &str) -> Vec<String> {
907    s.trim_end_matches(['\r', '\n'])
908        .lines()
909        .map(str::to_owned)
910        .collect()
911}
912
913pub(crate) fn parse_delimited_env_list(s: &str) -> Result<Vec<(String, String)>, Errno> {
914    let envs = s
915        .split(&['\n', '\r'])
916        .map(|a| a.to_string())
917        .filter(|a| !a.is_empty())
918        .collect::<Vec<_>>();
919    parse_env_entries(envs)
920}
921
922pub(crate) fn get_current_time_in_nanos() -> Result<Timestamp, Errno> {
923    let now = platform_clock_time_get(Snapshot0Clockid::Monotonic, 1_000_000).unwrap() as u128;
924    Ok(now as Timestamp)
925}
926
927pub(crate) fn get_stack_lower(env: &WasiEnv) -> u64 {
928    env.layout.stack_lower
929}
930
931pub(crate) fn get_stack_upper(env: &WasiEnv) -> u64 {
932    env.layout.stack_upper
933}
934
935pub(crate) unsafe fn get_memory_stack_pointer(
936    ctx: &mut FunctionEnvMut<'_, WasiEnv>,
937) -> Result<u64, String> {
938    // Get the current value of the stack pointer (which we will use
939    // to save all of the stack)
940    let stack_upper = get_stack_upper(ctx.data());
941    let stack_pointer = if let Some(stack_pointer) = ctx
942        .data()
943        .inner()
944        .main_module_instance_handles()
945        .stack_pointer
946        .clone()
947    {
948        match stack_pointer.get(ctx) {
949            Value::I32(a) => a as u64,
950            Value::I64(a) => a as u64,
951            _ => stack_upper,
952        }
953    } else {
954        return Err("failed to save stack: no __stack_pointer global".to_string());
955    };
956    Ok(stack_pointer)
957}
958
959pub(crate) unsafe fn get_memory_stack_offset(
960    ctx: &mut FunctionEnvMut<'_, WasiEnv>,
961) -> Result<u64, String> {
962    let stack_upper = get_stack_upper(ctx.data());
963    let stack_pointer = unsafe { get_memory_stack_pointer(ctx) }?;
964    Ok(stack_upper - stack_pointer)
965}
966
967pub(crate) fn set_memory_stack_offset(
968    env: &WasiEnv,
969    store: &mut impl AsStoreMut,
970    offset: u64,
971) -> Result<(), String> {
972    // Sets the stack pointer
973    let stack_upper = get_stack_upper(env);
974    let stack_pointer = stack_upper - offset;
975    if let Some(stack_pointer_ptr) = env
976        .inner()
977        .main_module_instance_handles()
978        .stack_pointer
979        .clone()
980    {
981        match stack_pointer_ptr.get(store) {
982            Value::I32(_) => {
983                stack_pointer_ptr.set(store, Value::I32(stack_pointer as i32));
984            }
985            Value::I64(_) => {
986                stack_pointer_ptr.set(store, Value::I64(stack_pointer as i64));
987            }
988            _ => {
989                return Err(
990                    "failed to save stack: __stack_pointer global is of an unknown type"
991                        .to_string(),
992                );
993            }
994        }
995    } else {
996        return Err("failed to save stack: no __stack_pointer global".to_string());
997    }
998    Ok(())
999}
1000
1001#[allow(dead_code)]
1002pub(crate) fn get_memory_stack<M: MemorySize>(
1003    env: &WasiEnv,
1004    store: &mut impl AsStoreMut,
1005) -> Result<BytesMut, String> {
1006    // Get the current value of the stack pointer (which we will use
1007    // to save all of the stack)
1008    let stack_base = get_stack_upper(env);
1009    let stack_pointer = if let Some(stack_pointer) = env
1010        .inner()
1011        .main_module_instance_handles()
1012        .stack_pointer
1013        .clone()
1014    {
1015        match stack_pointer.get(store) {
1016            Value::I32(a) => a as u64,
1017            Value::I64(a) => a as u64,
1018            _ => stack_base,
1019        }
1020    } else {
1021        return Err("failed to save stack: no __stack_pointer global".to_string());
1022    };
1023    let memory = env
1024        .try_memory_view(store)
1025        .ok_or_else(|| "unable to access the memory of the instance".to_string())?;
1026    let stack_offset = env.layout.stack_upper - stack_pointer;
1027
1028    // Read the memory stack into a vector
1029    let memory_stack_ptr = WasmPtr::<u8, M>::new(
1030        stack_pointer
1031            .try_into()
1032            .map_err(|err| format!("failed to save stack: stack pointer overflow (stack_pointer={}, stack_lower={}, stack_upper={})", stack_offset, env.layout.stack_lower, env.layout.stack_upper))?,
1033    );
1034
1035    memory_stack_ptr
1036        .slice(
1037            &memory,
1038            stack_offset
1039                .try_into()
1040                .map_err(|err| format!("failed to save stack: stack pointer overflow (stack_pointer={}, stack_lower={}, stack_upper={})", stack_offset, env.layout.stack_lower, env.layout.stack_upper))?,
1041        )
1042        .and_then(|memory_stack| memory_stack.read_to_bytes())
1043        .map_err(|err| format!("failed to read stack: {err}"))
1044}
1045
1046#[allow(dead_code)]
1047pub(crate) fn set_memory_stack<M: MemorySize>(
1048    env: &WasiEnv,
1049    store: &mut impl AsStoreMut,
1050    stack: Bytes,
1051) -> Result<(), String> {
1052    // First we restore the memory stack
1053    let stack_upper = get_stack_upper(env);
1054    let stack_offset = stack.len() as u64;
1055    let stack_pointer = stack_upper - stack_offset;
1056    let stack_ptr = WasmPtr::<u8, M>::new(
1057        stack_pointer
1058            .try_into()
1059            .map_err(|_| "failed to restore stack: stack pointer overflow".to_string())?,
1060    );
1061
1062    let memory = env
1063        .try_memory_view(store)
1064        .ok_or_else(|| "unable to set the stack pointer of the instance".to_string())?;
1065    stack_ptr
1066        .slice(
1067            &memory,
1068            stack_offset
1069                .try_into()
1070                .map_err(|_| "failed to restore stack: stack pointer overflow".to_string())?,
1071        )
1072        .and_then(|memory_stack| memory_stack.write_slice(&stack[..]))
1073        .map_err(|err| format!("failed to write stack: {err}"))?;
1074
1075    // Set the stack pointer itself and return
1076    set_memory_stack_offset(env, store, stack_offset)?;
1077    Ok(())
1078}
1079
1080/// Puts the process to deep sleep and wakes it again when
1081/// the supplied future completes
1082#[must_use = "you must return the result immediately so the stack can unwind"]
1083pub(crate) fn deep_sleep<M: MemorySize>(
1084    mut ctx: FunctionEnvMut<'_, WasiEnv>,
1085    trigger: Pin<Box<AsyncifyFuture>>,
1086) -> Result<(), WasiError> {
1087    // Grab all the globals and serialize them
1088    let store_data = crate::utils::store::capture_store_snapshot(&mut ctx.as_store_mut())
1089        .serialize()
1090        .unwrap();
1091    let store_data = Bytes::from(store_data);
1092    let thread_start = ctx.data().thread.thread_start_type();
1093
1094    // Perform the unwind action
1095    let tasks = ctx.data().tasks().clone();
1096    let res = unwind::<M, _>(ctx, move |mut ctx, memory_stack, rewind_stack| {
1097        let memory_stack = memory_stack.freeze();
1098        let rewind_stack = rewind_stack.freeze();
1099        let thread_layout = ctx.data().thread.memory_layout().clone();
1100
1101        // If journal'ing is enabled then we dump the stack into the journal
1102        if ctx.data().enable_journal {
1103            // Grab all the globals and serialize them
1104            let store_data = crate::utils::store::capture_store_snapshot(&mut ctx.as_store_mut())
1105                .serialize()
1106                .unwrap();
1107            let store_data = Bytes::from(store_data);
1108
1109            tracing::trace!(
1110                "stack snapshot unwind (memory_stack={}, rewind_stack={}, store_data={})",
1111                memory_stack.len(),
1112                rewind_stack.len(),
1113                store_data.len(),
1114            );
1115
1116            #[cfg(feature = "journal")]
1117            {
1118                // Write our thread state to the snapshot
1119                let tid = ctx.data().thread.tid();
1120                let thread_start = ctx.data().thread.thread_start_type();
1121                if let Err(err) = JournalEffector::save_thread_state::<M>(
1122                    &mut ctx,
1123                    tid,
1124                    memory_stack.clone(),
1125                    rewind_stack.clone(),
1126                    store_data.clone(),
1127                    thread_start,
1128                    thread_layout.clone(),
1129                ) {
1130                    return wasmer_types::OnCalledAction::Trap(err.into());
1131                }
1132            }
1133
1134            // If all the threads are now in a deep sleep state
1135            // then we can trigger the idle snapshot event
1136            let inner = ctx.data().process.inner.clone();
1137            let is_idle = {
1138                let mut guard = inner.0.lock().unwrap();
1139                guard.threads.values().all(WasiThread::is_deep_sleeping)
1140            };
1141
1142            // When we idle the journal functionality may be set
1143            // will take a snapshot of the memory and threads so
1144            // that it can resumed.
1145            #[cfg(feature = "journal")]
1146            {
1147                if is_idle && ctx.data_mut().has_snapshot_trigger(SnapshotTrigger::Idle) {
1148                    let mut guard = inner.0.lock().unwrap();
1149                    if let Err(err) = JournalEffector::save_memory_and_snapshot(
1150                        &mut ctx,
1151                        &mut guard,
1152                        SnapshotTrigger::Idle,
1153                    ) {
1154                        return wasmer_types::OnCalledAction::Trap(err.into());
1155                    }
1156                }
1157            }
1158        }
1159
1160        // Schedule the process on the stack so that it can be resumed
1161        OnCalledAction::Trap(Box::new(WasiError::DeepSleep(DeepSleepWork {
1162            trigger,
1163            rewind: RewindState {
1164                memory_stack,
1165                rewind_stack,
1166                store_data,
1167                start: thread_start,
1168                layout: thread_layout,
1169                is_64bit: M::is_64bit(),
1170            },
1171        })))
1172    })?;
1173
1174    // If there is an error then exit the process, otherwise we are done
1175    match res {
1176        Errno::Success => Ok(()),
1177        err => Err(WasiError::Exit(ExitCode::from(err))),
1178    }
1179}
1180
1181#[must_use = "you must return the result immediately so the stack can unwind"]
1182pub fn unwind<M: MemorySize, F>(
1183    mut ctx: FunctionEnvMut<'_, WasiEnv>,
1184    callback: F,
1185) -> Result<Errno, WasiError>
1186where
1187    F: FnOnce(FunctionEnvMut<'_, WasiEnv>, BytesMut, BytesMut) -> OnCalledAction
1188        + Send
1189        + Sync
1190        + 'static,
1191{
1192    // Get the current stack pointer (this will be used to determine the
1193    // upper limit of stack space remaining to unwind into)
1194    let (env, mut store) = ctx.data_and_store_mut();
1195    let memory_stack = match get_memory_stack::<M>(env, &mut store) {
1196        Ok(a) => a,
1197        Err(err) => {
1198            warn!("unable to get the memory stack - {}", err);
1199            return Err(WasiError::Exit(Errno::Unknown.into()));
1200        }
1201    };
1202
1203    // Perform a check to see if we have enough room
1204    let env = ctx.data();
1205    let memory = unsafe { env.memory_view(&ctx) };
1206
1207    // Write the addresses to the start of the stack space
1208    let unwind_pointer = env.layout.stack_lower;
1209    let unwind_data_start =
1210        unwind_pointer + (std::mem::size_of::<__wasi_asyncify_t<M::Offset>>() as u64);
1211    let unwind_data = __wasi_asyncify_t::<M::Offset> {
1212        start: wasi_try_ok!(unwind_data_start.try_into().map_err(|_| Errno::Overflow)),
1213        end: wasi_try_ok!(
1214            (env.layout.stack_upper - memory_stack.len() as u64)
1215                .try_into()
1216                .map_err(|_| Errno::Overflow)
1217        ),
1218    };
1219    let unwind_data_ptr: WasmPtr<__wasi_asyncify_t<M::Offset>, M> = WasmPtr::new(wasi_try_ok!(
1220        unwind_pointer.try_into().map_err(|_| Errno::Overflow)
1221    ));
1222    wasi_try_mem_ok!(unwind_data_ptr.write(&memory, unwind_data));
1223
1224    // Invoke the callback that will prepare to unwind
1225    // We need to start unwinding the stack
1226    let asyncify_data = wasi_try_ok!(unwind_pointer.try_into().map_err(|_| Errno::Overflow));
1227    if let Some(asyncify_start_unwind) = env
1228        .inner()
1229        .static_module_instance_handles()
1230        .and_then(|handles| handles.asyncify_start_unwind.clone())
1231    {
1232        asyncify_start_unwind.call(&mut ctx, asyncify_data);
1233    } else {
1234        warn!("failed to unwind the stack because the asyncify_start_unwind export is missing");
1235        return Err(WasiError::Exit(Errno::Noexec.into()));
1236    }
1237
1238    // Set callback that will be invoked when this process finishes
1239    let env = ctx.data();
1240    let unwind_stack_begin: u64 = unwind_data.start.into();
1241    let total_stack_space = env.layout.stack_size;
1242    let func = ctx.as_ref();
1243    trace!(
1244        stack_upper = env.layout.stack_upper,
1245        stack_lower = env.layout.stack_lower,
1246        "wasi[{}:{}]::unwinding (used_stack_space={} total_stack_space={})",
1247        ctx.data().pid(),
1248        ctx.data().tid(),
1249        memory_stack.len(),
1250        total_stack_space
1251    );
1252    ctx.as_store_mut().on_called(move |mut store| {
1253        let mut ctx = func.into_mut(&mut store);
1254        let env = ctx.data();
1255        let memory = env
1256            .try_memory_view(&ctx)
1257            .ok_or_else(|| "failed to save stack: stack pointer overflow - unable to access the memory of the instance".to_string())?;
1258
1259        let unwind_data_ptr: WasmPtr<__wasi_asyncify_t<M::Offset>, M> = WasmPtr::new(
1260            unwind_pointer
1261                .try_into()
1262                .map_err(|_| Errno::Overflow)
1263                .unwrap(),
1264        );
1265        let unwind_data_result = unwind_data_ptr.read(&memory).unwrap();
1266        let unwind_stack_finish: u64 = unwind_data_result.start.into();
1267        let unwind_size = unwind_stack_finish - unwind_stack_begin;
1268        trace!(
1269            "wasi[{}:{}]::unwound (memory_stack_size={} unwind_size={})",
1270            ctx.data().pid(),
1271            ctx.data().tid(),
1272            memory_stack.len(),
1273            unwind_size
1274        );
1275
1276        // Read the memory stack into a vector
1277        let unwind_stack_ptr = WasmPtr::<u8, M>::new(
1278            unwind_stack_begin
1279                .try_into()
1280                .map_err(|_| "failed to save stack: stack pointer overflow".to_string())?,
1281        );
1282        let unwind_stack = unwind_stack_ptr
1283            .slice(
1284                &memory,
1285                unwind_size
1286                    .try_into()
1287                    .map_err(|_| "failed to save stack: stack pointer overflow".to_string())?,
1288            )
1289            .and_then(|memory_stack| memory_stack.read_to_bytes())
1290            .map_err(|err| format!("failed to read stack: {err}"))?;
1291
1292        // Notify asyncify that we are no longer unwinding
1293        if let Some(asyncify_stop_unwind) = env
1294            .inner()
1295            .static_module_instance_handles()
1296            .and_then(|i| i.asyncify_stop_unwind.clone())
1297        {
1298            asyncify_stop_unwind.call(&mut ctx);
1299        } else {
1300            warn!("failed to unwind the stack because the asyncify_stop_unwind export is missing");
1301            return Ok(OnCalledAction::Finish);
1302        }
1303
1304        Ok(callback(ctx, memory_stack, unwind_stack))
1305    });
1306
1307    // We need to exit the function so that it can unwind and then invoke the callback
1308    Ok(Errno::Success)
1309}
1310
1311// NOTE: not tracing-instrumented because [`rewind_ext`] already is.
1312#[must_use = "the action must be passed to the call loop"]
1313pub fn rewind<M: MemorySize, T>(
1314    mut ctx: FunctionEnvMut<WasiEnv>,
1315    memory_stack: Option<Bytes>,
1316    rewind_stack: Bytes,
1317    store_data: Bytes,
1318    result: T,
1319) -> Errno
1320where
1321    T: serde::Serialize,
1322{
1323    let rewind_result = bincode::serde::encode_to_vec(&result, config::legacy())
1324        .unwrap()
1325        .into();
1326    rewind_ext::<M>(
1327        &mut ctx,
1328        memory_stack,
1329        rewind_stack,
1330        store_data,
1331        RewindResultType::RewindWithResult(rewind_result),
1332    )
1333}
1334
1335#[instrument(level = "trace", skip_all, fields(rewind_stack_len = rewind_stack.len(), store_data_len = store_data.len()))]
1336#[must_use = "the action must be passed to the call loop"]
1337pub fn rewind_ext<M: MemorySize>(
1338    ctx: &mut FunctionEnvMut<WasiEnv>,
1339    memory_stack: Option<Bytes>,
1340    rewind_stack: Bytes,
1341    store_data: Bytes,
1342    rewind_result: RewindResultType,
1343) -> Errno {
1344    // Store the memory stack so that it can be restored later
1345    ctx.data_mut().thread.set_rewind(RewindResult {
1346        memory_stack,
1347        rewind_result,
1348    });
1349
1350    // Deserialize the store data back into a snapshot
1351    let store_snapshot = match StoreSnapshot::deserialize(&store_data[..]) {
1352        Ok(a) => a,
1353        Err(err) => {
1354            warn!("snapshot restore failed - the store snapshot could not be deserialized");
1355            return Errno::Unknown;
1356        }
1357    };
1358    crate::utils::store::restore_store_snapshot(ctx, &store_snapshot);
1359    let env = ctx.data();
1360    let memory = match env.try_memory_view(&ctx) {
1361        Some(v) => v,
1362        None => {
1363            warn!("snapshot restore failed - unable to access the memory of the instance");
1364            return Errno::Unknown;
1365        }
1366    };
1367
1368    // Write the addresses to the start of the stack space
1369    let rewind_pointer = env.layout.stack_lower;
1370    let rewind_data_start =
1371        rewind_pointer + (std::mem::size_of::<__wasi_asyncify_t<M::Offset>>() as u64);
1372    let rewind_data_end = rewind_data_start + (rewind_stack.len() as u64);
1373    if rewind_data_end > env.layout.stack_upper {
1374        warn!(
1375            "attempting to rewind a stack bigger than the allocated stack space ({} > {})",
1376            rewind_data_end, env.layout.stack_upper
1377        );
1378        return Errno::Overflow;
1379    }
1380    let rewind_data = __wasi_asyncify_t::<M::Offset> {
1381        start: wasi_try!(rewind_data_end.try_into().map_err(|_| Errno::Overflow)),
1382        end: wasi_try!(
1383            env.layout
1384                .stack_upper
1385                .try_into()
1386                .map_err(|_| Errno::Overflow)
1387        ),
1388    };
1389    let rewind_data_ptr: WasmPtr<__wasi_asyncify_t<M::Offset>, M> = WasmPtr::new(wasi_try!(
1390        rewind_pointer.try_into().map_err(|_| Errno::Overflow)
1391    ));
1392    wasi_try_mem!(rewind_data_ptr.write(&memory, rewind_data));
1393
1394    // Copy the data to the address
1395    let rewind_stack_ptr = WasmPtr::<u8, M>::new(wasi_try!(
1396        rewind_data_start.try_into().map_err(|_| Errno::Overflow)
1397    ));
1398    wasi_try_mem!(
1399        rewind_stack_ptr
1400            .slice(
1401                &memory,
1402                wasi_try!(rewind_stack.len().try_into().map_err(|_| Errno::Overflow))
1403            )
1404            .and_then(|stack| { stack.write_slice(&rewind_stack[..]) })
1405    );
1406
1407    // Invoke the callback that will prepare to rewind
1408    let asyncify_data = wasi_try!(rewind_pointer.try_into().map_err(|_| Errno::Overflow));
1409    if let Some(asyncify_start_rewind) = env
1410        .inner()
1411        .static_module_instance_handles()
1412        .and_then(|a| a.asyncify_start_rewind.clone())
1413    {
1414        asyncify_start_rewind.call(ctx, asyncify_data);
1415    } else {
1416        warn!(
1417            "failed to rewind the stack because the asyncify_start_rewind export is missing or inaccessible"
1418        );
1419        return Errno::Noexec;
1420    }
1421
1422    Errno::Success
1423}
1424
1425pub fn rewind_ext2(
1426    ctx: &mut FunctionEnvMut<WasiEnv>,
1427    rewind_state: RewindStateOption,
1428) -> Result<(), ExitCode> {
1429    if let Some((rewind_state, rewind_result)) = rewind_state {
1430        tracing::trace!("Rewinding");
1431        let errno = if rewind_state.is_64bit {
1432            crate::rewind_ext::<wasmer_types::Memory64>(
1433                ctx,
1434                Some(rewind_state.memory_stack),
1435                rewind_state.rewind_stack,
1436                rewind_state.store_data,
1437                rewind_result,
1438            )
1439        } else {
1440            crate::rewind_ext::<wasmer_types::Memory32>(
1441                ctx,
1442                Some(rewind_state.memory_stack),
1443                rewind_state.rewind_stack,
1444                rewind_state.store_data,
1445                rewind_result,
1446            )
1447        };
1448
1449        if errno != Errno::Success {
1450            let exit_code = ExitCode::from(errno);
1451            ctx.data().blocking_on_exit(Some(exit_code));
1452            return Err(exit_code);
1453        }
1454    }
1455
1456    Ok(())
1457}
1458
1459pub fn anyhow_err_to_runtime_err(err: anyhow::Error) -> WasiRuntimeError {
1460    WasiRuntimeError::Runtime(RuntimeError::user(err.into()))
1461}
1462
1463pub(crate) unsafe fn handle_rewind<M: MemorySize, T>(
1464    ctx: &mut FunctionEnvMut<'_, WasiEnv>,
1465) -> Option<T>
1466where
1467    T: serde::de::DeserializeOwned,
1468{
1469    unsafe { handle_rewind_ext::<M, T>(ctx, HandleRewindType::ResultDriven) }.flatten()
1470}
1471
1472pub(crate) enum HandleRewindType {
1473    /// Handle rewind types that have a result to be processed
1474    ResultDriven,
1475    /// Handle rewind types that are result-less (generally these
1476    /// are caused by snapshot events)
1477    ResultLess,
1478}
1479
1480pub(crate) unsafe fn handle_rewind_ext_with_default<M: MemorySize, T>(
1481    ctx: &mut FunctionEnvMut<'_, WasiEnv>,
1482    type_: HandleRewindType,
1483) -> Option<T>
1484where
1485    T: serde::de::DeserializeOwned + Default,
1486{
1487    let ret = unsafe { handle_rewind_ext::<M, T>(ctx, type_) };
1488    ret.unwrap_or_default()
1489}
1490
1491pub(crate) unsafe fn handle_rewind_ext<M: MemorySize, T>(
1492    ctx: &mut FunctionEnvMut<'_, WasiEnv>,
1493    type_: HandleRewindType,
1494) -> Option<Option<T>>
1495where
1496    T: serde::de::DeserializeOwned,
1497{
1498    let env = ctx.data();
1499    if !env.thread.has_rewind_of_type(type_) {
1500        return None;
1501    };
1502
1503    // If the stack has been restored
1504    let tid = env.tid();
1505    let pid = env.pid();
1506    if let Some(result) = ctx.data_mut().thread.take_rewind() {
1507        // Deserialize the result
1508        let memory_stack = result.memory_stack;
1509
1510        // Notify asyncify that we are no longer rewinding
1511        let env = ctx.data();
1512        if let Some(asyncify_stop_rewind) = env
1513            .inner()
1514            .static_module_instance_handles()
1515            .and_then(|handles| handles.asyncify_stop_rewind.clone())
1516        {
1517            asyncify_stop_rewind.call(ctx);
1518        } else {
1519            warn!(
1520                "failed to handle rewind because the asyncify_stop_rewind export is missing or inaccessible"
1521            );
1522            return Some(None);
1523        }
1524
1525        // Restore the memory stack
1526        let (env, mut store) = ctx.data_and_store_mut();
1527        if let Some(memory_stack) = memory_stack {
1528            set_memory_stack::<M>(env, &mut store, memory_stack);
1529        }
1530
1531        match result.rewind_result {
1532            RewindResultType::RewindRestart => {
1533                tracing::trace!(%pid, %tid, "rewind for syscall restart");
1534                None
1535            }
1536            RewindResultType::RewindWithoutResult => {
1537                tracing::trace!(%pid, %tid, "rewind with no result");
1538                Some(None)
1539            }
1540            RewindResultType::RewindWithResult(rewind_result) => {
1541                tracing::trace!(%pid, %tid, "rewind with result (data={})", rewind_result.len());
1542                let (ret, _) = bincode::serde::decode_from_slice(&rewind_result, config::legacy())
1543                    .expect("failed to deserialize the rewind result");
1544                Some(Some(ret))
1545            }
1546        }
1547    } else {
1548        tracing::trace!(%pid, %tid, "rewind miss");
1549        Some(None)
1550    }
1551}
1552
1553// Function to prepare the WASI environment
1554pub(crate) fn _prepare_wasi(
1555    wasi_env: &mut WasiEnv,
1556    args: Option<Vec<String>>,
1557    envs: Option<Vec<(String, String)>>,
1558    signals: Option<Vec<SignalDisposition>>,
1559) {
1560    // Swap out the arguments with the new ones
1561    if let Some(args) = args {
1562        *wasi_env.state.args.lock().unwrap() = args;
1563    }
1564
1565    // Update the env vars
1566    if let Some(envs) = envs {
1567        let mut guard = wasi_env.state.envs.lock().unwrap();
1568
1569        let mut existing_envs = guard
1570            .iter()
1571            .map(|b| {
1572                let string = String::from_utf8_lossy(b);
1573                let (key, val) = string.split_once('=').expect("env var is malformed");
1574
1575                (key.to_string(), val.to_string().as_bytes().to_vec())
1576            })
1577            .collect::<Vec<_>>();
1578
1579        for (key, val) in envs {
1580            let val = val.as_bytes().to_vec();
1581            match existing_envs
1582                .iter_mut()
1583                .find(|(existing_key, _)| existing_key == &key)
1584            {
1585                Some((_, existing_val)) => *existing_val = val,
1586                None => existing_envs.push((key, val)),
1587            }
1588        }
1589
1590        let envs = conv_env_vars(existing_envs);
1591
1592        *guard = envs;
1593
1594        drop(guard)
1595    }
1596
1597    if let Some(signals) = signals {
1598        let mut guard = wasi_env.state.signals.lock().unwrap();
1599        for signal in signals {
1600            guard.insert(signal.sig, signal.disp);
1601        }
1602        drop(guard);
1603    }
1604}
1605
1606pub(crate) fn conv_spawn_err_to_errno(err: &SpawnError) -> Errno {
1607    match err {
1608        SpawnError::AccessDenied => Errno::Access,
1609        SpawnError::Unsupported => Errno::Noexec,
1610        _ if err.is_not_found() => Errno::Noent,
1611        _ => Errno::Inval,
1612    }
1613}
1614
1615pub(crate) fn conv_spawn_err_to_exit_code(err: &SpawnError) -> ExitCode {
1616    conv_spawn_err_to_errno(err).into()
1617}