Coverage Report

Created: 2023-04-25 07:07

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