@@ -68,6 +68,9 @@ const fn fs_opt_get_mapsize(x: i32) -> i32 {
68
68
const SHMEM_FUZZ_HDR_SIZE : usize = 4 ;
69
69
const MAX_INPUT_SIZE_DEFAULT : usize = 1024 * 1024 ;
70
70
71
+ /// The default signal to use to kill child processes
72
+ const KILL_SIGNAL_DEFAULT : Signal = Signal :: SIGTERM ;
73
+
71
74
/// Configure the target, `limit`, `setsid`, `pipe_stdin`, the code was borrowed from the [`Angora`](https://github.com/AngoraFuzzer/Angora) fuzzer
72
75
pub trait ConfigTarget {
73
76
/// Sets the sid
@@ -200,24 +203,42 @@ pub struct Forkserver {
200
203
status : i32 ,
201
204
/// If the last run timed out (in in-target i32)
202
205
last_run_timed_out : i32 ,
206
+ /// The signal this [`Forkserver`] will use to kill (defaults to [`self.kill_signal`])
207
+ kill_signal : Signal ,
203
208
}
204
209
205
210
impl Drop for Forkserver {
206
211
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
+
210
215
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 ) {
212
218
log:: warn!(
213
219
"Failed to deliver kill signal to child process {}: {err} ({})" ,
214
220
pid,
215
221
io:: Error :: last_os_error( )
216
222
) ;
217
223
}
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 ) ;
221
242
}
222
243
}
223
244
}
@@ -236,6 +257,36 @@ impl Forkserver {
236
257
is_persistent : bool ,
237
258
is_deferred_frksrv : bool ,
238
259
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 ,
239
290
) -> Result < Self , Error > {
240
291
let mut st_pipe = Pipe :: new ( ) . unwrap ( ) ;
241
292
let mut ctl_pipe = Pipe :: new ( ) . unwrap ( ) ;
@@ -300,6 +351,7 @@ impl Forkserver {
300
351
child_pid : None ,
301
352
status : 0 ,
302
353
last_run_timed_out : 0 ,
354
+ kill_signal,
303
355
} )
304
356
}
305
357
@@ -674,6 +726,7 @@ pub struct ForkserverExecutorBuilder<'a, SP> {
674
726
max_input_size : usize ,
675
727
map_size : Option < usize > ,
676
728
real_map_size : i32 ,
729
+ kill_signal : Option < Signal > ,
677
730
}
678
731
679
732
impl < ' a , SP > ForkserverExecutorBuilder < ' a , SP > {
@@ -800,7 +853,7 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
800
853
} ;
801
854
802
855
let mut forkserver = match & self . program {
803
- Some ( t) => Forkserver :: new (
856
+ Some ( t) => Forkserver :: with_kill_signal (
804
857
t. clone ( ) ,
805
858
self . arguments . clone ( ) ,
806
859
self . envs . clone ( ) ,
@@ -810,6 +863,7 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
810
863
self . is_persistent ,
811
864
self . is_deferred_frksrv ,
812
865
self . debug_child ,
866
+ self . kill_signal . unwrap_or ( KILL_SIGNAL_DEFAULT ) ,
813
867
) ?,
814
868
None => {
815
869
return Err ( Error :: illegal_argument (
@@ -1030,11 +1084,11 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
1030
1084
self
1031
1085
}
1032
1086
1033
- #[ must_use]
1034
1087
/// Place the input at this position and set the filename for the input.
1035
1088
///
1036
1089
/// Note: If you use this, you should ensure that there is only one instance using this
1037
1090
/// file at any given time.
1091
+ #[ must_use]
1038
1092
pub fn arg_input_file < P : AsRef < Path > > ( self , path : P ) -> Self {
1039
1093
let mut moved = self . arg ( path. as_ref ( ) ) ;
1040
1094
@@ -1050,40 +1104,47 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
1050
1104
moved
1051
1105
}
1052
1106
1053
- #[ must_use]
1054
1107
/// Place the input at this position and set the default filename for the input.
1108
+ #[ must_use]
1055
1109
/// The filename includes the PID of the fuzzer to ensure that no two fuzzers write to the same file
1056
1110
pub fn arg_input_file_std ( self ) -> Self {
1057
1111
self . arg_input_file ( get_unique_std_input_file ( ) )
1058
1112
}
1059
1113
1060
- #[ must_use]
1061
1114
/// If `debug_child` is set, the child will print to `stdout`/`stderr`.
1115
+ #[ must_use]
1062
1116
pub fn debug_child ( mut self , debug_child : bool ) -> Self {
1063
1117
self . debug_child = debug_child;
1064
1118
self
1065
1119
}
1066
1120
1067
- #[ must_use]
1068
1121
/// Call this if you want to run it under persistent mode; default is false
1122
+ #[ must_use]
1069
1123
pub fn is_persistent ( mut self , is_persistent : bool ) -> Self {
1070
1124
self . is_persistent = is_persistent;
1071
1125
self
1072
1126
}
1073
1127
1074
- #[ must_use]
1075
1128
/// Call this if the harness uses deferred forkserver mode; default is false
1129
+ #[ must_use]
1076
1130
pub fn is_deferred_frksrv ( mut self , is_deferred_frksrv : bool ) -> Self {
1077
1131
self . is_deferred_frksrv = is_deferred_frksrv;
1078
1132
self
1079
1133
}
1080
1134
1081
- #[ must_use]
1082
1135
/// Call this to set a defauult const coverage map size
1136
+ #[ must_use]
1083
1137
pub fn coverage_map_size ( mut self , size : usize ) -> Self {
1084
1138
self . map_size = Some ( size) ;
1085
1139
self
1086
1140
}
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
+ }
1087
1148
}
1088
1149
1089
1150
impl < ' a > ForkserverExecutorBuilder < ' a , UnixShMemProvider > {
@@ -1110,6 +1171,7 @@ impl<'a> ForkserverExecutorBuilder<'a, UnixShMemProvider> {
1110
1171
map_size : None ,
1111
1172
real_map_size : 0 ,
1112
1173
max_input_size : MAX_INPUT_SIZE_DEFAULT ,
1174
+ kill_signal : None ,
1113
1175
}
1114
1176
}
1115
1177
@@ -1133,6 +1195,7 @@ impl<'a> ForkserverExecutorBuilder<'a, UnixShMemProvider> {
1133
1195
map_size : self . map_size ,
1134
1196
real_map_size : self . real_map_size ,
1135
1197
max_input_size : MAX_INPUT_SIZE_DEFAULT ,
1198
+ kill_signal : None ,
1136
1199
}
1137
1200
}
1138
1201
}
0 commit comments