Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2a6f9db
use u64 internally to track file offsets
WorldSEnder Jul 18, 2026
22c59c1
inline LogicalOffset as per review comment
WorldSEnder Jul 21, 2026
5cf4607
self-review to simplify logic slightly
WorldSEnder Jul 21, 2026
e8fe38c
convert wasmprinter and wasm-encoder
WorldSEnder Jul 21, 2026
77ab11e
convert wasm-metadata, wasm-mutate, wast (tests)
WorldSEnder Jul 21, 2026
af69edf
fix msrv issue
WorldSEnder Jul 21, 2026
018d1e9
convert wasm-tools bins and fuzz
WorldSEnder Jul 21, 2026
2391907
fix some lints and self-review
WorldSEnder Jul 21, 2026
8c251e0
fix clippy lints and adjust test on error message
WorldSEnder Jul 21, 2026
e0533e0
remove and inline MemOffset structure by review
WorldSEnder Jul 24, 2026
b210630
Merge remote-tracking branch 'upstream/main' into u64-position
WorldSEnder Jul 29, 2026
3560f20
remove InMemData from two uses
WorldSEnder Jul 29, 2026
e5cea77
move range conversion into an OffsetCoverter struct
WorldSEnder Jul 30, 2026
edaf1b2
remove OffsetConverter from parse_all
WorldSEnder Jul 30, 2026
c1a38d0
remove references to OffsetConverter::from_start(0)
WorldSEnder Jul 31, 2026
958f85d
oops missed one due to not building wit-dylib locally
WorldSEnder Jul 31, 2026
5764b12
convert wasm-tools to as-casts
WorldSEnder Jul 31, 2026
9df8023
remove ConvertOffset
WorldSEnder Aug 3, 2026
21390de
minify the diff by reverting some unrelated changes
WorldSEnder Aug 6, 2026
daced2a
two more stylistic changes for diff optimization
WorldSEnder Aug 6, 2026
d8ce95c
fix clippy warnings
WorldSEnder Aug 6, 2026
a40526a
do not silently truncate data input
WorldSEnder Aug 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/wasm-encoder/src/core/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ impl CodeSection {
/// // Add the body to a new code section encoder by copying bytes rather
/// // than re-parsing and re-encoding it.
/// let mut encoder = wasm_encoder::CodeSection::new();
/// encoder.raw(&code_section[body_range.start..body_range.end]);
/// encoder.raw(&code_section[body_range.start as usize..body_range.end as usize]);
/// ```
pub fn raw(&mut self, data: &[u8]) -> &mut Self {
data.encode(&mut self.bytes);
Expand Down
2 changes: 1 addition & 1 deletion crates/wasm-encoder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ impl Encode for str {

impl Encode for usize {
fn encode(&self, sink: &mut Vec<u8>) {
assert!(*self <= u32::max_value() as usize);
assert!(*self <= u32::MAX as usize);
(*self as u32).encode(sink)
}
}
Expand Down
33 changes: 16 additions & 17 deletions crates/wasm-encoder/src/reencode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -679,19 +679,20 @@ pub mod utils {
reencoder.intersperse_section_hook(module, after, before)
}

// Convert from `range` to a byte range within `data` while
// accounting for various offsets. Then create a
// `CodeSectionReader` (which notably the payload does not
// give us here) and recurse with that. This means that
// users overriding `parse_code_section` always get that
// function called.
let orig_offset = parser.offset() as usize;
let get_original_section = |range: Range<usize>| {
data.get(range.start - orig_offset..range.end - orig_offset)
.ok_or(Error::InvalidCodeSectionSize)
};
let mut last_section = None;

let start_offset = parser.offset();
// Convert from `range` to a byte range within `data` while
// accounting for various offsets.
let get_original_section = |range: Range<u64>| {
let start = range.start - start_offset;
let end = range.end - start_offset;
let Ok(end) = usize::try_from(end) else {
return Err(Error::InvalidCodeSectionSize);
};
let data_range = start as usize..end;
data.get(data_range).ok_or(Error::InvalidCodeSectionSize)
};
for section in parser.parse_all(data) {
match section? {
wasmparser::Payload::Version {
Expand Down Expand Up @@ -841,12 +842,10 @@ pub mod utils {
)?;
let mut codes = crate::CodeSection::new();

// Convert from `range` to a byte range within `data` while
// accounting for various offsets. Then create a
// `CodeSectionReader` (which notably the payload does not
// give us here) and recurse with that. This means that
// users overriding `parse_code_section` always get that
// function called.
// Crate a `CodeSectionReader` (which notably the payload
// does not give us here) and recurse with that. This means
// that users overriding `parse_code_section` always get
// that function called.
let section = get_original_section(range.clone())?;
let reader = wasmparser::BinaryReader::new(section, range.start);
let section = wasmparser::CodeSectionReader::new(reader)?;
Expand Down
17 changes: 13 additions & 4 deletions crates/wasm-encoder/src/reencode/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ pub trait ReencodeComponent: Reencode {
parser: wasmparser::Parser,
data: &[u8],
) -> Result<(), Error<Self::Error>> {
// so we can slice into the data with the offsets from the parser
assert_eq!(parser.offset(), 0, "data must be parsed at offset 0");
Comment on lines +80 to +81

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could instead pass the parser offset down into parse_component. I would like your guidance on this, as it's not clear which part is public API and where we assume "correct" data.

component_utils::parse_component(self, component, parser, data, data)
}

Expand Down Expand Up @@ -385,6 +387,8 @@ impl ReencodeComponent for RoundtripReencoder {}

#[allow(missing_docs)] // FIXME
pub mod component_utils {
use core::ops::Range;

use super::super::utils::name_map;
use super::ReencodeComponent;
use crate::reencode::Error;
Expand Down Expand Up @@ -414,7 +418,8 @@ pub mod component_utils {
| wasmparser::Payload::ModuleSection {
unchecked_range, ..
} => {
remaining = &remaining[unchecked_range.len()..];
let skipped_len = (unchecked_range.end - unchecked_range.start) as usize;
remaining = &remaining[skipped_len..];
}
_ => {}
}
Expand All @@ -430,6 +435,10 @@ pub mod component_utils {
payload: wasmparser::Payload<'_>,
whole_component: &[u8],
) -> Result<(), Error<T::Error>> {
let convert_range = |file_range: &Range<u64>| {
// By assumption that `whole_component` is at parser offset 0
file_range.start as usize..file_range.end as usize
};
match payload {
wasmparser::Payload::Version {
encoding: wasmparser::Encoding::Component,
Expand Down Expand Up @@ -504,7 +513,7 @@ pub mod component_utils {
reencoder.parse_component_submodule(
component,
parser,
&whole_component[unchecked_range],
&whole_component[convert_range(&unchecked_range)],
)?;
}
wasmparser::Payload::ComponentSection {
Expand All @@ -514,7 +523,7 @@ pub mod component_utils {
reencoder.parse_component_subcomponent(
component,
parser,
&whole_component[unchecked_range],
&whole_component[convert_range(&unchecked_range)],
whole_component,
)?;
}
Expand All @@ -525,7 +534,7 @@ pub mod component_utils {

other => match other.as_section() {
Some((id, range)) => {
let section = &whole_component[range];
let section = &whole_component[convert_range(&range)];
reencoder.parse_unknown_component_section(component, id, section)?;
}
None => unreachable!(),
Expand Down
2 changes: 1 addition & 1 deletion crates/wasm-metadata/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub struct Metadata {
/// Version of the packaged software
pub version: Option<Version>,
/// Byte range of the module in the parent binary
pub range: Range<usize>,
pub range: Range<u64>,
/// Dependencies of the component
pub dependencies: Option<Dependencies>,
}
2 changes: 1 addition & 1 deletion crates/wasm-metadata/src/names/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl<'a> ComponentNames<'a> {
}
/// Read a component-name section from a WebAssembly binary. Records the component name, as
/// well as all other component name fields for later serialization.
pub fn from_bytes(bytes: &'a [u8], offset: usize) -> Result<ComponentNames<'a>> {
pub fn from_bytes(bytes: &'a [u8], offset: u64) -> Result<ComponentNames<'a>> {
let reader = BinaryReader::new(bytes, offset);
let section = ComponentNameSectionReader::new(reader);
let mut s = Self::empty();
Expand Down
2 changes: 1 addition & 1 deletion crates/wasm-metadata/src/names/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl<'a> ModuleNames<'a> {
}
/// Read a name section from a WebAssembly binary. Records the module name, and all other
/// contents of name section, for later serialization.
pub fn from_bytes(bytes: &'a [u8], offset: usize) -> Result<ModuleNames<'a>> {
pub fn from_bytes(bytes: &'a [u8], offset: u64) -> Result<ModuleNames<'a>> {
let reader = BinaryReader::new(bytes, offset);
let section = NameSectionReader::new(reader);
let mut s = Self::empty();
Expand Down
8 changes: 4 additions & 4 deletions crates/wasm-metadata/src/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ impl Payload {
if output.is_empty() {
match encoding {
wasmparser::Encoding::Module => {
output.push(Self::empty_module(0..input.len()))
output.push(Self::empty_module(0..input.len() as u64))
}
wasmparser::Encoding::Component => {
output.push(Self::empty_component(0..input.len()))
output.push(Self::empty_component(0..input.len() as u64))
}
}
}
Expand Down Expand Up @@ -191,7 +191,7 @@ impl Payload {
}
}

fn empty_component(range: Range<usize>) -> Self {
fn empty_component(range: Range<u64>) -> Self {
let mut this = Self::Component {
metadata: Metadata::default(),
children: vec![],
Expand All @@ -200,7 +200,7 @@ impl Payload {
this
}

fn empty_module(range: Range<usize>) -> Self {
fn empty_module(range: Range<u64>) -> Self {
let mut this = Self::Module(Metadata::default());
this.metadata_mut().range = range;
this
Expand Down
2 changes: 1 addition & 1 deletion crates/wasm-metadata/src/producers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl Producers {
Ok(None)
}
/// Read the producers section from a Wasm binary.
pub fn from_bytes(bytes: &[u8], offset: usize) -> Result<Self> {
pub fn from_bytes(bytes: &[u8], offset: u64) -> Result<Self> {
let reader = BinaryReader::new(bytes, offset);
let section = ProducersSectionReader::new(reader)?;
let mut fields = IndexMap::new();
Expand Down
4 changes: 2 additions & 2 deletions crates/wasm-metadata/src/rewrite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub(crate) fn rewrite_wasm(
let mut names_found = false;
let mut stack = Vec::new();
let mut output = Vec::new();
for payload in Parser::new(0).parse_all(&input) {
for payload in Parser::new(0).parse_all(input) {
let payload = payload?;

// Track nesting depth, so that we don't mess with inner producer sections:
Expand Down Expand Up @@ -169,7 +169,7 @@ pub(crate) fn rewrite_wasm(
if let Some((id, range)) = payload.as_section() {
wasm_encoder::RawSection {
id,
data: &input[range],
data: &input[range.start as usize..range.end as usize],
}
.append_to(&mut output);
}
Expand Down
45 changes: 23 additions & 22 deletions crates/wasm-mutate/src/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ use crate::{
Error, Result,
module::{PrimitiveTypeInfo, TypeInfo},
};
use std::collections::HashSet;
use std::ops::Range;
use std::{collections::HashSet, ops::Range};
use wasm_encoder::{RawSection, SectionId};
use wasmparser::{BinaryReader, Chunk, Parser, Payload};

Expand Down Expand Up @@ -66,6 +65,11 @@ impl<'a> ModuleInfo<'a> {
info.input_wasm = wasm;

loop {
// Convert into a range into the wasm input
let offset = parser.offset();
let get_input_data = |range: &Range<u64>| {
&wasm[(range.start - offset) as usize..(range.end - offset) as usize]
};
let (payload, consumed) = match parser.parse(wasm, true)? {
Chunk::NeedMoreData(hint) => {
panic!("Invalid Wasm module {hint:?}");
Expand All @@ -79,16 +83,16 @@ impl<'a> ModuleInfo<'a> {
size: _,
} => {
info.code = Some(info.raw_sections.len());
info.section(SectionId::Code.into(), range.clone(), input_wasm);
info.section(SectionId::Code.into(), get_input_data(&range));
parser.skip_section();
// update slice, bypass the section
wasm = &input_wasm[range.end..];
wasm = &wasm[(range.end - offset) as usize..];

continue;
}
Payload::TypeSection(reader) => {
info.types = Some(info.raw_sections.len());
info.section(SectionId::Type.into(), reader.range(), input_wasm);
info.section(SectionId::Type.into(), get_input_data(&reader.range()));

// Save function types
for ty in reader.into_iter_err_on_gc_types() {
Expand All @@ -97,7 +101,7 @@ impl<'a> ModuleInfo<'a> {
}
Payload::ImportSection(reader) => {
info.imports = Some(info.raw_sections.len());
info.section(SectionId::Import.into(), reader.range(), input_wasm);
info.section(SectionId::Import.into(), get_input_data(&reader.range()));

for ty in reader.into_imports() {
match ty?.ty {
Expand Down Expand Up @@ -130,7 +134,7 @@ impl<'a> ModuleInfo<'a> {
}
Payload::FunctionSection(reader) => {
info.functions = Some(info.raw_sections.len());
info.section(SectionId::Function.into(), reader.range(), input_wasm);
info.section(SectionId::Function.into(), get_input_data(&reader.range()));

for ty in reader {
info.function_map.push(ty?);
Expand All @@ -139,7 +143,7 @@ impl<'a> ModuleInfo<'a> {
Payload::TableSection(reader) => {
info.tables = Some(info.raw_sections.len());
info.table_count += reader.count();
info.section(SectionId::Table.into(), reader.range(), input_wasm);
info.section(SectionId::Table.into(), get_input_data(&reader.range()));

for table in reader {
let table = table?;
Expand All @@ -149,15 +153,15 @@ impl<'a> ModuleInfo<'a> {
Payload::MemorySection(reader) => {
info.memories = Some(info.raw_sections.len());
info.memory_count += reader.count();
info.section(SectionId::Memory.into(), reader.range(), input_wasm);
info.section(SectionId::Memory.into(), get_input_data(&reader.range()));

for ty in reader {
info.memory_types.push(ty?);
}
}
Payload::GlobalSection(reader) => {
info.globals = Some(info.raw_sections.len());
info.section(SectionId::Global.into(), reader.range(), input_wasm);
info.section(SectionId::Global.into(), get_input_data(&reader.range()));

for ty in reader {
let ty = ty?;
Expand All @@ -174,36 +178,36 @@ impl<'a> ModuleInfo<'a> {
info.export_names.insert(entry?.name.into());
}

info.section(SectionId::Export.into(), reader.range(), input_wasm);
info.section(SectionId::Export.into(), get_input_data(&reader.range()));
}
Payload::StartSection { func, range } => {
info.start = Some(info.raw_sections.len());
info.start_function = Some(func);
info.section(SectionId::Start.into(), range, input_wasm);
info.section(SectionId::Start.into(), get_input_data(&range));
}
Payload::ElementSection(reader) => {
info.elements = Some(info.raw_sections.len());
info.elements_count = reader.count();
info.section(SectionId::Element.into(), reader.range(), input_wasm);
info.section(SectionId::Element.into(), get_input_data(&reader.range()));
}
Payload::DataSection(reader) => {
info.data = Some(info.raw_sections.len());
info.data_segments_count = reader.count();
info.section(SectionId::Data.into(), reader.range(), input_wasm);
info.section(SectionId::Data.into(), get_input_data(&reader.range()));
}
Payload::CustomSection(c) => {
info.section(SectionId::Custom.into(), c.range(), input_wasm);
info.section(SectionId::Custom.into(), get_input_data(&c.range()));
}
Payload::UnknownSection {
id,
contents: _,
range,
} => {
info.section(id, range, input_wasm);
info.section(id, get_input_data(&range));
}
Payload::DataCountSection { count: _, range } => {
info.data_count = Some(info.raw_sections.len());
info.section(SectionId::DataCount.into(), range, input_wasm);
info.section(SectionId::DataCount.into(), get_input_data(&range));
}
Payload::Version { .. } => {}
Payload::End(_) => {
Expand Down Expand Up @@ -241,11 +245,8 @@ impl<'a> ModuleInfo<'a> {
}

/// Registers a new raw_section in the ModuleInfo
pub fn section(&mut self, id: u8, range: Range<usize>, full_wasm: &'a [u8]) {
self.raw_sections.push(RawSection {
id,
data: &full_wasm[range],
});
pub fn section(&mut self, id: u8, data: &'a [u8]) {
self.raw_sections.push(RawSection { id, data });
}

pub fn get_code_section(&self) -> RawSection<'a> {
Expand Down
2 changes: 1 addition & 1 deletion crates/wasm-mutate/src/mutators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ pub trait Mutator {
}

/// Type helper to wrap operator and the byte offset in the code section of a Wasm module
pub type OperatorAndByteOffset<'a> = (Operator<'a>, usize);
pub type OperatorAndByteOffset<'a> = (Operator<'a>, u64);

#[cfg(test)]
fn match_mutation<T>(original: &str, mutator: T, expected: &str)
Expand Down
3 changes: 2 additions & 1 deletion crates/wasm-mutate/src/mutators/codemotion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ impl CodemotionMutator {
) -> crate::Result<(Function, u32)> {
let original_code_section = config.info().code.unwrap();
let reader = config.info().get_binary_reader(original_code_section);
let input_code_section = config.info().get_code_section().data;
let sectionreader = CodeSectionReader::new(reader)?;
let function_count = sectionreader.count();
let function_to_mutate = config.rng().random_range(0..function_count);
Expand Down Expand Up @@ -100,7 +101,7 @@ impl CodemotionMutator {
&ast,
&self.copy_locals(reader)?,
&operators,
config.info().raw_sections[original_code_section].data,
input_code_section,
)?;
return Ok((newfunc, fidx));
}
Expand Down
2 changes: 1 addition & 1 deletion crates/wasm-mutate/src/mutators/codemotion/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ pub trait AstWriter {
&operators[operator_range.0].1,
&operators[operator_range.1].1,
);
let piece_of_code = &input_wasm[*bytes_range.0..*bytes_range.1];
let piece_of_code = &input_wasm[*bytes_range.0 as usize..*bytes_range.1 as usize];
newfunc.raw(piece_of_code.to_vec());
Ok(())
}
Expand Down
Loading
Loading