Skip to content

Commit ddd0930

Browse files
authored
Support binding to a single core for ForkserverExecutor (#3236)
* support bind to core for ForkserverExecutor * fix for non-fork platforms * clippy * Remove redundant env
1 parent 4763ada commit ddd0930

File tree

3 files changed

+45
-2
lines changed

3 files changed

+45
-2
lines changed

libafl/src/executors/command.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,17 @@ impl CommandExecutorBuilder {
653653
command.stderr(Stdio::piped());
654654
}
655655

656+
if let Some(core) = self.child_env_inner.core {
657+
#[cfg(feature = "fork")]
658+
command.bind(core);
659+
660+
#[cfg(not(feature = "fork"))]
661+
return Err(Error::illegal_argument(format!(
662+
"Your host doesn't support fork and thus libafl can not bind to core {:?} right after children get spawned",
663+
core
664+
)));
665+
}
666+
656667
let configurator = StdCommandConfigurator {
657668
debug_child: self.child_env_inner.debug_child,
658669
stdout_cap,

libafl/src/executors/forkserver.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use std::{
2222
use libafl_bolts::tuples::{Handle, Handled};
2323
use libafl_bolts::{
2424
AsSlice, AsSliceMut, InputLocation, StdTargetArgs, StdTargetArgsInner, Truncate,
25+
core_affinity::CoreId,
2526
fs::{InputFile, get_unique_std_input_file},
2627
os::{dup2, pipes::Pipe},
2728
shmem::{ShMem, ShMemProvider, UnixShMem, UnixShMemProvider},
@@ -179,6 +180,8 @@ pub trait ConfigTarget {
179180
) -> &mut Self;
180181
/// dup2 the specific fd, used for stdio
181182
fn setdup2(&mut self, old_fd: RawFd, new_fd: RawFd) -> &mut Self;
183+
/// Bind children to a single core
184+
fn bind(&mut self, core: CoreId) -> &mut Self;
182185
}
183186

184187
impl ConfigTarget for Command {
@@ -281,6 +284,19 @@ impl ConfigTarget for Command {
281284
// This calls our non-shady function from above.
282285
unsafe { self.pre_exec(func) }
283286
}
287+
288+
fn bind(&mut self, core: CoreId) -> &mut Self {
289+
let func = move || {
290+
if let Err(e) = core.set_affinity_forced() {
291+
return Err(io::Error::other(e));
292+
}
293+
294+
Ok(())
295+
};
296+
// # Safety
297+
// This calls our non-shady function from above.
298+
unsafe { self.pre_exec(func) }
299+
}
284300
}
285301

286302
/// The [`Forkserver`] is communication channel with a child process that forks on request of the fuzzer.
@@ -365,6 +381,7 @@ impl Forkserver {
365381
stdout_memfd: Option<RawFd>,
366382
stderr_memfd: Option<RawFd>,
367383
cwd: Option<PathBuf>,
384+
core: Option<CoreId>,
368385
) -> Result<Self, Error> {
369386
let Some(coverage_map_size) = coverage_map_size else {
370387
return Err(Error::unknown(
@@ -421,6 +438,10 @@ impl Forkserver {
421438
command.stderr(Stdio::null());
422439
}
423440

441+
if let Some(core) = core {
442+
command.bind(core);
443+
}
444+
424445
command.env(AFL_MAP_SIZE_ENV_VAR, format!("{coverage_map_size}"));
425446

426447
// Persistent, deferred forkserver
@@ -1056,6 +1077,7 @@ where
10561077
.expect("only memory fd backend is allowed for forkserver executor")
10571078
}),
10581079
self.child_env_inner.current_directory.clone(),
1080+
self.child_env_inner.core,
10591081
)?,
10601082
None => {
10611083
return Err(Error::illegal_argument(

libafl/src/executors/mod.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ pub use inprocess::InProcessExecutor;
1616
pub use inprocess_fork::InProcessForkExecutor;
1717
#[cfg(unix)]
1818
use libafl_bolts::os::unix_signals::Signal;
19-
#[cfg(feature = "std")]
20-
use libafl_bolts::tuples::Handle;
2119
use libafl_bolts::tuples::RefIndexable;
20+
#[cfg(feature = "std")]
21+
use libafl_bolts::{core_affinity::CoreId, tuples::Handle};
2222
use serde::{Deserialize, Serialize};
2323
pub use shadow::ShadowExecutor;
2424
pub use with_observers::WithObservers;
@@ -247,6 +247,8 @@ pub struct StdChildArgsInner {
247247
pub current_directory: Option<PathBuf>,
248248
/// Whether debug child by inheriting stdout/stderr
249249
pub debug_child: bool,
250+
/// Core to bind for the children
251+
pub core: Option<CoreId>,
250252
}
251253

252254
#[cfg(feature = "std")]
@@ -258,6 +260,7 @@ impl Default for StdChildArgsInner {
258260
stdout_observer: None,
259261
current_directory: None,
260262
debug_child: false,
263+
core: None,
261264
}
262265
}
263266
}
@@ -312,6 +315,13 @@ pub trait StdChildArgs: Sized {
312315
self.inner_mut().debug_child = debug_child;
313316
self
314317
}
318+
319+
#[must_use]
320+
/// Set the core to bind for the children
321+
fn core(mut self, core: CoreId) -> Self {
322+
self.inner_mut().core = Some(core);
323+
self
324+
}
315325
}
316326

317327
#[cfg(test)]

0 commit comments

Comments
 (0)