Skip to content

Conversation

khagankhan
Copy link
Contributor

  • Add a function import that takes a structref argument to our Wasm binary skeleton
  • Add a fuzzer op that calls that new function
  • Add a function import for each defined struct type that takes a reference of that type specifically
  • Add a fuzzer op that calls these new typed functions
  • Generic (type (;4;) (func (param (ref any)))) is called after StructNew op instead of Drop to observe that specific struct and also maintain stack discipline.
  • Typed versions are called with specific structs of the types

cc @fitzgen @eeide

@khagankhan khagankhan requested a review from a team as a code owner September 18, 2025 23:04
@khagankhan khagankhan requested review from alexcrichton and removed request for a team September 18, 2025 23:04
@github-actions github-actions bot added the fuzzing Issues related to our fuzzing infrastructure label Sep 19, 2025
Copy link

Subscribe to Label Action

cc @fitzgen

This issue or pull request has been labeled: "fuzzing"

Thus the following users have been cc'd because of the following labels:

  • fitzgen: fuzzing

To subscribe or unsubscribe from this label, edit the .github/subscribe-to-label.json configuration file.

Learn more.

Copy link
Member

@fitzgen fitzgen left a comment

Choose a reason for hiding this comment

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

Looks good with the below addressed, thanks!

Comment on lines +22 to +23
const STRUCT_BASE: u32 = 5;
const TYPED_FN_BASE: u32 = 4;
Copy link
Member

Choose a reason for hiding this comment

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

Not necessary in this PR, but it would probably be easier/cleaner down the line to switch from constants to using the wasm_encoder::{Type,Func}Section::len methods to keep track of these things, so that we don't need to update these constants every time we unconditionally add a new type/function.

https://docs.rs/wasm-encoder/latest/wasm_encoder/struct.TypeSection.html#method.len


for i in 0..struct_count {
let concrete = STRUCT_BASE + i;
let ty_idx = typed_ft_base + i; //
Copy link
Member

Choose a reason for hiding this comment

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

Empty comment at the end of line?

Comment on lines +264 to +270
let name = format!("take_struct_{concrete}");
typed_names.push(name);
imports.import(
"",
typed_names.last().unwrap().as_str(),
EntityType::Function(ty_idx),
);
Copy link
Member

Choose a reason for hiding this comment

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

You can avoid the wonky .last() stuff by pushing the name after defining the import:

Suggested change
let name = format!("take_struct_{concrete}");
typed_names.push(name);
imports.import(
"",
typed_names.last().unwrap().as_str(),
EntityType::Function(ty_idx),
);
let name = format!("take_struct_{concrete}");
imports.import(
"",
&name,
EntityType::Function(ty_idx),
);
typed_names.push(name);

Comment on lines +259 to +261
let mut typed_names: Vec<String> = Vec::new();

for i in 0..struct_count {
Copy link
Member

Choose a reason for hiding this comment

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

Would be nice to have a single-sentence comment for readers here describing what this code is doing, so that they don't have to scrutinize it to figure out what is going on. Something like

For each of our concrete struct types, define a function import that takes an argument of that concrete type.

Comment on lines +296 to +302
// Define the "run" function export.
let mut functions = FunctionSection::new();
functions.function(1);

let mut exports = ExportSection::new();
exports.export("run", ExportKind::Func, 3);
let imported_fn_count: u32 = 4 + struct_count;
exports.export("run", ExportKind::Func, imported_fn_count);
Copy link
Member

Choose a reason for hiding this comment

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

As mentioned earlier, things get a bit simpler and less brittle if we use the len functions on the sections that define index spaces:

Suggested change
exports.export("run", ExportKind::Func, imported_fn_count);
let mut functions = FunctionSection::new();
let mut exports = ExportSection::new();
// Define the "run" function export.
let run_index = functions.len();
functions.function(1);
exports.export("run", ExportKind::Func, run_index);

I think it is worth doing this to avoid adding more opaque <constant> + <count> incantations.

Comment on lines +698 to +709
func.instruction(&Instruction::StructNew(x + 5));
func.instruction(&Instruction::Call(take_structref_idx));
}
Self::TakeStructCall(x) => {
func.instruction(&Instruction::StructNew(x + 5));
func.instruction(&Instruction::Call(take_structref_idx));
}
Self::TakeTypedStructCall(x) => {
let s = STRUCT_BASE + x;
let f = TYPED_FN_BASE + x;
func.instruction(&Instruction::StructNew(s));
func.instruction(&Instruction::Call(f));
Copy link
Member

Choose a reason for hiding this comment

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

Nothing to change in this PR, but I want to point out that these emitted instruction sequences are not our ideal end state. We want to separate struct.new from various calls and drops and have the existing TableOp::Drop be used to drop struct refs and we want the take_* calls to take a struct ref from the stack, not create a new one to pass to the import. This will require updating the abstract stack representation to track types, however, so it should be left to a follow up PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I agree!

@khagankhan
Copy link
Contributor Author

Thanks @fitzgen. Working on them!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
fuzzing Issues related to our fuzzing infrastructure
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants