Skip to content

Commit c5ce6f9

Browse files
committed
factors: Fix wasi-libc#377 bug detection
Signed-off-by: Lann Martin <[email protected]>
1 parent eddff50 commit c5ce6f9

File tree

3 files changed

+45
-33
lines changed

3 files changed

+45
-33
lines changed

crates/componentize/src/bugs.rs

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,19 @@ use crate::module_info::ModuleInfo;
22

33
pub const EARLIEST_PROBABLY_SAFE_CLANG_VERSION: &str = "15.0.7";
44

5-
/// Represents the detected likelihood of the allocation bug fixed in
6-
/// https://github.com/WebAssembly/wasi-libc/pull/377 being present in a Wasm
7-
/// module.
5+
/// This error represents the likely presence of the allocation bug fixed in
6+
/// https://github.com/WebAssembly/wasi-libc/pull/377 in a Wasm module.
87
#[derive(Debug, PartialEq)]
9-
pub enum WasiLibc377Bug {
10-
ProbablySafe,
11-
ProbablyUnsafe { clang_version: String },
12-
Unknown,
8+
pub struct WasiLibc377Bug {
9+
clang_version: Option<String>,
1310
}
1411

1512
impl WasiLibc377Bug {
16-
pub fn detect(module_info: &ModuleInfo) -> anyhow::Result<Self> {
13+
/// Detects the likely presence of this bug.
14+
pub fn check(module_info: &ModuleInfo) -> Result<(), Self> {
1715
if module_info.probably_uses_wit_bindgen() {
1816
// Modules built with wit-bindgen are probably safe.
19-
return Ok(Self::ProbablySafe);
17+
return Ok(());
2018
}
2119
if let Some(clang_version) = &module_info.clang_version {
2220
// Clang/LLVM version is a good proxy for wasi-sdk
@@ -25,12 +23,12 @@ impl WasiLibc377Bug {
2523
if let Some((major, minor, patch)) = parse_clang_version(clang_version) {
2624
let earliest_safe =
2725
parse_clang_version(EARLIEST_PROBABLY_SAFE_CLANG_VERSION).unwrap();
28-
return if (major, minor, patch) >= earliest_safe {
29-
Ok(Self::ProbablySafe)
26+
if (major, minor, patch) >= earliest_safe {
27+
return Ok(());
3028
} else {
31-
Ok(Self::ProbablyUnsafe {
32-
clang_version: clang_version.clone(),
33-
})
29+
return Err(Self {
30+
clang_version: Some(clang_version.clone()),
31+
});
3432
};
3533
} else {
3634
tracing::warn!(
@@ -39,10 +37,27 @@ impl WasiLibc377Bug {
3937
);
4038
}
4139
}
42-
Ok(Self::Unknown)
40+
// If we can't assert that the module uses wit-bindgen OR was compiled
41+
// with a new-enough wasi-sdk, conservatively assume it may be buggy.
42+
Err(Self {
43+
clang_version: None,
44+
})
4345
}
4446
}
4547

48+
impl std::fmt::Display for WasiLibc377Bug {
49+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50+
write!(
51+
f,
52+
"This Wasm module may have been compiled with wasi-sdk version <19 which \
53+
contains a critical memory safety bug. For more information, see: \
54+
https://github.com/fermyon/spin/issues/2552"
55+
)
56+
}
57+
}
58+
59+
impl std::error::Error for WasiLibc377Bug {}
60+
4661
fn parse_clang_version(ver: &str) -> Option<(u16, u16, u16)> {
4762
// Strip optional trailing detail after space
4863
let ver = ver.split(' ').next().unwrap();
@@ -59,47 +74,42 @@ mod tests {
5974

6075
#[test]
6176
fn wasi_libc_377_detect() {
62-
use WasiLibc377Bug::*;
63-
for (wasm, expected) in [
64-
(r#"(module)"#, Unknown),
77+
for (wasm, safe) in [
78+
(r#"(module)"#, false),
6579
(
6680
r#"(module (func (export "cabi_realloc") (unreachable)))"#,
67-
ProbablySafe,
81+
true,
6882
),
6983
(
7084
r#"(module (func (export "some_other_function") (unreachable)))"#,
71-
Unknown,
85+
false,
7286
),
7387
(
7488
r#"(module (@producers (processed-by "clang" "16.0.0 extra-stuff")))"#,
75-
ProbablySafe,
89+
true,
7690
),
7791
(
7892
r#"(module (@producers (processed-by "clang" "15.0.7")))"#,
79-
ProbablySafe,
93+
true,
8094
),
8195
(
8296
r#"(module (@producers (processed-by "clang" "15.0.6")))"#,
83-
ProbablyUnsafe {
84-
clang_version: "15.0.6".into(),
85-
},
97+
false,
8698
),
8799
(
88100
r#"(module (@producers (processed-by "clang" "14.0.0 extra-stuff")))"#,
89-
ProbablyUnsafe {
90-
clang_version: "14.0.0 extra-stuff".into(),
91-
},
101+
false,
92102
),
93103
(
94104
r#"(module (@producers (processed-by "clang" "a.b.c")))"#,
95-
Unknown,
105+
false,
96106
),
97107
] {
98108
eprintln!("WAT: {wasm}");
99109
let module = wat::parse_str(wasm).unwrap();
100110
let module_info = ModuleInfo::from_module(&module).unwrap();
101-
let detected = WasiLibc377Bug::detect(&module_info).unwrap();
102-
assert_eq!(detected, expected);
111+
let detected = WasiLibc377Bug::check(&module_info);
112+
assert!(detected.is_ok() == safe, "{wasm} -> {detected:?}");
103113
}
104114
}
105115
}

crates/componentize/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ pub fn componentize_old_module(module: &[u8], module_info: &ModuleInfo) -> Resul
115115
// If the module has a _start export and doesn't obviously use wit-bindgen
116116
// it is likely an old p1 command module.
117117
if module_info.has_start_export && !module_info.probably_uses_wit_bindgen() {
118+
bugs::WasiLibc377Bug::check(module_info)?;
118119
componentize_command(module)
119120
} else {
120121
componentize_old_bindgen(module)

crates/trigger/src/cli.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -357,9 +357,10 @@ impl<T: Trigger> TriggerAppBuilder<T> {
357357
quoted_path(&path)
358358
)
359359
})?;
360-
let component = spin_componentize::componentize_if_necessary(&bytes)?;
360+
let component = spin_componentize::componentize_if_necessary(&bytes)
361+
.with_context(|| format!("preparing wasm {}", quoted_path(&path)))?;
361362
spin_core::Component::new(engine, component)
362-
.with_context(|| format!("loading module {}", quoted_path(&path)))
363+
.with_context(|| format!("compiling wasm {}", quoted_path(&path)))
363364
}
364365
}
365366

0 commit comments

Comments
 (0)