spin_componentize/
bugs.rs1use crate::module_info::ModuleInfo;
2
3pub const EARLIEST_PROBABLY_SAFE_CLANG_VERSION: &str = "15.0.7";
4
5#[derive(Debug, PartialEq)]
8pub struct WasiLibc377Bug {
9 clang_version: Option<String>,
10}
11
12impl WasiLibc377Bug {
13 pub fn check(module_info: &ModuleInfo) -> Result<(), Self> {
15 if module_info.probably_uses_wit_bindgen() {
16 return Ok(());
18 }
19 if let Some(clang_version) = &module_info.clang_version {
20 if let Some((major, minor, patch)) = parse_clang_version(clang_version) {
24 let earliest_safe =
25 parse_clang_version(EARLIEST_PROBABLY_SAFE_CLANG_VERSION).unwrap();
26 if (major, minor, patch) < earliest_safe {
27 return Err(Self {
28 clang_version: Some(clang_version.clone()),
29 });
30 };
31 } else {
32 tracing::warn!(
33 clang_version,
34 "Unexpected producers.processed-by.clang version"
35 );
36 }
37 }
38 Ok(())
39 }
40}
41
42impl std::fmt::Display for WasiLibc377Bug {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 write!(
45 f,
46 "This Wasm module appears to have been compiled with wasi-sdk version <19 \
47 which contains a critical memory safety bug. For more information, see: \
48 https://github.com/spinframework/spin/issues/2552"
49 )
50 }
51}
52
53impl std::error::Error for WasiLibc377Bug {}
54
55fn parse_clang_version(ver: &str) -> Option<(u16, u16, u16)> {
56 let ver = ver.split(' ').next().unwrap();
58 let ver = ver.strip_suffix("-wasi-sdk").unwrap_or(ver);
60 let mut parts = ver.split('.');
61 let major = parts.next()?.parse().ok()?;
62 let minor = parts.next()?.parse().ok()?;
63 let patch = parts.next()?.parse().ok()?;
64 Some((major, minor, patch))
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70
71 #[test]
72 fn wasi_libc_377_detect() {
73 for (wasm, safe) in [
74 (r#"(module)"#, true),
75 (
76 r#"(module (func (export "cabi_realloc") (unreachable)))"#,
77 true,
78 ),
79 (
80 r#"(module (@producers (processed-by "clang" "16.0.0 extra-stuff")))"#,
81 true,
82 ),
83 (
84 r#"(module (@producers (processed-by "clang" "15.0.7")))"#,
85 true,
86 ),
87 (
88 r#"(module (@producers (processed-by "clang" "18.1.2-wasi-sdk (https://github.com/llvm/llvm-project 26a1d6601d727a96f4301d0d8647b5a42760ae0c)")))"#,
89 true,
90 ),
91 (
92 r#"(module (@producers (processed-by "clang" "15.0.6")))"#,
93 false,
94 ),
95 (
96 r#"(module (@producers (processed-by "clang" "14.0.0 extra-stuff")))"#,
97 false,
98 ),
99 ] {
100 eprintln!("WAT: {wasm}");
101 let module = wat::parse_str(wasm).unwrap();
102 let module_info = ModuleInfo::from_module(&module).unwrap();
103 let detected = WasiLibc377Bug::check(&module_info);
104 assert!(detected.is_ok() == safe, "{wasm} -> {detected:?}");
105 }
106 }
107}