wasmer_wasi/
utils.rs

1use std::collections::BTreeSet;
2#[cfg(not(feature = "js"))]
3use wasmer::vm::VMSharedMemory;
4use wasmer::{AsStoreMut, Imports, Memory, Module};
5use wasmer_wasi_types::wasi::Errno;
6
7#[allow(dead_code)]
8/// Check if a provided module is compiled for some version of WASI.
9/// Use [`get_wasi_version`] to find out which version of WASI the module is.
10pub fn is_wasi_module(module: &Module) -> bool {
11    get_wasi_version(module, false).is_some()
12}
13
14#[allow(dead_code)]
15#[cfg(feature = "wasix")]
16/// Returns if the module is WASIX or not
17pub fn is_wasix_module(module: &Module) -> bool {
18    match get_wasi_versions(module, false).ok_or(false) {
19        Ok(wasi_versions) => {
20            wasi_versions.contains(&WasiVersion::Wasix32v1)
21                || wasi_versions.contains(&WasiVersion::Wasix64v1)
22        }
23        Err(_) => false,
24    }
25}
26
27pub fn map_io_err(err: std::io::Error) -> Errno {
28    use std::io::ErrorKind;
29    match err.kind() {
30        ErrorKind::NotFound => Errno::Noent,
31        ErrorKind::PermissionDenied => Errno::Perm,
32        ErrorKind::ConnectionRefused => Errno::Connrefused,
33        ErrorKind::ConnectionReset => Errno::Connreset,
34        ErrorKind::ConnectionAborted => Errno::Connaborted,
35        ErrorKind::NotConnected => Errno::Notconn,
36        ErrorKind::AddrInUse => Errno::Addrinuse,
37        ErrorKind::AddrNotAvailable => Errno::Addrnotavail,
38        ErrorKind::BrokenPipe => Errno::Pipe,
39        ErrorKind::AlreadyExists => Errno::Exist,
40        ErrorKind::WouldBlock => Errno::Again,
41        ErrorKind::InvalidInput => Errno::Io,
42        ErrorKind::InvalidData => Errno::Io,
43        ErrorKind::TimedOut => Errno::Timedout,
44        ErrorKind::WriteZero => Errno::Io,
45        ErrorKind::Interrupted => Errno::Intr,
46        ErrorKind::Other => Errno::Io,
47        ErrorKind::UnexpectedEof => Errno::Io,
48        ErrorKind::Unsupported => Errno::Notsup,
49        _ => Errno::Io,
50    }
51}
52
53/// Imports (any) shared memory into the imports.
54/// (if the module does not import memory then this function is ignored)
55#[cfg(not(feature = "js"))]
56pub fn wasi_import_shared_memory(
57    imports: &mut Imports,
58    module: &Module,
59    store: &mut impl AsStoreMut,
60) {
61    // Determine if shared memory needs to be created and imported
62    let shared_memory = module
63        .imports()
64        .memories()
65        .next()
66        .map(|a| *a.ty())
67        .map(|ty| {
68            let style = store.as_store_ref().tunables().memory_style(&ty);
69            VMSharedMemory::new(&ty, &style).unwrap()
70        });
71
72    if let Some(memory) = shared_memory {
73        // if the memory has already be defined, don't redefine it!
74        if !imports.exists("env", "memory") {
75            imports.define(
76                "env",
77                "memory",
78                Memory::new_from_existing(store, memory.into()),
79            );
80        }
81    };
82}
83#[cfg(feature = "js")]
84pub fn wasi_import_shared_memory(
85    _imports: &mut Imports,
86    _module: &Module,
87    _store: &mut impl AsStoreMut,
88) {
89}
90
91/// The version of WASI. This is determined by the imports namespace
92/// string.
93#[derive(Debug, Clone, Copy, Eq)]
94pub enum WasiVersion {
95    /// `wasi_unstable`.
96    Snapshot0,
97
98    /// `wasi_snapshot_preview1`.
99    Snapshot1,
100
101    /// `wasix_32v1`.
102    Wasix32v1,
103
104    /// `wasix_64v1`.
105    Wasix64v1,
106
107    /// Latest version.
108    ///
109    /// It's a “floating” version, i.e. it's an alias to the latest
110    /// version (for the moment, `Snapshot1`). Using this version is a
111    /// way to ensure that modules will run only if they come with the
112    /// latest WASI version (in case of security issues for instance),
113    /// by just updating the runtime.
114    ///
115    /// Note that this version is never returned by an API. It is
116    /// provided only by the user.
117    Latest,
118}
119
120impl WasiVersion {
121    /// Get the version as its namespace str as it appears in Wasm modules.
122    pub const fn get_namespace_str(&self) -> &'static str {
123        match *self {
124            WasiVersion::Snapshot0 => SNAPSHOT0_NAMESPACE,
125            WasiVersion::Snapshot1 => SNAPSHOT1_NAMESPACE,
126            WasiVersion::Wasix32v1 => WASIX_32V1_NAMESPACE,
127            WasiVersion::Wasix64v1 => WASIX_64V1_NAMESPACE,
128            WasiVersion::Latest => SNAPSHOT1_NAMESPACE,
129        }
130    }
131}
132
133impl PartialEq<WasiVersion> for WasiVersion {
134    fn eq(&self, other: &Self) -> bool {
135        matches!(
136            (*self, *other),
137            (Self::Snapshot1, Self::Latest)
138                | (Self::Latest, Self::Snapshot1)
139                | (Self::Latest, Self::Latest)
140                | (Self::Snapshot0, Self::Snapshot0)
141                | (Self::Snapshot1, Self::Snapshot1)
142                | (Self::Wasix32v1, Self::Wasix32v1)
143                | (Self::Wasix64v1, Self::Wasix64v1)
144        )
145    }
146}
147
148impl PartialOrd for WasiVersion {
149    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
150        Some(self.cmp(other))
151    }
152}
153
154impl Ord for WasiVersion {
155    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
156        if self == other {
157            return std::cmp::Ordering::Equal;
158        }
159        match (*self, *other) {
160            (Self::Snapshot1, Self::Snapshot0) => std::cmp::Ordering::Greater,
161            (Self::Wasix32v1, Self::Snapshot1) | (Self::Wasix32v1, Self::Snapshot0) => {
162                std::cmp::Ordering::Greater
163            }
164            (Self::Wasix64v1, Self::Wasix32v1)
165            | (Self::Wasix64v1, Self::Snapshot1)
166            | (Self::Wasix64v1, Self::Snapshot0) => std::cmp::Ordering::Greater,
167            (Self::Latest, Self::Wasix64v1)
168            | (Self::Latest, Self::Wasix32v1)
169            | (Self::Latest, Self::Snapshot1)
170            | (Self::Latest, Self::Snapshot0) => std::cmp::Ordering::Greater,
171            // they are not equal and not greater so they must be less
172            (_, _) => std::cmp::Ordering::Less,
173        }
174    }
175}
176
177/// Namespace for the `Snapshot0` version.
178const SNAPSHOT0_NAMESPACE: &str = "wasi_unstable";
179
180/// Namespace for the `Snapshot1` version.
181const SNAPSHOT1_NAMESPACE: &str = "wasi_snapshot_preview1";
182
183/// Namespace for the `wasix` version.
184const WASIX_32V1_NAMESPACE: &str = "wasix_32v1";
185
186/// Namespace for the `wasix` version.
187const WASIX_64V1_NAMESPACE: &str = "wasix_64v1";
188
189/// Detect the version of WASI being used based on the import
190/// namespaces.
191///
192/// A strict detection expects that all imports live in a single WASI
193/// namespace. A non-strict detection expects that at least one WASI
194/// namespace exists to detect the version. Note that the strict
195/// detection is faster than the non-strict one.
196pub fn get_wasi_version(module: &Module, strict: bool) -> Option<WasiVersion> {
197    let mut imports = module.imports().functions().map(|f| f.module().to_owned());
198
199    if strict {
200        let first_module = imports.next()?;
201        if imports.all(|module| module == first_module) {
202            match first_module.as_str() {
203                SNAPSHOT0_NAMESPACE => Some(WasiVersion::Snapshot0),
204                SNAPSHOT1_NAMESPACE => Some(WasiVersion::Snapshot1),
205                WASIX_32V1_NAMESPACE => Some(WasiVersion::Wasix32v1),
206                WASIX_64V1_NAMESPACE => Some(WasiVersion::Wasix64v1),
207                _ => None,
208            }
209        } else {
210            None
211        }
212    } else {
213        // Check that at least a WASI namespace exists, and use the
214        // first one in the list to detect the WASI version.
215        imports.find_map(|module| match module.as_str() {
216            SNAPSHOT0_NAMESPACE => Some(WasiVersion::Snapshot0),
217            SNAPSHOT1_NAMESPACE => Some(WasiVersion::Snapshot1),
218            WASIX_32V1_NAMESPACE => Some(WasiVersion::Wasix32v1),
219            WASIX_64V1_NAMESPACE => Some(WasiVersion::Wasix64v1),
220            _ => None,
221        })
222    }
223}
224
225/// Like [`get_wasi_version`] but detects multiple WASI versions in a single module.
226/// Thus `strict` behaves differently in this function as multiple versions are
227/// always supported. `strict` indicates whether non-WASI imports should trigger a
228/// failure or be ignored.
229pub fn get_wasi_versions(module: &Module, strict: bool) -> Option<BTreeSet<WasiVersion>> {
230    let mut out = BTreeSet::new();
231    let imports = module.imports().functions().map(|f| f.module().to_owned());
232
233    let mut non_wasi_seen = false;
234    for ns in imports {
235        match ns.as_str() {
236            SNAPSHOT0_NAMESPACE => {
237                out.insert(WasiVersion::Snapshot0);
238            }
239            SNAPSHOT1_NAMESPACE => {
240                out.insert(WasiVersion::Snapshot1);
241            }
242            WASIX_32V1_NAMESPACE => {
243                out.insert(WasiVersion::Wasix32v1);
244            }
245            WASIX_64V1_NAMESPACE => {
246                out.insert(WasiVersion::Wasix64v1);
247            }
248            _ => {
249                non_wasi_seen = true;
250            }
251        }
252    }
253    if strict && non_wasi_seen {
254        None
255    } else {
256        Some(out)
257    }
258}
259
260#[cfg(test)]
261mod test {
262    use super::*;
263
264    #[test]
265    fn wasi_version_equality() {
266        assert_eq!(WasiVersion::Snapshot0, WasiVersion::Snapshot0);
267        assert_eq!(WasiVersion::Wasix64v1, WasiVersion::Wasix64v1);
268        assert_eq!(WasiVersion::Wasix32v1, WasiVersion::Wasix32v1);
269        assert_eq!(WasiVersion::Snapshot1, WasiVersion::Snapshot1);
270        assert_eq!(WasiVersion::Snapshot1, WasiVersion::Latest);
271        assert_eq!(WasiVersion::Latest, WasiVersion::Snapshot1);
272        assert_eq!(WasiVersion::Latest, WasiVersion::Latest);
273        assert!(WasiVersion::Wasix32v1 != WasiVersion::Wasix64v1);
274        assert!(WasiVersion::Wasix64v1 != WasiVersion::Wasix32v1);
275        assert!(WasiVersion::Snapshot1 != WasiVersion::Wasix64v1);
276        assert!(WasiVersion::Wasix64v1 != WasiVersion::Snapshot1);
277        assert!(WasiVersion::Snapshot1 != WasiVersion::Wasix32v1);
278        assert!(WasiVersion::Wasix32v1 != WasiVersion::Snapshot1);
279        assert!(WasiVersion::Snapshot0 != WasiVersion::Snapshot1);
280        assert!(WasiVersion::Snapshot1 != WasiVersion::Snapshot0);
281        assert!(WasiVersion::Snapshot0 != WasiVersion::Latest);
282        assert!(WasiVersion::Latest != WasiVersion::Snapshot0);
283        assert!(WasiVersion::Snapshot0 != WasiVersion::Latest);
284        assert!(WasiVersion::Latest != WasiVersion::Snapshot0);
285        assert!(WasiVersion::Wasix32v1 != WasiVersion::Latest);
286        assert!(WasiVersion::Wasix64v1 != WasiVersion::Latest);
287    }
288
289    #[test]
290    fn wasi_version_ordering() {
291        assert!(WasiVersion::Snapshot0 <= WasiVersion::Snapshot0);
292        assert!(WasiVersion::Snapshot1 <= WasiVersion::Snapshot1);
293        assert!(WasiVersion::Wasix32v1 <= WasiVersion::Wasix32v1);
294        assert!(WasiVersion::Wasix64v1 <= WasiVersion::Wasix64v1);
295        assert!(WasiVersion::Latest <= WasiVersion::Latest);
296        assert!(WasiVersion::Snapshot0 >= WasiVersion::Snapshot0);
297        assert!(WasiVersion::Snapshot1 >= WasiVersion::Snapshot1);
298        assert!(WasiVersion::Wasix32v1 >= WasiVersion::Wasix32v1);
299        assert!(WasiVersion::Wasix64v1 >= WasiVersion::Wasix64v1);
300        assert!(WasiVersion::Latest >= WasiVersion::Latest);
301
302        assert!(WasiVersion::Snapshot0 < WasiVersion::Latest);
303        assert!(WasiVersion::Snapshot0 < WasiVersion::Wasix32v1);
304        assert!(WasiVersion::Snapshot0 < WasiVersion::Wasix64v1);
305        assert!(WasiVersion::Snapshot0 < WasiVersion::Snapshot1);
306        assert!(WasiVersion::Latest > WasiVersion::Snapshot0);
307        assert!(WasiVersion::Wasix32v1 > WasiVersion::Snapshot0);
308        assert!(WasiVersion::Wasix64v1 > WasiVersion::Snapshot0);
309        assert!(WasiVersion::Snapshot1 > WasiVersion::Snapshot0);
310
311        assert!(WasiVersion::Snapshot1 < WasiVersion::Wasix32v1);
312        assert!(WasiVersion::Snapshot1 < WasiVersion::Wasix64v1);
313        assert!(WasiVersion::Wasix32v1 > WasiVersion::Snapshot1);
314        assert!(WasiVersion::Wasix64v1 > WasiVersion::Snapshot1);
315
316        assert!(WasiVersion::Wasix32v1 < WasiVersion::Latest);
317        assert!(WasiVersion::Wasix32v1 > WasiVersion::Snapshot1);
318        assert!(WasiVersion::Wasix64v1 < WasiVersion::Latest);
319        assert!(WasiVersion::Wasix64v1 > WasiVersion::Snapshot1);
320        assert!(WasiVersion::Latest > WasiVersion::Wasix32v1);
321        assert!(WasiVersion::Snapshot1 < WasiVersion::Wasix32v1);
322        assert!(WasiVersion::Latest > WasiVersion::Wasix64v1);
323        assert!(WasiVersion::Snapshot1 < WasiVersion::Wasix32v1);
324
325        assert!(WasiVersion::Wasix32v1 < WasiVersion::Wasix64v1);
326        assert!(WasiVersion::Wasix64v1 > WasiVersion::Wasix32v1);
327    }
328}