@@ -2,21 +2,19 @@ use crate::module_info::ModuleInfo;
22
33pub 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
1512impl 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+
4661fn 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}
0 commit comments