Skip to content

Commit

Permalink
Merge pull request #554 from fitzgen/mutate-custom-sections
Browse files Browse the repository at this point in the history
`wasm-mutate`: Add a mutator for custom sections
  • Loading branch information
fitzgen authored Apr 14, 2022
2 parents d2f7059 + aec1b53 commit 519d152
Show file tree
Hide file tree
Showing 14 changed files with 314 additions and 148 deletions.
21 changes: 8 additions & 13 deletions crates/dump/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,18 +228,13 @@ impl<'a> Dump<'a> {
Payload::ComponentStartSection { .. } => todo!("component-model"),
Payload::AliasSection(_) => todo!("component-model"),

Payload::CustomSection {
name,
data_offset,
data,
range,
} => {
Payload::CustomSection(c) => {
write!(self.state, "custom section")?;
self.print(range.start)?;
write!(self.state, "name: {:?}", name)?;
self.print(data_offset)?;
if name == "name" {
let mut iter = NameSectionReader::new(data, data_offset)?;
self.print(c.range().start)?;
write!(self.state, "name: {:?}", c.name())?;
self.print(c.data_offset())?;
if c.name() == "name" {
let mut iter = NameSectionReader::new(c.data(), c.data_offset())?;
while !iter.eof() {
self.print_custom_name_section(iter.read()?, iter.original_position())?;
}
Expand All @@ -248,8 +243,8 @@ impl<'a> Dump<'a> {
for _ in 0..NBYTES {
write!(self.dst, "---")?;
}
write!(self.dst, "-| ... {} bytes of data\n", data.len())?;
self.cur += data.len();
write!(self.dst, "-| ... {} bytes of data\n", c.data().len())?;
self.cur += c.data().len();
}
}
Payload::UnknownSection {
Expand Down
16 changes: 9 additions & 7 deletions crates/wasm-mutate/src/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,13 +200,8 @@ impl<'a> ModuleInfo<'a> {
info.data_segments_count = reader.get_count();
info.section(SectionId::Data.into(), reader.range(), input_wasm);
}
Payload::CustomSection {
name: _,
data_offset: _,
data: _,
range,
} => {
info.section(SectionId::Custom.into(), range, input_wasm);
Payload::CustomSection(c) => {
info.section(SectionId::Custom.into(), c.range(), input_wasm);
}
Payload::UnknownSection {
id,
Expand Down Expand Up @@ -246,6 +241,13 @@ impl<'a> ModuleInfo<'a> {
self.code != None
}

/// Does this module have any custom sections?
pub fn has_custom_section(&self) -> bool {
self.raw_sections
.iter()
.any(|s| s.id == SectionId::Custom as u8)
}

/// Registers a new raw_section in the ModuleInfo
pub fn section(&mut self, id: u8, range: wasmparser::Range, full_wasm: &'a [u8]) {
self.raw_sections.push(RawSection {
Expand Down
1 change: 1 addition & 0 deletions crates/wasm-mutate/src/mutators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
pub mod add_function;
pub mod add_type;
pub mod codemotion;
pub mod custom;
pub mod function_body_unreachable;
pub mod modify_data;
pub mod modify_init_exprs;
Expand Down
146 changes: 146 additions & 0 deletions crates/wasm-mutate/src/mutators/custom.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
//! Mutate custom sections.
use super::Mutator;
use rand::{seq::SliceRandom, Rng};

#[derive(Clone, Copy)]
pub struct CustomSectionMutator;

impl Mutator for CustomSectionMutator {
fn can_mutate(&self, config: &crate::WasmMutate) -> bool {
config.info().has_custom_section()
}

fn mutate<'a>(
self,
config: &'a mut crate::WasmMutate,
) -> crate::Result<Box<dyn Iterator<Item = crate::Result<wasm_encoder::Module>> + 'a>> {
let custom_section_indices: Vec<_> = config
.info()
.raw_sections
.iter()
.enumerate()
.filter(|(_i, s)| s.id == wasm_encoder::SectionId::Custom as u8)
.map(|(i, _s)| i)
.collect();
assert!(!custom_section_indices.is_empty());

let custom_section_index = *custom_section_indices.choose(config.rng()).unwrap();
let old_custom_section = &config.info().raw_sections[custom_section_index];
let old_custom_section =
wasmparser::CustomSectionReader::new(old_custom_section.data, 0).unwrap();

let name_string;
let data_vec;
let mut name = old_custom_section.name();
let mut data = old_custom_section.data();

if config.rng().gen_ratio(1, 20) {
// Mutate the custom section's name.
let mut new_name = name.to_string().into_bytes();
config.raw_mutate(
&mut new_name,
if config.reduce {
name.len().saturating_sub(1)
} else {
std::cmp::max(name.len() * 2, 32)
},
)?;
name_string = String::from_utf8_lossy(&new_name).to_string();
name = &name_string;
} else {
// Mutate the custom section's data.
let mut new_data = data.to_vec();
config.raw_mutate(
&mut new_data,
if config.reduce {
data.len().saturating_sub(1)
} else {
std::cmp::max(data.len() * 2, 32)
},
)?;
data_vec = new_data;
data = &data_vec;
};

Ok(Box::new(std::iter::once(Ok(config
.info()
.replace_section(
custom_section_index,
&wasm_encoder::CustomSection { name, data },
)))))
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_grow_custom_section() {
crate::mutators::match_mutation(
r#"
(module
(@custom "name" "data")
)
"#,
CustomSectionMutator,
r#"
(module
(@custom "name" "datadata")
)
"#,
);
}

#[test]
fn test_shrink_custom_section() {
crate::mutators::match_mutation(
r#"
(module
(@custom "name" "data")
)
"#,
CustomSectionMutator,
r#"
(module
(@custom "name" "d")
)
"#,
);
}

#[test]
fn test_mutate_custom_section() {
crate::mutators::match_mutation(
r#"
(module
(@custom "name" "data")
)
"#,
CustomSectionMutator,
r#"
(module
(@custom "name" "aaaa")
)
"#,
);
}

#[test]
fn test_mutate_custom_section_name() {
crate::mutators::match_mutation(
r#"
(module
(@custom "name" "data")
)
"#,
CustomSectionMutator,
r#"
(module
(@custom "n" "data")
)
"#,
);
}
}
6 changes: 1 addition & 5 deletions crates/wasm-mutate/src/mutators/remove_section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,7 @@ fn is_empty_section(section: &wasm_encoder::RawSection) -> bool {
impl Mutator for RemoveSection {
fn can_mutate(&self, config: &WasmMutate) -> bool {
match self {
&Self::Custom => config
.info()
.raw_sections
.iter()
.any(|s| s.id == wasm_encoder::SectionId::Custom as u8),
&Self::Custom => config.info().has_custom_section(),
&Self::Empty => config
.info()
.raw_sections
Expand Down
70 changes: 17 additions & 53 deletions crates/wasmparser/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ use crate::{
InstanceSectionReader,
};
use crate::{
BinaryReader, BinaryReaderError, DataSectionReader, ElementSectionReader, ExportSectionReader,
FunctionBody, FunctionSectionReader, GlobalSectionReader, ImportSectionReader,
MemorySectionReader, Range, Result, TableSectionReader, TagSectionReader, TypeSectionReader,
BinaryReader, BinaryReaderError, CustomSectionReader, DataSectionReader, ElementSectionReader,
ExportSectionReader, FunctionBody, FunctionSectionReader, GlobalSectionReader,
ImportSectionReader, MemorySectionReader, Range, Result, TableSectionReader, TagSectionReader,
TypeSectionReader,
};
use std::convert::TryInto;
use std::fmt;
Expand Down Expand Up @@ -247,19 +248,7 @@ pub enum Payload<'a> {
CodeSectionEntry(crate::FunctionBody<'a>),

/// A module or component custom section was received.
CustomSection {
/// The name of the custom section.
name: &'a str,
/// The offset, relative to the start of the original module or component,
/// that the `data` payload for this custom section starts at.
data_offset: usize,
/// The actual contents of the custom section.
data: &'a [u8],
/// The range of bytes that specify this whole custom section (including
/// both the name of this custom section and its data) specified in
/// offsets relative to the start of the byte stream.
range: Range,
},
CustomSection(crate::CustomSectionReader<'a>),

/// An unknown section was found.
///
Expand Down Expand Up @@ -411,7 +400,7 @@ impl Parser {
/// ComponentStartSection { .. } => { /* ... */ }
/// AliasSection(_) => { /* ... */ }
///
/// CustomSection { name, .. } => { /* ... */ }
/// CustomSection(_) => { /* ... */ }
///
/// // most likely you'd return an error here
/// UnknownSection { id, .. } => { /* ... */ }
Expand Down Expand Up @@ -538,26 +527,12 @@ impl Parser {
}

// Check for custom sections (supported by all encodings)
if id == 0 {
let start = reader.original_position();
let range = Range {
start,
end: reader.original_position() + len as usize,
};
let mut content = subreader(reader, len)?;
// Note that if this fails we can't read any more bytes,
// so clear the "we'd succeed if we got this many more
// bytes" because we can't recover from "eof" at this point.
let name = content.read_string().map_err(clear_hint)?;
return Ok(Payload::CustomSection {
name,
data_offset: content.original_position(),
data: content.remaining_buffer(),
range,
});
}
if id == 0 {}

match (self.encoding, id) {
// Sections for both modules and components.
(_, 0) => section(reader, len, CustomSectionReader::new, CustomSection),

// Module sections
(Encoding::Module, 1) => {
section(reader, len, TypeSectionReader::new, TypeSection)
Expand Down Expand Up @@ -1015,18 +990,7 @@ impl fmt::Debug for Payload<'_> {
.finish(),
AliasSection(_) => f.debug_tuple("AliasSection").field(&"...").finish(),

CustomSection {
name,
data_offset,
data: _,
range,
} => f
.debug_struct("CustomSection")
.field("name", name)
.field("data_offset", data_offset)
.field("range", range)
.field("data", &"...")
.finish(),
CustomSection(c) => f.debug_tuple("CustomSection").field(c).finish(),

UnknownSection { id, range, .. } => f
.debug_struct("UnknownSection")
Expand Down Expand Up @@ -1206,36 +1170,36 @@ mod tests {
parser_after_header().parse(&[0, 1, 0], false),
Ok(Chunk::Parsed {
consumed: 3,
payload: Payload::CustomSection {
payload: Payload::CustomSection(CustomSectionReader {
name: "",
data_offset: 11,
data: b"",
range: Range { start: 10, end: 11 },
},
}),
}),
);
assert_matches!(
parser_after_header().parse(&[0, 2, 1, b'a'], false),
Ok(Chunk::Parsed {
consumed: 4,
payload: Payload::CustomSection {
payload: Payload::CustomSection(CustomSectionReader {
name: "a",
data_offset: 12,
data: b"",
range: Range { start: 10, end: 12 },
},
}),
}),
);
assert_matches!(
parser_after_header().parse(&[0, 2, 0, b'a'], false),
Ok(Chunk::Parsed {
consumed: 4,
payload: Payload::CustomSection {
payload: Payload::CustomSection(CustomSectionReader {
name: "",
data_offset: 11,
data: b"a",
range: Range { start: 10, end: 12 },
},
}),
}),
);
}
Expand Down
2 changes: 2 additions & 0 deletions crates/wasmparser/src/readers/core.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod code;
mod custom;
mod data;
mod elements;
mod exports;
Expand All @@ -17,6 +18,7 @@ mod tags;
mod types;

pub use self::code::*;
pub use self::custom::*;
pub use self::data::*;
pub use self::elements::*;
pub use self::exports::*;
Expand Down
Loading

0 comments on commit 519d152

Please sign in to comment.