wasmer_wasix/utils/
mod.rs

1mod dummy_waker;
2mod owned_mutex_guard;
3pub mod store;
4pub mod thread_local_executor;
5mod thread_parker;
6
7#[cfg(feature = "js")]
8pub(crate) mod web;
9
10pub use self::{dummy_waker::WasiDummyWaker, thread_parker::WasiParkingLot};
11
12pub(crate) use owned_mutex_guard::{
13    OwnedRwLockReadGuard, OwnedRwLockWriteGuard, read_owned, write_owned,
14};
15
16use std::collections::BTreeSet;
17
18use wasmer::Module;
19use wasmer_wasix_types::wasi::Errno;
20
21/// Check if a provided module is compiled for some version of WASI.
22/// Use [`get_wasi_version`] to find out which version of WASI the module is.
23pub fn is_wasi_module(module: &Module) -> bool {
24    get_wasi_version(module, false).is_some()
25}
26
27/// Returns if the module is WASIX or not
28pub fn is_wasix_module(module: &Module) -> bool {
29    match get_wasi_versions(module, false).ok_or(false) {
30        Ok(wasi_versions) => {
31            wasi_versions.contains(&WasiVersion::Wasix32v1)
32                || wasi_versions.contains(&WasiVersion::Wasix64v1)
33        }
34        Err(_) => false,
35    }
36}
37
38pub fn map_io_err(err: std::io::Error) -> Errno {
39    From::<std::io::Error>::from(err)
40}
41
42#[cfg(feature = "journal")]
43pub fn map_snapshot_err(err: anyhow::Error) -> Errno {
44    tracing::warn!("unknown snapshot error: {}", err);
45    Errno::Unknown
46}
47
48/// The version of WASI. This is determined by the imports namespace
49/// string.
50#[derive(Debug, Clone, Copy, Eq)]
51pub enum WasiVersion {
52    /// `wasi_unstable`.
53    Snapshot0,
54
55    /// `wasi_snapshot_preview1`.
56    Snapshot1,
57
58    /// `wasix_32v1`.
59    Wasix32v1,
60
61    /// `wasix_64v1`.
62    Wasix64v1,
63
64    /// Latest version.
65    ///
66    /// It's a “floating” version, i.e. it's an alias to the latest
67    /// version (for the moment, `Snapshot1`). Using this version is a
68    /// way to ensure that modules will run only if they come with the
69    /// latest WASI version (in case of security issues for instance),
70    /// by just updating the runtime.
71    ///
72    /// Note that this version is never returned by an API. It is
73    /// provided only by the user.
74    Latest,
75}
76
77impl WasiVersion {
78    /// Get the version as its namespace str as it appears in Wasm modules.
79    pub const fn get_namespace_str(&self) -> &'static str {
80        match *self {
81            WasiVersion::Snapshot0 => SNAPSHOT0_NAMESPACE,
82            WasiVersion::Snapshot1 => SNAPSHOT1_NAMESPACE,
83            WasiVersion::Wasix32v1 => WASIX_32V1_NAMESPACE,
84            WasiVersion::Wasix64v1 => WASIX_64V1_NAMESPACE,
85            WasiVersion::Latest => SNAPSHOT1_NAMESPACE,
86        }
87    }
88}
89
90impl PartialEq<WasiVersion> for WasiVersion {
91    fn eq(&self, other: &Self) -> bool {
92        matches!(
93            (*self, *other),
94            (Self::Snapshot1, Self::Latest)
95                | (Self::Latest, Self::Snapshot1)
96                | (Self::Latest, Self::Latest)
97                | (Self::Snapshot0, Self::Snapshot0)
98                | (Self::Snapshot1, Self::Snapshot1)
99                | (Self::Wasix32v1, Self::Wasix32v1)
100                | (Self::Wasix64v1, Self::Wasix64v1)
101        )
102    }
103}
104
105impl PartialOrd for WasiVersion {
106    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
107        Some(self.cmp(other))
108    }
109}
110
111impl Ord for WasiVersion {
112    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
113        if self == other {
114            return std::cmp::Ordering::Equal;
115        }
116        match (*self, *other) {
117            (Self::Snapshot1, Self::Snapshot0) => std::cmp::Ordering::Greater,
118            (Self::Wasix32v1, Self::Snapshot1) | (Self::Wasix32v1, Self::Snapshot0) => {
119                std::cmp::Ordering::Greater
120            }
121            (Self::Wasix64v1, Self::Wasix32v1)
122            | (Self::Wasix64v1, Self::Snapshot1)
123            | (Self::Wasix64v1, Self::Snapshot0) => std::cmp::Ordering::Greater,
124            (Self::Latest, Self::Wasix64v1)
125            | (Self::Latest, Self::Wasix32v1)
126            | (Self::Latest, Self::Snapshot1)
127            | (Self::Latest, Self::Snapshot0) => std::cmp::Ordering::Greater,
128            // they are not equal and not greater so they must be less
129            (_, _) => std::cmp::Ordering::Less,
130        }
131    }
132}
133
134/// Namespace for the `Snapshot0` version.
135const SNAPSHOT0_NAMESPACE: &str = "wasi_unstable";
136
137/// Namespace for the `Snapshot1` version.
138const SNAPSHOT1_NAMESPACE: &str = "wasi_snapshot_preview1";
139
140/// Namespace for the `wasix` version.
141const WASIX_32V1_NAMESPACE: &str = "wasix_32v1";
142
143/// Namespace for the `wasix` version.
144const WASIX_64V1_NAMESPACE: &str = "wasix_64v1";
145
146/// Namespace for the `wasix` version.
147const WASIX_HTTP_V1_NAMESPACE: &str = "wasix_http_client_v1";
148
149/// Detect the version of WASI being used based on the import
150/// namespaces.
151///
152/// A strict detection expects that all imports live in a single WASI
153/// namespace. A non-strict detection expects that at least one WASI
154/// namespace exists to detect the version. Note that the strict
155/// detection is faster than the non-strict one.
156pub fn get_wasi_version(module: &Module, strict: bool) -> Option<WasiVersion> {
157    let mut imports = module.imports().functions().map(|f| f.module().to_owned());
158
159    if strict {
160        let first_module = imports.next()?;
161        if imports.all(|module| module == first_module) {
162            match first_module.as_str() {
163                SNAPSHOT0_NAMESPACE => Some(WasiVersion::Snapshot0),
164                SNAPSHOT1_NAMESPACE => Some(WasiVersion::Snapshot1),
165                WASIX_32V1_NAMESPACE => Some(WasiVersion::Wasix32v1),
166                WASIX_64V1_NAMESPACE => Some(WasiVersion::Wasix64v1),
167                _ => None,
168            }
169        } else {
170            None
171        }
172    } else {
173        // Check that at least a WASI namespace exists, and use the
174        // first one in the list to detect the WASI version.
175        imports.find_map(|module| match module.as_str() {
176            SNAPSHOT0_NAMESPACE => Some(WasiVersion::Snapshot0),
177            SNAPSHOT1_NAMESPACE => Some(WasiVersion::Snapshot1),
178            WASIX_32V1_NAMESPACE => Some(WasiVersion::Wasix32v1),
179            WASIX_64V1_NAMESPACE => Some(WasiVersion::Wasix64v1),
180            _ => None,
181        })
182    }
183}
184
185/// Like [`get_wasi_version`] but detects multiple WASI versions in a single module.
186/// Thus `strict` behaves differently in this function as multiple versions are
187/// always supported. `strict` indicates whether non-WASI imports should trigger a
188/// failure or be ignored.
189pub fn get_wasi_versions(module: &Module, strict: bool) -> Option<BTreeSet<WasiVersion>> {
190    let mut out = BTreeSet::new();
191    let imports = module.imports().functions().map(|f| f.module().to_owned());
192
193    let mut non_wasi_seen = false;
194    for ns in imports {
195        match ns.as_str() {
196            SNAPSHOT0_NAMESPACE => {
197                out.insert(WasiVersion::Snapshot0);
198            }
199            SNAPSHOT1_NAMESPACE => {
200                out.insert(WasiVersion::Snapshot1);
201            }
202            WASIX_32V1_NAMESPACE => {
203                out.insert(WasiVersion::Wasix32v1);
204            }
205            WASIX_64V1_NAMESPACE => {
206                out.insert(WasiVersion::Wasix64v1);
207            }
208            WASIX_HTTP_V1_NAMESPACE => {
209                out.insert(WasiVersion::Wasix64v1);
210            }
211            _ => {
212                non_wasi_seen = true;
213            }
214        }
215    }
216    if strict && non_wasi_seen {
217        None
218    } else {
219        Some(out)
220    }
221}
222
223#[cfg(test)]
224mod test {
225    use super::*;
226
227    #[test]
228    fn wasi_version_equality() {
229        assert_eq!(WasiVersion::Snapshot0, WasiVersion::Snapshot0);
230        assert_eq!(WasiVersion::Wasix64v1, WasiVersion::Wasix64v1);
231        assert_eq!(WasiVersion::Wasix32v1, WasiVersion::Wasix32v1);
232        assert_eq!(WasiVersion::Snapshot1, WasiVersion::Snapshot1);
233        assert_eq!(WasiVersion::Snapshot1, WasiVersion::Latest);
234        assert_eq!(WasiVersion::Latest, WasiVersion::Snapshot1);
235        assert_eq!(WasiVersion::Latest, WasiVersion::Latest);
236        assert!(WasiVersion::Wasix32v1 != WasiVersion::Wasix64v1);
237        assert!(WasiVersion::Wasix64v1 != WasiVersion::Wasix32v1);
238        assert!(WasiVersion::Snapshot1 != WasiVersion::Wasix64v1);
239        assert!(WasiVersion::Wasix64v1 != WasiVersion::Snapshot1);
240        assert!(WasiVersion::Snapshot1 != WasiVersion::Wasix32v1);
241        assert!(WasiVersion::Wasix32v1 != WasiVersion::Snapshot1);
242        assert!(WasiVersion::Snapshot0 != WasiVersion::Snapshot1);
243        assert!(WasiVersion::Snapshot1 != WasiVersion::Snapshot0);
244        assert!(WasiVersion::Snapshot0 != WasiVersion::Latest);
245        assert!(WasiVersion::Latest != WasiVersion::Snapshot0);
246        assert!(WasiVersion::Snapshot0 != WasiVersion::Latest);
247        assert!(WasiVersion::Latest != WasiVersion::Snapshot0);
248        assert!(WasiVersion::Wasix32v1 != WasiVersion::Latest);
249        assert!(WasiVersion::Wasix64v1 != WasiVersion::Latest);
250    }
251
252    #[test]
253    fn wasi_version_ordering() {
254        assert!(WasiVersion::Snapshot0 <= WasiVersion::Snapshot0);
255        assert!(WasiVersion::Snapshot1 <= WasiVersion::Snapshot1);
256        assert!(WasiVersion::Wasix32v1 <= WasiVersion::Wasix32v1);
257        assert!(WasiVersion::Wasix64v1 <= WasiVersion::Wasix64v1);
258        assert!(WasiVersion::Latest <= WasiVersion::Latest);
259        assert!(WasiVersion::Snapshot0 >= WasiVersion::Snapshot0);
260        assert!(WasiVersion::Snapshot1 >= WasiVersion::Snapshot1);
261        assert!(WasiVersion::Wasix32v1 >= WasiVersion::Wasix32v1);
262        assert!(WasiVersion::Wasix64v1 >= WasiVersion::Wasix64v1);
263        assert!(WasiVersion::Latest >= WasiVersion::Latest);
264
265        assert!(WasiVersion::Snapshot0 < WasiVersion::Latest);
266        assert!(WasiVersion::Snapshot0 < WasiVersion::Wasix32v1);
267        assert!(WasiVersion::Snapshot0 < WasiVersion::Wasix64v1);
268        assert!(WasiVersion::Snapshot0 < WasiVersion::Snapshot1);
269        assert!(WasiVersion::Latest > WasiVersion::Snapshot0);
270        assert!(WasiVersion::Wasix32v1 > WasiVersion::Snapshot0);
271        assert!(WasiVersion::Wasix64v1 > WasiVersion::Snapshot0);
272        assert!(WasiVersion::Snapshot1 > WasiVersion::Snapshot0);
273
274        assert!(WasiVersion::Snapshot1 < WasiVersion::Wasix32v1);
275        assert!(WasiVersion::Snapshot1 < WasiVersion::Wasix64v1);
276        assert!(WasiVersion::Wasix32v1 > WasiVersion::Snapshot1);
277        assert!(WasiVersion::Wasix64v1 > WasiVersion::Snapshot1);
278
279        assert!(WasiVersion::Wasix32v1 < WasiVersion::Latest);
280        assert!(WasiVersion::Wasix32v1 > WasiVersion::Snapshot1);
281        assert!(WasiVersion::Wasix64v1 < WasiVersion::Latest);
282        assert!(WasiVersion::Wasix64v1 > WasiVersion::Snapshot1);
283        assert!(WasiVersion::Latest > WasiVersion::Wasix32v1);
284        assert!(WasiVersion::Snapshot1 < WasiVersion::Wasix32v1);
285        assert!(WasiVersion::Latest > WasiVersion::Wasix64v1);
286        assert!(WasiVersion::Snapshot1 < WasiVersion::Wasix32v1);
287
288        assert!(WasiVersion::Wasix32v1 < WasiVersion::Wasix64v1);
289        assert!(WasiVersion::Wasix64v1 > WasiVersion::Wasix32v1);
290    }
291}