Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 16 additions & 8 deletions smite-ir-mutator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use rand::rngs::SmallRng;
use rand::{RngExt, SeedableRng};

use smite_ir::generators::OpenChannelGenerator;
use smite_ir::mutators::{InputSwapMutator, OperationParamMutator};
use smite_ir::mutators::{InputSwapMutator, InstructionReorderMutator, OperationParamMutator};
use smite_ir::{Generator, Mutator, Program, ProgramBuilder};

/// Mutator state owned by AFL++ across calls. Allocated by [`afl_custom_init`],
Expand Down Expand Up @@ -77,12 +77,20 @@ impl MutatorState {
let stack = 1u32 << self.rng.random_range(0..=4);
for _ in 0..stack {
// Uniform pick between the available mutators.
let name = if self.rng.random() {
OperationParamMutator.mutate(program, &mut self.rng);
"op-param"
} else {
InputSwapMutator.mutate(program, &mut self.rng);
"input-swap"
let name = match self.rng.random_range(0..3) {
0 => {
OperationParamMutator.mutate(program, &mut self.rng);
"op-param"
}
1 => {
InputSwapMutator.mutate(program, &mut self.rng);
"input-swap"
}
2 => {
InstructionReorderMutator.mutate(program, &mut self.rng);
"instr-reorder"
}
_ => unreachable!(),
};
self.last_sequence.push(name);
}
Expand Down Expand Up @@ -369,7 +377,7 @@ mod tests {
}
for name in suffix.split(',') {
assert!(
name == "op-param" || name == "input-swap",
name == "op-param" || name == "input-swap" || name == "instr-reorder",
"unexpected mutator name in description: {name:?} (full: {s:?})",
);
}
Expand Down
2 changes: 2 additions & 0 deletions smite-ir/src/mutators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
//! structural validity. Each mutator makes a small, targeted change.

mod input_swap;
mod instruction_reorder;
mod operation_param;

pub use input_swap::InputSwapMutator;
pub use instruction_reorder::InstructionReorderMutator;
pub use operation_param::OperationParamMutator;

use rand::Rng;
Expand Down
67 changes: 67 additions & 0 deletions smite-ir/src/mutators/instruction_reorder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//! Mutator that swaps Act instructions.

use rand::{Rng, seq::IteratorRandom};

use super::Mutator;
use crate::Program;

/// Swaps two `Act` instructions that have no data dependencies between them.
/// This explores alternative execution orderings while preserving SSA invariants.
pub struct InstructionReorderMutator;

impl Mutator for InstructionReorderMutator {
fn mutate(&self, program: &mut Program, rng: &mut impl Rng) -> bool {
// Select an Act instruction at random (say Act_1).
let Some(act1_idx) = program
.instructions
.iter()
.enumerate()
.filter_map(|(i, instr)| instr.operation.is_act().then_some(i))
.choose(rng)
else {
return false;
};

// Find the first instruction that consumes Act_1. We cannot move
// Act_1 past this point without breaking def-before-use.
let mut usage_boundary = act1_idx;
for instr in &program.instructions[(act1_idx + 1)..] {
if instr.inputs.contains(&act1_idx) {
break;
}
usage_boundary += 1;
}

// Uniformly sample a valid Act_2 from the safe range.
let Some(act2_idx) = ((act1_idx + 1)..=usage_boundary)
.filter(|&i| {
// Ensure the candidate is an Act and does not depend on anything
// defined at or after Act_1, guaranteeing it can be safely moved up.
program.instructions[i].operation.is_act()
&& !program.instructions[i]
.inputs
.iter()
.any(|&input| input >= act1_idx)
})
.choose(rng)
else {
// Abort if no valid independent Act instructions exist in the range.
return false;
};

// Swap Act_1 and Act_2.
program.instructions.swap(act1_idx, act2_idx);

// Healing: Update downstream references to Act_1 and Act_2.
for instr in &mut program.instructions[(act2_idx + 1)..] {
for input in &mut instr.inputs {
if *input == act1_idx {
*input = act2_idx;
} else if *input == act2_idx {
*input = act1_idx;
}
}
}
true
}
}
6 changes: 6 additions & 0 deletions smite-ir/src/operation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,4 +304,10 @@ impl Operation {
| Self::ExtractAcceptChannel(_)
)
}

/// Returns true for Act instructions.
#[must_use]
pub fn is_act(&self) -> bool {
matches!(self, Self::SendMessage | Self::RecvAcceptChannel)
}
}
Loading