Skip to content
Open
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
42 changes: 41 additions & 1 deletion crates/libafl/src/mutators/scheduled.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,12 @@ where
let mut testcase = (*state.corpus_mut().get(id)?).borrow_mut();
let mut log = Vec::<Cow<'static, str>>::new();
while let Some(idx) = self.mutation_log.pop() {
let name = self.scheduled.mutations().name(idx.0).unwrap().clone(); // TODO maybe return an Error on None
let name = self
.scheduled
.mutations()
.name(idx.0)
.cloned()
.ok_or_else(|| Error::unknown(format!("Missing mutation name for id {}", idx.0)))?;
Copy link
Member

Choose a reason for hiding this comment

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

Can we somehow get the actual mutation and debug print that?

Copy link
Contributor Author

@Jay-1409 Jay-1409 Jan 12, 2026

Choose a reason for hiding this comment

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

I believe that if we store the name over here then its possible to print the name as well. Shoud i proceed?

Line 363 scheduled.rs

    fn scheduled_mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
        let mut r = MutationResult::Skipped;
        let num = self.iterations(state, input);
        self.mutation_log.clear();
        for _ in 0..num {
            let idx = self.schedule(state, input);
            self.mutation_log.push(idx); //<---------- here save the name as well ?
            let outcome = self.mutations_mut().get_and_mutate(idx, state, input)?;
            if outcome == MutationResult::Mutated {
                r = MutationResult::Mutated;
            }
        }
        Ok(r)
    }

Copy link
Member

Choose a reason for hiding this comment

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

if it's possible to only store a ref (not clone the full string), then yes, but I highly doubt it.

Copy link
Contributor Author

@Jay-1409 Jay-1409 Jan 12, 2026

Choose a reason for hiding this comment

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

if it's possible to only store a ref (not clone the full string), then yes, but I highly doubt it.

I cannot think of a way to implement that without saving the entire string.

Could you suggest some ideas to me?

Otherwise, could we proceed without that specific change? Further if I do figure it out, I'll propose it with a new pr

Copy link
Member

Choose a reason for hiding this comment

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

Can we get the actual mutation in the error message and include the debug representation? Not using name at all? In the error handler we have a lot of time, it's not a hot code path.
If not I can merge the current state, too, but it'll make debugging this harder

Copy link
Member

@domenukk domenukk Jan 12, 2026

Choose a reason for hiding this comment

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

Alternatively we can just print the complete mutations tuple, and the id

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alternatively we can just print the complete mutations tuple, and the id

Hey,
For the alternative, did you mean this?

                    .ok_or_else(|| {
                        Error::unknown(format!(
                            "Missing mutation name for id {}. Available mutations: {:?}",
                            idx.0,
                            self.scheduled.mutations().names()
                        ))
                    })?;

log.push(name);
}
let meta = LogMutationMetadata::new(log);
Expand Down Expand Up @@ -518,4 +523,39 @@ mod tests {
assert_ne!(equal_in_a_row, 20);
}
}
#[test]
fn logger_scheduled_errors_on_missing_name() {
use crate::mutators::MutationId;
use libafl_bolts::rands::StdRand;
use crate::{
corpus::{Corpus, InMemoryCorpus, Testcase},
feedbacks::ConstFeedback,
inputs::BytesInput,
mutators::{havoc_mutations::havoc_mutations, scheduled::SingleChoiceScheduledMutator},
state::StdState,
};

let rand = StdRand::with_seed(0x1337);
let mut corpus: InMemoryCorpus<BytesInput> = InMemoryCorpus::new();
corpus.add(Testcase::new(b"abc".to_vec().into())).unwrap();
let corpus_id = corpus.first().unwrap();
let mut feedback = ConstFeedback::new(false);
let mut objective = ConstFeedback::new(false);
let mut state = StdState::new(
rand,
corpus,
InMemoryCorpus::new(),
&mut feedback,
&mut objective,
).unwrap();

let scheduled = SingleChoiceScheduledMutator::new(havoc_mutations());
let mut logger = super::LoggerScheduledMutator::new(scheduled);

// Push an invalid id to trigger the error path
logger.mutation_log.push(MutationId(usize::MAX));

let res = logger.post_exec(&mut state, Some(corpus_id));
assert!(res.is_err());
}
}
Loading