Coverage Report

Created: 2025-10-12 07:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasm-tools/fuzz/src/roundtrip.rs
Line
Count
Source
1
use std::str;
2
3
2.34k
pub fn run(string: &str) {
4
2.34k
    write_file("wasm1.wat", &string);
5
    // Weed out `(module binary ...)` because when we print the bytes and
6
    // convert it back to binary it's not guaranteed to be exactly the same.
7
    // (think of something like an over-long LEB encoding)
8
2.34k
    if string.contains("binary") {
9
181
        return;
10
2.16k
    }
11
12
    // Also weed out `@custom` custom sections since we don't print those right
13
    // now.
14
2.16k
    if string.contains("@custom") {
15
109
        return;
16
2.05k
    }
17
2.05k
    let wasm = match wat::parse_str(string) {
18
7
        Ok(bytes) => bytes,
19
2.04k
        Err(_) => return,
20
    };
21
7
    write_file("wasm1.wasm", &wasm);
22
23
    // Only roundtrip valid modules for now since invalid modules can often have
24
    // bizarre structures which aren't intended to print correctly or roundtrip
25
    // well.
26
7
    if wasmparser::validate(&wasm).is_err() {
27
1
        return;
28
6
    }
29
30
    // And finally validate that the name section, if present, is valid. This
31
    // can be invalid if names in the name section are too long (e.g. exceeding
32
    // the maximum length of a string). The printing process will skip invalid
33
    // name sections, so if it's invalid then our roundtrip'd bytes will
34
    // trivially not match, but not in an interesting way.
35
6
    if validate_name_section(&wasm).is_err() {
36
0
        return;
37
6
    }
38
6
    let string2 = match wasmprinter::print_bytes(&wasm) {
39
6
        Ok(s) => s,
40
0
        Err(_) => return,
41
    };
42
6
    write_file("wasm2.wat", &string2);
43
44
6
    let wasm2 = wat::parse_str(&string2).unwrap();
45
6
    write_file("wasm2.wasm", &wasm2);
46
6
    if wasm == wasm2 {
47
6
        return;
48
0
    }
49
50
0
    panic!("wasm bytes differ on roundtrip");
51
2.34k
}
52
53
2.36k
fn write_file(path: &str, contents: impl AsRef<[u8]>) {
54
2.36k
    if !log::log_enabled!(log::Level::Debug) {
55
2.36k
        return;
56
0
    }
57
0
    log::debug!("writing file {path}");
58
0
    std::fs::write(path, contents.as_ref()).unwrap();
59
2.36k
}
wasm_tools_fuzz::roundtrip::write_file::<&alloc::vec::Vec<u8>>
Line
Count
Source
53
13
fn write_file(path: &str, contents: impl AsRef<[u8]>) {
54
13
    if !log::log_enabled!(log::Level::Debug) {
55
13
        return;
56
0
    }
57
0
    log::debug!("writing file {path}");
58
0
    std::fs::write(path, contents.as_ref()).unwrap();
59
13
}
wasm_tools_fuzz::roundtrip::write_file::<&alloc::string::String>
Line
Count
Source
53
6
fn write_file(path: &str, contents: impl AsRef<[u8]>) {
54
6
    if !log::log_enabled!(log::Level::Debug) {
55
6
        return;
56
0
    }
57
0
    log::debug!("writing file {path}");
58
0
    std::fs::write(path, contents.as_ref()).unwrap();
59
6
}
wasm_tools_fuzz::roundtrip::write_file::<&&str>
Line
Count
Source
53
2.34k
fn write_file(path: &str, contents: impl AsRef<[u8]>) {
54
2.34k
    if !log::log_enabled!(log::Level::Debug) {
55
2.34k
        return;
56
0
    }
57
0
    log::debug!("writing file {path}");
58
0
    std::fs::write(path, contents.as_ref()).unwrap();
59
2.34k
}
60
61
6
fn validate_name_section(wasm: &[u8]) -> wasmparser::Result<()> {
62
    use wasmparser::*;
63
17
    for payload in Parser::new(0).parse_all(wasm) {
64
17
        let reader = match payload? {
65
0
            Payload::CustomSection(c) => match c.as_known() {
66
0
                KnownCustom::Name(name) => name,
67
0
                _ => continue,
68
            },
69
17
            _ => continue,
70
        };
71
0
        for section in reader {
72
0
            match section? {
73
0
                Name::Module { .. } => {}
74
0
                Name::Function(n)
75
0
                | Name::Type(n)
76
0
                | Name::Table(n)
77
0
                | Name::Memory(n)
78
0
                | Name::Global(n)
79
0
                | Name::Element(n)
80
0
                | Name::Data(n)
81
0
                | Name::Tag(n) => {
82
0
                    for name in n {
83
0
                        name?;
84
                    }
85
                }
86
0
                Name::Local(n) | Name::Label(n) | Name::Field(n) => {
87
0
                    for name in n {
88
0
                        for name in name?.names {
89
0
                            name?;
90
                        }
91
                    }
92
                }
93
0
                Name::Unknown { .. } => {}
94
            }
95
        }
96
    }
97
6
    Ok(())
98
6
}