Skip to content

Commit 3625e88

Browse files
authored
Fix forkserver child kill, add kill_signal support (#1521)
* Fix forkserver child kill, add kill_signal support * more fix
1 parent 8f6efe9 commit 3625e88

File tree

1 file changed

+77
-14
lines changed

1 file changed

+77
-14
lines changed

libafl/src/executors/forkserver.rs

Lines changed: 77 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ const fn fs_opt_get_mapsize(x: i32) -> i32 {
6868
const SHMEM_FUZZ_HDR_SIZE: usize = 4;
6969
const MAX_INPUT_SIZE_DEFAULT: usize = 1024 * 1024;
7070

71+
/// The default signal to use to kill child processes
72+
const KILL_SIGNAL_DEFAULT: Signal = Signal::SIGTERM;
73+
7174
/// Configure the target, `limit`, `setsid`, `pipe_stdin`, the code was borrowed from the [`Angora`](https://github.com/AngoraFuzzer/Angora) fuzzer
7275
pub trait ConfigTarget {
7376
/// Sets the sid
@@ -200,24 +203,42 @@ pub struct Forkserver {
200203
status: i32,
201204
/// If the last run timed out (in in-target i32)
202205
last_run_timed_out: i32,
206+
/// The signal this [`Forkserver`] will use to kill (defaults to [`self.kill_signal`])
207+
kill_signal: Signal,
203208
}
204209

205210
impl Drop for Forkserver {
206211
fn drop(&mut self) {
207-
if let Err(err) = self.fsrv_handle.kill() {
208-
log::warn!("Failed kill forkserver: {err}",);
209-
}
212+
// Modelled after <https://github.com/AFLplusplus/AFLplusplus/blob/dee76993812fa9b5d8c1b75126129887a10befae/src/afl-forkserver.c#L1429>
213+
log::debug!("Dropping forkserver",);
214+
210215
if let Some(pid) = self.child_pid {
211-
if let Err(err) = kill(pid, Signal::SIGKILL) {
216+
log::debug!("Sending {} to child {pid}", self.kill_signal);
217+
if let Err(err) = kill(pid, self.kill_signal) {
212218
log::warn!(
213219
"Failed to deliver kill signal to child process {}: {err} ({})",
214220
pid,
215221
io::Error::last_os_error()
216222
);
217223
}
218-
if let Err(err) = waitpid(pid, None) {
219-
log::warn!("Failed to wait for child pid ({pid}): {err}",);
220-
}
224+
}
225+
226+
let forkserver_pid = Pid::from_raw(self.fsrv_handle.id().try_into().unwrap());
227+
if let Err(err) = kill(forkserver_pid, self.kill_signal) {
228+
log::warn!(
229+
"Failed to deliver {} signal to forkserver {}: {err} ({})",
230+
self.kill_signal,
231+
forkserver_pid,
232+
io::Error::last_os_error()
233+
);
234+
let _ = kill(forkserver_pid, Signal::SIGKILL);
235+
} else if let Err(err) = waitpid(forkserver_pid, None) {
236+
log::warn!(
237+
"Waitpid on forkserver {} failed: {err} ({})",
238+
forkserver_pid,
239+
io::Error::last_os_error()
240+
);
241+
let _ = kill(forkserver_pid, Signal::SIGKILL);
221242
}
222243
}
223244
}
@@ -236,6 +257,36 @@ impl Forkserver {
236257
is_persistent: bool,
237258
is_deferred_frksrv: bool,
238259
debug_output: bool,
260+
) -> Result<Self, Error> {
261+
Self::with_kill_signal(
262+
target,
263+
args,
264+
envs,
265+
input_filefd,
266+
use_stdin,
267+
memlimit,
268+
is_persistent,
269+
is_deferred_frksrv,
270+
debug_output,
271+
KILL_SIGNAL_DEFAULT,
272+
)
273+
}
274+
275+
/// Create a new [`Forkserver`] that will kill child processes
276+
/// with the given `kill_signal`.
277+
/// Using `Forkserver::new(..)` will default to [`Signal::SIGTERM`].
278+
#[allow(clippy::too_many_arguments)]
279+
pub fn with_kill_signal(
280+
target: OsString,
281+
args: Vec<OsString>,
282+
envs: Vec<(OsString, OsString)>,
283+
input_filefd: RawFd,
284+
use_stdin: bool,
285+
memlimit: u64,
286+
is_persistent: bool,
287+
is_deferred_frksrv: bool,
288+
debug_output: bool,
289+
kill_signal: Signal,
239290
) -> Result<Self, Error> {
240291
let mut st_pipe = Pipe::new().unwrap();
241292
let mut ctl_pipe = Pipe::new().unwrap();
@@ -300,6 +351,7 @@ impl Forkserver {
300351
child_pid: None,
301352
status: 0,
302353
last_run_timed_out: 0,
354+
kill_signal,
303355
})
304356
}
305357

@@ -674,6 +726,7 @@ pub struct ForkserverExecutorBuilder<'a, SP> {
674726
max_input_size: usize,
675727
map_size: Option<usize>,
676728
real_map_size: i32,
729+
kill_signal: Option<Signal>,
677730
}
678731

679732
impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
@@ -800,7 +853,7 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
800853
};
801854

802855
let mut forkserver = match &self.program {
803-
Some(t) => Forkserver::new(
856+
Some(t) => Forkserver::with_kill_signal(
804857
t.clone(),
805858
self.arguments.clone(),
806859
self.envs.clone(),
@@ -810,6 +863,7 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
810863
self.is_persistent,
811864
self.is_deferred_frksrv,
812865
self.debug_child,
866+
self.kill_signal.unwrap_or(KILL_SIGNAL_DEFAULT),
813867
)?,
814868
None => {
815869
return Err(Error::illegal_argument(
@@ -1030,11 +1084,11 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
10301084
self
10311085
}
10321086

1033-
#[must_use]
10341087
/// Place the input at this position and set the filename for the input.
10351088
///
10361089
/// Note: If you use this, you should ensure that there is only one instance using this
10371090
/// file at any given time.
1091+
#[must_use]
10381092
pub fn arg_input_file<P: AsRef<Path>>(self, path: P) -> Self {
10391093
let mut moved = self.arg(path.as_ref());
10401094

@@ -1050,40 +1104,47 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
10501104
moved
10511105
}
10521106

1053-
#[must_use]
10541107
/// Place the input at this position and set the default filename for the input.
1108+
#[must_use]
10551109
/// The filename includes the PID of the fuzzer to ensure that no two fuzzers write to the same file
10561110
pub fn arg_input_file_std(self) -> Self {
10571111
self.arg_input_file(get_unique_std_input_file())
10581112
}
10591113

1060-
#[must_use]
10611114
/// If `debug_child` is set, the child will print to `stdout`/`stderr`.
1115+
#[must_use]
10621116
pub fn debug_child(mut self, debug_child: bool) -> Self {
10631117
self.debug_child = debug_child;
10641118
self
10651119
}
10661120

1067-
#[must_use]
10681121
/// Call this if you want to run it under persistent mode; default is false
1122+
#[must_use]
10691123
pub fn is_persistent(mut self, is_persistent: bool) -> Self {
10701124
self.is_persistent = is_persistent;
10711125
self
10721126
}
10731127

1074-
#[must_use]
10751128
/// Call this if the harness uses deferred forkserver mode; default is false
1129+
#[must_use]
10761130
pub fn is_deferred_frksrv(mut self, is_deferred_frksrv: bool) -> Self {
10771131
self.is_deferred_frksrv = is_deferred_frksrv;
10781132
self
10791133
}
10801134

1081-
#[must_use]
10821135
/// Call this to set a defauult const coverage map size
1136+
#[must_use]
10831137
pub fn coverage_map_size(mut self, size: usize) -> Self {
10841138
self.map_size = Some(size);
10851139
self
10861140
}
1141+
1142+
/// Call this to set a signal to be used to kill child processes after executions
1143+
#[must_use]
1144+
pub fn kill_signal(mut self, kill_signal: Signal) -> Self {
1145+
self.kill_signal = Some(kill_signal);
1146+
self
1147+
}
10871148
}
10881149

10891150
impl<'a> ForkserverExecutorBuilder<'a, UnixShMemProvider> {
@@ -1110,6 +1171,7 @@ impl<'a> ForkserverExecutorBuilder<'a, UnixShMemProvider> {
11101171
map_size: None,
11111172
real_map_size: 0,
11121173
max_input_size: MAX_INPUT_SIZE_DEFAULT,
1174+
kill_signal: None,
11131175
}
11141176
}
11151177

@@ -1133,6 +1195,7 @@ impl<'a> ForkserverExecutorBuilder<'a, UnixShMemProvider> {
11331195
map_size: self.map_size,
11341196
real_map_size: self.real_map_size,
11351197
max_input_size: MAX_INPUT_SIZE_DEFAULT,
1198+
kill_signal: None,
11361199
}
11371200
}
11381201
}

0 commit comments

Comments
 (0)