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#[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 res = work => res,
286 _ = timeout => Ok(Err(Errno::Timedout)),
288 }
289 };
290
291 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 block_on(work)
304}
305
306pub(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 if let Some(exit_code) = env.should_exit() {
323 return Err(WasiError::Exit(exit_code));
324 }
325
326 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 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
360pub type AsyncifyFuture = dyn Future<Output = Bytes> + Send + Sync + 'static;
364
365struct 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 self.ctx.data().thread.signals_subscribe(cx.waker());
422 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 Finish(FunctionEnvMut<'a, WasiEnv>, R),
441 Unwind,
444}
445
446pub(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 if env.enable_exponential_cpu_backoff.is_none() {
464 return Ok(Ok(ctx));
465 }
466
467 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
482pub(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 let deep_sleep_time = match ctx.data().enable_journal {
501 true => Duration::from_micros(100),
502 false => Duration::from_millis(50),
503 };
504
505 let mut trigger = Box::pin(work);
507
508 let tasks = ctx.data().tasks().clone();
510 let work = async move {
511 let env = ctx.data();
512
513 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 res = AsyncifyPoller {
533 ctx: &mut ctx,
534 work: &mut trigger,
535 } => {
536 let result = res?;
537 AsyncifyAction::Finish(ctx, result)
538 },
539 _ = deep_sleep_wait => {
541 let pid = ctx.data().pid();
542 let tid = ctx.data().tid();
543
544 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 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_on(work)
567}
568
569pub(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 Ok(block_on(work))
587}
588
589#[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
601pub(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 let socket = socket.clone();
623 drop(guard);
624
625 actor(socket, fd_entry)
627 }
628 _ => {
629 return Err(Errno::Notsock);
630 }
631 }
632 };
633
634 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
651pub(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 let socket = socket.clone();
677 drop(guard);
678
679 let mut work = actor(socket, fd_entry);
681
682 block_on(work)
685 }
686 _ => Err(Errno::Notsock),
687 }
688}
689
690pub(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 let socket = socket.clone();
718 drop(guard);
719
720 actor(socket, fd_entry)
722 }
723 _ => Err(Errno::Notsock),
724 }
725}
726
727pub(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 let socket = socket.clone();
753 drop(guard);
754
755 actor(socket, fd_entry)
757 }
758 _ => Err(Errno::Notsock),
759 }
760}
761
762pub(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 let work = actor(socket, fd_entry.inner.flags);
799
800 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 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 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 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 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 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_memory_stack_offset(env, store, stack_offset)?;
1077 Ok(())
1078}
1079
1080#[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 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 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 ctx.data().enable_journal {
1103 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 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 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 #[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 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 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 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 let env = ctx.data();
1205 let memory = unsafe { env.memory_view(&ctx) };
1206
1207 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 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 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 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 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 Ok(Errno::Success)
1309}
1310
1311#[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 ctx.data_mut().thread.set_rewind(RewindResult {
1346 memory_stack,
1347 rewind_result,
1348 });
1349
1350 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 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 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 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 ResultDriven,
1475 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 let tid = env.tid();
1505 let pid = env.pid();
1506 if let Some(result) = ctx.data_mut().thread.take_rewind() {
1507 let memory_stack = result.memory_stack;
1509
1510 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 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
1553pub(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 if let Some(args) = args {
1562 *wasi_env.state.args.lock().unwrap() = args;
1563 }
1564
1565 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}