Skip to content

Commit 97c4f04

Browse files
authored
Account for invalid names in roundtrip fuzzing (#237)
This fixes a bug in the roundtrip fuzzing target where an input `*.wat` file can produce a wasm file with an "invalid" name section because the name might be too large. This means that the roundtrip-ing of that file would not preserve the name section, causing the previous assertion to fail. The fuzzer now validates that the name section, if present, is valid before proceeding with fuzzing.
1 parent 763e3d8 commit 97c4f04

2 files changed

Lines changed: 65 additions & 4 deletions

File tree

crates/wasmparser/src/binary_reader.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -680,7 +680,7 @@ impl<'a> BinaryReader<'a> {
680680
let len = self.read_var_u32()? as usize;
681681
if len > MAX_WASM_STRING_SIZE {
682682
return Err(BinaryReaderError::new(
683-
"string size in out of bounds",
683+
"string size out of bounds",
684684
self.original_position() - 1,
685685
));
686686
}
@@ -829,7 +829,7 @@ impl<'a> BinaryReader<'a> {
829829
let len = self.read_var_u32()? as usize;
830830
if len > MAX_WASM_STRING_SIZE {
831831
return Err(BinaryReaderError::new(
832-
"string size in out of bounds",
832+
"string size out of bounds",
833833
self.original_position() - 1,
834834
));
835835
}

fuzz/fuzz_targets/roundtrip.rs

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,73 @@ fuzz_target!(|data: &[u8]| {
2424
Ok(bytes) => bytes,
2525
Err(_) => return,
2626
};
27+
28+
// Only roundtrip valid modules for now since invalid modules can often have
29+
// bizarre structures which aren't intended to print correctly or roundtrip
30+
// well.
2731
if wasmparser::validate(&wasm).is_err() {
2832
return;
2933
}
30-
let string = match wasmprinter::print_bytes(&wasm) {
34+
35+
// And finally validate that the name section, if present, is valid. This
36+
// can be invalid if names in the name section are too long (e.g. exceeding
37+
// the maximum length of a string). The printing process will skip invalid
38+
// name sections, so if it's invalid then our roundtrip'd bytes will
39+
// trivially not match, but not in an interesting way.
40+
if validate_name_section(&wasm).is_err() {
41+
return;
42+
}
43+
let string2 = match wasmprinter::print_bytes(&wasm) {
3144
Ok(s) => s,
3245
Err(_) => return,
3346
};
34-
assert_eq!(wasm, wat::parse_str(&string).unwrap());
47+
48+
let wasm2 = wat::parse_str(&string2).unwrap();
49+
if wasm == wasm2 {
50+
return;
51+
}
52+
53+
std::fs::write("wasm1.wasm", &wasm).unwrap();
54+
std::fs::write("wasm1.wat", &string).unwrap();
55+
std::fs::write("wasm2.wasm", &wasm2).unwrap();
56+
std::fs::write("wasm2.wat", &string2).unwrap();
57+
panic!("wasm bytes differ on roundtrip");
3558
});
59+
60+
fn validate_name_section(wasm: &[u8]) -> wasmparser::Result<()> {
61+
use wasmparser::*;
62+
for payload in Parser::new(0).parse_all(wasm) {
63+
let reader = match payload? {
64+
Payload::CustomSection {
65+
name: "name",
66+
data_offset,
67+
data,
68+
} => NameSectionReader::new(data, data_offset)?,
69+
_ => continue,
70+
};
71+
for section in reader {
72+
match section? {
73+
Name::Module(n) => {
74+
n.get_name()?;
75+
}
76+
Name::Function(n) => {
77+
let mut map = n.get_map()?;
78+
for _ in 0..map.get_count() {
79+
map.read()?;
80+
}
81+
}
82+
Name::Local(n) => {
83+
let mut reader = n.get_function_local_reader()?;
84+
for _ in 0..reader.get_count() {
85+
let local_name = reader.read()?;
86+
let mut map = local_name.get_map()?;
87+
for _ in 0..map.get_count() {
88+
map.read()?;
89+
}
90+
}
91+
}
92+
}
93+
}
94+
}
95+
Ok(())
96+
}

0 commit comments

Comments
 (0)