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