From 6a34c5403c93e5eb6559212abf360c5f80f445d9 Mon Sep 17 00:00:00 2001 From: k5602 <188656344+k5602@users.noreply.github.com> Date: Mon, 23 Feb 2026 06:06:26 +0200 Subject: [PATCH] fix(stages): route non-Ok exits through evaluate_execution in GeneralizationStage verify_input called executor.run_target() directly and silently discarded any ExitKind::Crash / Timeout / Oom result. Crashes found while probing gap candidates were never forwarded to objective feedback and therefore never added to the solutions corpus. Fix: after post_exec_all, if exit_kind != ExitKind::Ok, delegate to fuzzer.evaluate_execution() with send_events=true so objective/corpus feedback runs through the canonical pipeline, then return Ok(false) to halt generalization on the failing input. Propagate the required Z: ExecutionProcessor<...> bound to the Stage impl where-clause and to find_gaps / find_gaps_in_closures, which also call verify_input on every reduced candidate. --- crates/libafl/src/stages/generalization.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/crates/libafl/src/stages/generalization.rs b/crates/libafl/src/stages/generalization.rs index 712eb1ca0cd..d8d2f111cf2 100644 --- a/crates/libafl/src/stages/generalization.rs +++ b/crates/libafl/src/stages/generalization.rs @@ -14,9 +14,9 @@ use libafl_bolts::{ #[cfg(feature = "introspection")] use crate::monitors::stats::PerfFeature; use crate::{ - Error, HasMetadata, HasNamedMetadata, + Error, ExecutionProcessor, HasMetadata, HasNamedMetadata, corpus::{Corpus, HasCurrentCorpusId}, - executors::{Executor, HasObservers}, + executors::{Executor, ExitKind, HasObservers}, feedbacks::map::MapNoveltiesMetadata, inputs::{ BytesInput, GeneralizedInputMetadata, GeneralizedItem, HasMutatorBytes, ResizableMutator, @@ -92,6 +92,7 @@ where + HasNamedMetadata + HasCurrentCorpusId + MaybeHasClientPerfMonitor, + Z: ExecutionProcessor, { #[inline] #[expect(clippy::too_many_lines)] @@ -377,6 +378,7 @@ where where E: Executor + HasObservers, E::Observers: ObserversTuple, + Z: ExecutionProcessor, { start_timer!(state); executor.observers_mut().pre_exec_all(state, input)?; @@ -392,6 +394,12 @@ where .post_exec_all(state, input, &exit_kind)?; mark_feature_time!(state, PerfFeature::PostExecObservers); + if exit_kind != ExitKind::Ok { + let observers = executor.observers(); + fuzzer.evaluate_execution(state, manager, input, &*observers, &exit_kind, true)?; + return Ok(false); + } + let cnt = executor.observers()[&self.map_observer_handle] .as_ref() .how_many_set(novelties); @@ -418,6 +426,7 @@ where ) -> Result<(), Error> where E: Executor + HasObservers, + Z: ExecutionProcessor, { let mut start = 0; while start < payload.len() { @@ -456,6 +465,7 @@ where ) -> Result<(), Error> where E: Executor + HasObservers, + Z: ExecutionProcessor, { let mut index = 0; while index < payload.len() {