Skip to content

smite-ir/mutators: Add SpliceMutator#135

Open
Chand-ra wants to merge 4 commits into
morehouse:masterfrom
Chand-ra:splice
Open

smite-ir/mutators: Add SpliceMutator#135
Chand-ra wants to merge 4 commits into
morehouse:masterfrom
Chand-ra:splice

Conversation

@Chand-ra

Copy link
Copy Markdown

Add SpliceMutator for smite-IR. Mutates a given program by inserting a spliced input at a random point in the said program.

Chandra Pratap added 4 commits June 29, 2026 06:37
The following commit will implement `SpliceMutator`, which uses
the decoded program from this buffer.
Refactor the common logic from `GeneratorInsertionMutator` tests
that we will need for implementing `SpliceMutator` tests.
Comment on lines +32 to +38
for instr in &self.splice.instructions {
let shifted_inputs: Vec<usize> = instr
.inputs
.iter()
.map(|&input| input + insert_idx)
.collect();
builder.append(instr.operation.clone(), &shifted_inputs);

@Chand-ra Chand-ra Jun 29, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This logic is the only logical difference between this mutator and GeneratorInsertion. It could also be implemented as:

Suggested change
for instr in &self.splice.instructions {
let shifted_inputs: Vec<usize> = instr
.inputs
.iter()
.map(|&input| input + insert_idx)
.collect();
builder.append(instr.operation.clone(), &shifted_inputs);
for instr in &self.splice.instructions {
let mut inputs = vec![];
for var_type in instr.operation.input_types() {
inputs.push(builder.pick_variable(var_type, rng));
}
builder.append(instr.operation.clone(), &inputs);
}

which I guess would "wire it stronger" to the rest of the program, but I'm not sure if that's a worthwhile tradeoff for the simplicity of the current approach.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

It's an interesting idea, but I think the current simpler approach may be better. By doing pick_variable, we end up losing the specific values that the splice program used (which were selected as interesting after many random mutations), which probably hurts fuzzing effectiveness. We'd also need to figure out how to handle variable types for which pick_variable currently panics.

We also can get some of the same behavior already if InputSwapMutator is stacked after the splice.

"gen-insert"
}
5 => {
let mutator = SpliceMutator::new(splice.expect("splice is non-empty").clone());

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Nit: the splice program is expected to be present, but the program itself could be empty

Suggested change
let mutator = SpliceMutator::new(splice.expect("splice is non-empty").clone());
let mutator = SpliceMutator::new(splice.expect("splice is present").clone());

use crate::{Program, ProgramBuilder};

/// Inserts a spliced program at a random point in the given program.
pub struct SpliceMutator {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Nit: perhaps SpliceInsertionMutator would be a more descriptive name. It would also make it clear that this mutator parallels GeneratorInsertionMutator.

Comment on lines +32 to +38
for instr in &self.splice.instructions {
let shifted_inputs: Vec<usize> = instr
.inputs
.iter()
.map(|&input| input + insert_idx)
.collect();
builder.append(instr.operation.clone(), &shifted_inputs);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

It's an interesting idea, but I think the current simpler approach may be better. By doing pick_variable, we end up losing the specific values that the splice program used (which were selected as interesting after many random mutations), which probably hurts fuzzing effectiveness. We'd also need to figure out how to handle variable types for which pick_variable currently panics.

We also can get some of the same behavior already if InputSwapMutator is stacked after the splice.

}

// Insert the spliced program.
for instr in &self.splice.instructions {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I think it would be interesting to do an experiment where we compare full-program insertion with only inserting a random subset (prefix) of the spliced program.

Full-program insertion will tend to create longer (and slower) programs, so it's possible that prefix insertion actually performs better.

/// larger leaps per execution, similar in spirit to AFL++'s havoc stage.
/// Records the ordered list of mutator names in `last_sequence`.
fn mutate_stacked(&mut self, program: &mut Program) {
fn mutate_stacked(&mut self, program: &mut Program, splice: Option<&Program>) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Nit: doc comment needs to be updated to explain the new splice parameter

Comment on lines -589 to -590
#[test]
fn splice_optout_symbol_exists() {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Now that splicing is enabled, we should add an FFI test for it. Currently all tests set add_buf=NULL.

"gen-insert"
}
5 => {
let mutator = SpliceMutator::new(splice.expect("splice is non-empty").clone());

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Nit: we can eliminate the program clone by hoisting splice mutator creation out of the loop and having mutate_stacked take ownership of the splice.

fn mutate_stacked(..., splice: Option<Program>) {
  ...
  let splice_mutator = splice.map(SpliceMutator::new);
  let upper_bound = if splice_mutator.is_some() { 6 } else { 5 };
  for _ in 0..stack {
    let name = match self.rng.random_range(0..upper_bound) {
      ...
      5 => {
        splice_mutator.as_ref().expect("splice present").mutate(program, &mut self.rng);
        "splice"
      }
      ...

Comment thread smite-ir/src/tests.rs
Comment on lines +2438 to +2442
assert_graph_shifted_correctly(
&original,
&splice_program,
splice_program.instructions.len(),
);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This is testing the wrong thing. I'm surprised it actually passes.

Suggested change
assert_graph_shifted_correctly(
&original,
&splice_program,
splice_program.instructions.len(),
);
assert_graph_shifted_correctly(
&original,
&program,
splice_program.instructions.len(),
);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants