/src/wasm-tools/fuzz/fuzz_targets/roundtrip-wit.rs
Line | Count | Source (jump to first uncovered line) |
1 | | #![no_main] |
2 | | |
3 | | use libfuzzer_sys::fuzz_target; |
4 | | use std::collections::HashMap; |
5 | | use std::path::Path; |
6 | | use wasm_encoder::{CustomSection, Encode, Section}; |
7 | | use wit_component::*; |
8 | | use wit_parser::{Resolve, SourceMap}; |
9 | | |
10 | | fuzz_target!(|data: &[u8]| { |
11 | | drop(env_logger::try_init()); |
12 | | |
13 | | let mut u = arbitrary::Unstructured::new(data); |
14 | 6.58k | let wasm = match u.arbitrary().and_then(|config| { |
15 | 6.58k | log::debug!("config: {config:#?}"); |
16 | 6.58k | wit_smith::smith(&config, &mut u) |
17 | 6.58k | }) { |
18 | | Ok(wasm) => wasm, |
19 | | Err(_) => return, |
20 | | }; |
21 | | write_file("doc1.wasm", &wasm); |
22 | | let (resolve, _pkg) = match wit_component::decode("root", &wasm).unwrap() { |
23 | | DecodedWasm::WitPackage(resolve, pkg) => (resolve, pkg), |
24 | | DecodedWasm::Component(..) => unreachable!(), |
25 | | }; |
26 | | roundtrip_through_printing("doc1", &resolve, &wasm); |
27 | | |
28 | | let (resolve2, pkg2) = match wit_component::decode("root", &wasm).unwrap() { |
29 | | DecodedWasm::WitPackage(resolve, pkg) => (resolve, pkg), |
30 | | DecodedWasm::Component(..) => unreachable!(), |
31 | | }; |
32 | | |
33 | | let wasm2 = wit_component::encode(&resolve2, pkg2).expect("failed to encode WIT document"); |
34 | | write_file("doc2.wasm", &wasm2); |
35 | | roundtrip_through_printing("doc2", &resolve2, &wasm2); |
36 | | |
37 | | if wasm != wasm2 { |
38 | | panic!("roundtrip wasm didn't match"); |
39 | | } |
40 | | |
41 | | // If there's hundreds or thousands of worlds only work with the first few |
42 | | // to avoid timing out this fuzzer with asan enabled. |
43 | | for (id, _world) in resolve.worlds.iter().take(20) { |
44 | | let mut dummy = wit_component::dummy_module(&resolve, id); |
45 | | let metadata = |
46 | | wit_component::metadata::encode(&resolve, id, StringEncoding::UTF8, None).unwrap(); |
47 | | let section = CustomSection { |
48 | | name: "component-type", |
49 | | data: &metadata, |
50 | | }; |
51 | | dummy.push(section.id()); |
52 | | section.encode(&mut dummy); |
53 | | |
54 | | write_file("dummy.wasm", &dummy); |
55 | | let wasm = wit_component::ComponentEncoder::default() |
56 | | .module(&dummy) |
57 | | .unwrap() |
58 | | .encode() |
59 | | .unwrap(); |
60 | | write_file("dummy.component.wasm", &wasm); |
61 | | wasmparser::Validator::new_with_features(wasmparser::WasmFeatures { |
62 | | component_model: true, |
63 | | ..Default::default() |
64 | | }) |
65 | | .validate_all(&wasm) |
66 | | .unwrap(); |
67 | | |
68 | | wit_component::decode("root", &wasm).unwrap(); |
69 | | } |
70 | | }); |
71 | | |
72 | 12.5k | fn roundtrip_through_printing(file: &str, resolve: &Resolve, wasm: &[u8]) { |
73 | 12.5k | // For all packages in `resolve` print them all to a string, then re-parse |
74 | 12.5k | // them and insert them into a `new_resolve`. |
75 | 12.5k | let mut new_deps = HashMap::new(); |
76 | 12.5k | let mut new_resolve = Resolve::default(); |
77 | 12.5k | let mut last = None; |
78 | 18.0k | for (_, pkg) in resolve.packages.iter() { |
79 | 18.0k | let mut map = SourceMap::new(); |
80 | 18.0k | let pkg_name = &pkg.name; |
81 | 46.7k | for (name, doc) in pkg.documents.iter() { |
82 | 46.7k | let doc = DocumentPrinter::default().print(resolve, *doc).unwrap(); |
83 | 46.7k | write_file(&format!("{file}-{pkg_name}-{name}.wit"), &doc); |
84 | 46.7k | map.push(format!("{name}.wit").as_ref(), &name, doc); |
85 | 46.7k | } |
86 | 18.0k | let unresolved = map.parse(&pkg.name, pkg.url.as_deref()).unwrap(); |
87 | 18.0k | let id = new_resolve.push(unresolved, &new_deps).unwrap(); |
88 | 18.0k | new_deps.insert(pkg.name.clone(), id); |
89 | 18.0k | last = Some(id); |
90 | | } |
91 | | |
92 | | // Finally encode the `new_resolve` which should be the exact same as |
93 | | // before. |
94 | 12.5k | let wasm2 = wit_component::encode(&new_resolve, last.unwrap()).unwrap(); |
95 | 12.5k | write_file(&format!("{file}-reencoded.wasm"), &wasm2); |
96 | 12.5k | if wasm != wasm2 { |
97 | 0 | panic!("failed to roundtrip through text printing"); |
98 | 12.5k | } |
99 | 12.5k | } |
100 | | |
101 | 146k | fn write_file(path: &str, contents: impl AsRef<[u8]>) { |
102 | 146k | if !log::log_enabled!(log::Level::Debug) { |
103 | 146k | return; |
104 | 0 | } |
105 | 0 | log::debug!("writing file {path}"); |
106 | 0 | let contents = contents.as_ref(); |
107 | 0 | let path = Path::new(path); |
108 | 0 | std::fs::write(path, contents).unwrap(); |
109 | 0 | if path.extension().and_then(|s| s.to_str()) == Some("wasm") {Unexecuted instantiation: roundtrip_wit::write_file::<&alloc::vec::Vec<u8>>::{closure#0}Unexecuted instantiation: roundtrip_wit::write_file::<&alloc::string::String>::{closure#0} |
110 | 0 | let path = path.with_extension("wat"); |
111 | 0 | log::debug!("writing file {}", path.display()); |
112 | 0 | std::fs::write(path, wasmprinter::print_bytes(&contents).unwrap()).unwrap(); |
113 | 0 | } |
114 | 146k | } roundtrip_wit::write_file::<&alloc::vec::Vec<u8>> Line | Count | Source | 101 | 100k | fn write_file(path: &str, contents: impl AsRef<[u8]>) { | 102 | 100k | if !log::log_enabled!(log::Level::Debug) { | 103 | 100k | return; | 104 | 0 | } | 105 | 0 | log::debug!("writing file {path}"); | 106 | 0 | let contents = contents.as_ref(); | 107 | 0 | let path = Path::new(path); | 108 | 0 | std::fs::write(path, contents).unwrap(); | 109 | 0 | if path.extension().and_then(|s| s.to_str()) == Some("wasm") { | 110 | 0 | let path = path.with_extension("wat"); | 111 | 0 | log::debug!("writing file {}", path.display()); | 112 | 0 | std::fs::write(path, wasmprinter::print_bytes(&contents).unwrap()).unwrap(); | 113 | 0 | } | 114 | 100k | } |
roundtrip_wit::write_file::<&alloc::string::String> Line | Count | Source | 101 | 46.7k | fn write_file(path: &str, contents: impl AsRef<[u8]>) { | 102 | 46.7k | if !log::log_enabled!(log::Level::Debug) { | 103 | 46.7k | return; | 104 | 0 | } | 105 | 0 | log::debug!("writing file {path}"); | 106 | 0 | let contents = contents.as_ref(); | 107 | 0 | let path = Path::new(path); | 108 | 0 | std::fs::write(path, contents).unwrap(); | 109 | 0 | if path.extension().and_then(|s| s.to_str()) == Some("wasm") { | 110 | 0 | let path = path.with_extension("wat"); | 111 | 0 | log::debug!("writing file {}", path.display()); | 112 | 0 | std::fs::write(path, wasmprinter::print_bytes(&contents).unwrap()).unwrap(); | 113 | 0 | } | 114 | 46.7k | } |
|