@@ -12,7 +12,7 @@ use std::{
12
12
io:: { self , prelude:: * , ErrorKind } ,
13
13
os:: unix:: { io:: RawFd , process:: CommandExt } ,
14
14
path:: Path ,
15
- process:: { Command , Stdio } ,
15
+ process:: { Child , Command , Stdio } ,
16
16
} ;
17
17
18
18
use libafl_bolts:: {
@@ -27,6 +27,7 @@ use nix::{
27
27
select:: { pselect, FdSet } ,
28
28
signal:: { kill, SigSet , Signal } ,
29
29
time:: { TimeSpec , TimeValLike } ,
30
+ wait:: waitpid,
30
31
} ,
31
32
unistd:: Pid ,
32
33
} ;
@@ -150,6 +151,9 @@ impl ConfigTarget for Command {
150
151
if memlimit == 0 {
151
152
return self ;
152
153
}
154
+ // SAFETY
155
+ // This method does not do shady pointer foo.
156
+ // It merely call libc functions.
153
157
let func = move || {
154
158
let memlimit: libc:: rlim_t = ( memlimit as libc:: rlim_t ) << 20 ;
155
159
let r = libc:: rlimit {
@@ -174,6 +178,8 @@ impl ConfigTarget for Command {
174
178
}
175
179
Ok ( ( ) )
176
180
} ;
181
+ // # SAFETY
182
+ // This calls our non-shady function from above.
177
183
unsafe { self . pre_exec ( func) }
178
184
}
179
185
}
@@ -182,13 +188,40 @@ impl ConfigTarget for Command {
182
188
/// The communication happens via pipe.
183
189
#[ derive( Debug ) ]
184
190
pub struct Forkserver {
191
+ /// The "actual" forkserver we spawned in the target
192
+ fsrv_handle : Child ,
193
+ /// Status pipe
185
194
st_pipe : Pipe ,
195
+ /// Control pipe
186
196
ctl_pipe : Pipe ,
187
- child_pid : Pid ,
197
+ /// Pid of the current forked child (child of the forkserver) during execution
198
+ child_pid : Option < Pid > ,
199
+ /// The last status reported to us by the in-target forkserver
188
200
status : i32 ,
201
+ /// If the last run timed out (in in-target i32)
189
202
last_run_timed_out : i32 ,
190
203
}
191
204
205
+ impl Drop for Forkserver {
206
+ fn drop ( & mut self ) {
207
+ if let Err ( err) = self . fsrv_handle . kill ( ) {
208
+ log:: warn!( "Failed kill forkserver: {err}" , ) ;
209
+ }
210
+ if let Some ( pid) = self . child_pid {
211
+ if let Err ( err) = kill ( pid, Signal :: SIGKILL ) {
212
+ log:: warn!(
213
+ "Failed to deliver kill signal to child process {}: {err} ({})" ,
214
+ pid,
215
+ io:: Error :: last_os_error( )
216
+ ) ;
217
+ }
218
+ if let Err ( err) = waitpid ( pid, None ) {
219
+ log:: warn!( "Failed to wait for child pid ({pid}): {err}" , ) ;
220
+ }
221
+ }
222
+ }
223
+ }
224
+
192
225
#[ allow( clippy:: fn_params_excessive_bools) ]
193
226
impl Forkserver {
194
227
/// Create a new [`Forkserver`]
@@ -234,7 +267,7 @@ impl Forkserver {
234
267
#[ cfg( feature = "regex" ) ]
235
268
command. env ( "ASAN_OPTIONS" , get_asan_runtime_flags_with_log_path ( ) ) ;
236
269
237
- match command
270
+ let fsrv_handle = match command
238
271
. env ( "LD_BIND_NOW" , "1" )
239
272
. envs ( envs)
240
273
. setlimit ( memlimit)
@@ -248,7 +281,7 @@ impl Forkserver {
248
281
)
249
282
. spawn ( )
250
283
{
251
- Ok ( _ ) => ( ) ,
284
+ Ok ( fsrv_handle ) => fsrv_handle ,
252
285
Err ( err) => {
253
286
return Err ( Error :: illegal_state ( format ! (
254
287
"Could not spawn the forkserver: {err:#?}"
@@ -261,25 +294,39 @@ impl Forkserver {
261
294
st_pipe. close_write_end ( ) ;
262
295
263
296
Ok ( Self {
297
+ fsrv_handle,
264
298
st_pipe,
265
299
ctl_pipe,
266
- child_pid : Pid :: from_raw ( 0 ) ,
300
+ child_pid : None ,
267
301
status : 0 ,
268
302
last_run_timed_out : 0 ,
269
303
} )
270
304
}
271
305
272
- /// If the last run timed out
306
+ /// If the last run timed out (as in-target i32)
273
307
#[ must_use]
274
- pub fn last_run_timed_out ( & self ) -> i32 {
308
+ pub fn last_run_timed_out_raw ( & self ) -> i32 {
275
309
self . last_run_timed_out
276
310
}
277
311
278
- /// Sets if the last run timed out
279
- pub fn set_last_run_timed_out ( & mut self , last_run_timed_out : i32 ) {
312
+ /// If the last run timed out
313
+ #[ must_use]
314
+ pub fn last_run_timed_out ( & self ) -> bool {
315
+ self . last_run_timed_out_raw ( ) != 0
316
+ }
317
+
318
+ /// Sets if the last run timed out (as in-target i32)
319
+ #[ inline]
320
+ pub fn set_last_run_timed_out_raw ( & mut self , last_run_timed_out : i32 ) {
280
321
self . last_run_timed_out = last_run_timed_out;
281
322
}
282
323
324
+ /// Sets if the last run timed out
325
+ #[ inline]
326
+ pub fn set_last_run_timed_out ( & mut self , last_run_timed_out : bool ) {
327
+ self . last_run_timed_out = i32:: from ( last_run_timed_out) ;
328
+ }
329
+
283
330
/// The status
284
331
#[ must_use]
285
332
pub fn status ( & self ) -> i32 {
@@ -294,12 +341,17 @@ impl Forkserver {
294
341
/// The child pid
295
342
#[ must_use]
296
343
pub fn child_pid ( & self ) -> Pid {
297
- self . child_pid
344
+ self . child_pid . unwrap ( )
298
345
}
299
346
300
347
/// Set the child pid
301
348
pub fn set_child_pid ( & mut self , child_pid : Pid ) {
302
- self . child_pid = child_pid;
349
+ self . child_pid = Some ( child_pid) ;
350
+ }
351
+
352
+ /// Remove the child pid.
353
+ pub fn reset_child_pid ( & mut self ) {
354
+ self . child_pid = None ;
303
355
}
304
356
305
357
/// Read from the st pipe
@@ -433,9 +485,15 @@ where
433
485
) -> Result < ExitKind , Error > {
434
486
let mut exit_kind = ExitKind :: Ok ;
435
487
436
- let last_run_timed_out = self . executor . forkserver ( ) . last_run_timed_out ( ) ;
488
+ let last_run_timed_out = self . executor . forkserver ( ) . last_run_timed_out_raw ( ) ;
437
489
438
490
if self . executor . uses_shmem_testcase ( ) {
491
+ debug_assert ! (
492
+ self . executor. shmem_mut( ) . is_some( ) ,
493
+ "The uses_shmem_testcase() bool can only exist when a map is set"
494
+ ) ;
495
+ // # SAFETY
496
+ // Struct can never be created when uses_shmem_testcase is true and map is none.
439
497
let map = unsafe { self . executor . shmem_mut ( ) . as_mut ( ) . unwrap_unchecked ( ) } ;
440
498
let target_bytes = input. target_bytes ( ) ;
441
499
let mut size = target_bytes. as_slice ( ) . len ( ) ;
@@ -461,7 +519,7 @@ where
461
519
. forkserver_mut ( )
462
520
. write_ctl ( last_run_timed_out) ?;
463
521
464
- self . executor . forkserver_mut ( ) . set_last_run_timed_out ( 0 ) ;
522
+ self . executor . forkserver_mut ( ) . set_last_run_timed_out ( false ) ;
465
523
466
524
if send_len != 4 {
467
525
return Err ( Error :: unknown (
@@ -503,21 +561,18 @@ where
503
561
}
504
562
}
505
563
} else {
506
- self . executor . forkserver_mut ( ) . set_last_run_timed_out ( 1 ) ;
564
+ self . executor . forkserver_mut ( ) . set_last_run_timed_out ( true ) ;
507
565
508
566
// We need to kill the child in case he has timed out, or we can't get the correct pid in the next call to self.executor.forkserver_mut().read_st()?
509
- let _: Result < ( ) , nix:: errno:: Errno > =
510
- kill ( self . executor . forkserver ( ) . child_pid ( ) , self . signal ) ;
567
+ let _ = kill ( self . executor . forkserver ( ) . child_pid ( ) , self . signal ) ;
511
568
let ( recv_status_len, _) = self . executor . forkserver_mut ( ) . read_st ( ) ?;
512
569
if recv_status_len != 4 {
513
570
return Err ( Error :: unknown ( "Could not kill timed-out child" . to_string ( ) ) ) ;
514
571
}
515
572
exit_kind = ExitKind :: Timeout ;
516
573
}
517
574
518
- self . executor
519
- . forkserver_mut ( )
520
- . set_child_pid ( Pid :: from_raw ( 0 ) ) ;
575
+ self . executor . forkserver_mut ( ) . reset_child_pid ( ) ;
521
576
522
577
Ok ( exit_kind)
523
578
}
@@ -645,6 +700,12 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
645
700
self . use_stdin
646
701
) ;
647
702
703
+ if self . uses_shmem_testcase && map. is_none ( ) {
704
+ return Err ( Error :: illegal_state (
705
+ "Map must always be set for `uses_shmem_testcase`" ,
706
+ ) ) ;
707
+ }
708
+
648
709
Ok ( ForkserverExecutor {
649
710
target,
650
711
args : self . arguments . clone ( ) ,
@@ -690,6 +751,12 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
690
751
691
752
let observers: ( MO , OT ) = other_observers. prepend ( map_observer) ;
692
753
754
+ if self . uses_shmem_testcase && map. is_none ( ) {
755
+ return Err ( Error :: illegal_state (
756
+ "Map must always be set for `uses_shmem_testcase`" ,
757
+ ) ) ;
758
+ }
759
+
693
760
Ok ( ForkserverExecutor {
694
761
target,
695
762
args : self . arguments . clone ( ) ,
@@ -1097,6 +1164,12 @@ where
1097
1164
1098
1165
// Write to testcase
1099
1166
if self . uses_shmem_testcase {
1167
+ debug_assert ! (
1168
+ self . map. is_some( ) ,
1169
+ "The uses_shmem_testcase bool can only exist when a map is set"
1170
+ ) ;
1171
+ // # SAFETY
1172
+ // Struct can never be created when uses_shmem_testcase is true and map is none.
1100
1173
let map = unsafe { self . map . as_mut ( ) . unwrap_unchecked ( ) } ;
1101
1174
let target_bytes = input. target_bytes ( ) ;
1102
1175
let mut size = target_bytes. as_slice ( ) . len ( ) ;
@@ -1120,7 +1193,7 @@ where
1120
1193
1121
1194
let send_len = self
1122
1195
. forkserver
1123
- . write_ctl ( self . forkserver ( ) . last_run_timed_out ( ) ) ?;
1196
+ . write_ctl ( self . forkserver ( ) . last_run_timed_out_raw ( ) ) ?;
1124
1197
if send_len != 4 {
1125
1198
return Err ( Error :: illegal_state (
1126
1199
"Unable to request new process from fork server (OOM?)" . to_string ( ) ,
@@ -1170,7 +1243,7 @@ where
1170
1243
}
1171
1244
}
1172
1245
1173
- self . forkserver . set_child_pid ( Pid :: from_raw ( 0 ) ) ;
1246
+ self . forkserver . reset_child_pid ( ) ;
1174
1247
1175
1248
// Clear the observer map after the execution is finished
1176
1249
compiler_fence ( Ordering :: SeqCst ) ;
0 commit comments