LetForkserverExecutor being Send (#3242)

* Implement `Send` for `Shm`

it is safe because we take the ownership of the inner map pointer. Only potential
violation is deref the underlying pointer but that’s already unsafe. Therefore,
the properties of Send still hold within the safe world.

* Bump 1.87

* use std::io::pipe so that they are `Send`

* clippy

* upgrade

* Avoid phantomdata to make ForkserverExecutor !Send

* Missing gates

* Fix nostd

* bump in Dockerfile

* use dtolnay/rust-toolchain@stable instead

* setup latest toolchain on non Linux

* Fix typo
This commit is contained in:
lazymio 2025-05-17 20:45:08 +08:00 committed by GitHub
parent ba93e9d2ea
commit 0e9dfd62ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 61 additions and 50 deletions

View File

@ -42,6 +42,8 @@ jobs:
- uses: actions/checkout@v4
- if: runner.os == 'Linux'
uses: ./.github/workflows/ubuntu-prepare
- if: runner.os != 'Linux'
uses: dtolnay/rust-toolchain@stable
- name: Install LLVM
if: runner.os == 'MacOS'
run: brew install llvm@${{env.MAIN_LLVM_VERSION}}
@ -227,8 +229,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: taiki-e/install-action@cargo-hack
- run: rustup upgrade
# Note: We currently only specify minimum rust versions for the default workspace members
- run: cargo hack check --rust-version -p libafl -p libafl_bolts -p libafl_derive -p libafl_cc -p libafl_targets

View File

@ -1,5 +1,5 @@
# syntax=docker/dockerfile:1.2
FROM rust:1.85.0 AS libafl
FROM rust:1.87.0 AS libafl
LABEL "maintainer"="afl++ team <afl@aflplus.plus>"
LABEL "about"="LibAFL Docker image"

View File

@ -12,7 +12,7 @@ readme = "../../README.md"
license = "MIT OR Apache-2.0"
keywords = ["fuzzing", "testing", "security"]
edition = "2024"
rust-version = "1.85"
rust-version = "1.87"
categories = [
"development-tools::testing",
"emulators",

View File

@ -671,7 +671,7 @@ pub struct ForkserverExecutor<I, OT, S, SHM> {
forkserver: Forkserver,
observers: OT,
map: Option<SHM>,
phantom: PhantomData<(I, S)>,
phantom: PhantomData<fn() -> (I, S)>, // For Send/Sync
map_size: Option<usize>,
min_input_size: usize,
max_input_size: usize,

View File

@ -12,7 +12,7 @@ readme = "./README.md"
license = "MIT OR Apache-2.0"
keywords = ["fuzzing", "testing", "security"]
edition = "2024"
rust-version = "1.85"
rust-version = "1.87"
categories = [
"development-tools::testing",
"emulators",

View File

@ -1,41 +1,55 @@
//! Unix `pipe` wrapper for `LibAFL`
#[cfg(feature = "std")]
use alloc::rc::Rc;
#[cfg(feature = "std")]
use core::{borrow::Borrow, cell::RefCell};
#[cfg(feature = "std")]
use std::{
io::{self, ErrorKind, Read, Write},
os::{
fd::{AsFd, AsRawFd, OwnedFd},
unix::io::RawFd,
},
io::{self, ErrorKind, PipeReader, PipeWriter, Read, Write},
os::unix::io::RawFd,
};
#[cfg(feature = "std")]
use nix::unistd::{pipe, read, write};
#[cfg(feature = "std")]
use crate::Error;
/// A unix pipe wrapper for `LibAFL`
#[cfg(feature = "std")]
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct Pipe {
/// The read end of the pipe
read_end: Option<Rc<RefCell<OwnedFd>>>,
read_end: Option<PipeReader>,
/// The write end of the pipe
write_end: Option<Rc<RefCell<OwnedFd>>>,
write_end: Option<PipeWriter>,
}
#[cfg(feature = "std")]
impl Clone for Pipe {
fn clone(&self) -> Self {
// try_clone only fails if we run out of fds (dup2) so this should be rather safe
let read_end = self
.read_end
.as_ref()
.map(PipeReader::try_clone)
.transpose()
.expect("fail to clone read_end");
let write_end = self
.write_end
.as_ref()
.map(PipeWriter::try_clone)
.transpose()
.expect("fail to clone read_end");
Self {
read_end,
write_end,
}
}
}
#[cfg(feature = "std")]
impl Pipe {
/// Create a new `Unix` pipe
pub fn new() -> Result<Self, Error> {
let (read_end, write_end) = pipe()?;
let (read_end, write_end) = io::pipe()?;
Ok(Self {
read_end: Some(Rc::new(RefCell::new(read_end))),
write_end: Some(Rc::new(RefCell::new(write_end))),
read_end: Some(read_end),
write_end: Some(write_end),
})
}
@ -54,19 +68,13 @@ impl Pipe {
/// The read end
#[must_use]
pub fn read_end(&self) -> Option<RawFd> {
self.read_end.as_ref().map(|fd| {
let borrowed: &RefCell<OwnedFd> = fd.borrow();
borrowed.borrow().as_raw_fd()
})
self.read_end.as_ref().map(std::os::fd::AsRawFd::as_raw_fd)
}
/// The write end
#[must_use]
pub fn write_end(&self) -> Option<RawFd> {
self.write_end.as_ref().map(|fd| {
let borrowed: &RefCell<OwnedFd> = fd.borrow();
borrowed.borrow().as_raw_fd()
})
self.write_end.as_ref().map(std::os::fd::AsRawFd::as_raw_fd)
}
}
@ -74,11 +82,8 @@ impl Pipe {
impl Read for Pipe {
/// Reads a few bytes
fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
match self.read_end() {
Some(read_end) => match read(read_end, buf) {
Ok(res) => Ok(res),
Err(e) => Err(io::Error::from_raw_os_error(e as i32)),
},
match self.read_end.as_mut() {
Some(read_end) => read_end.read(buf),
None => Err(io::Error::new(
ErrorKind::BrokenPipe,
"Read pipe end was already closed",
@ -91,14 +96,8 @@ impl Read for Pipe {
impl Write for Pipe {
/// Writes a few bytes
fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
match self.write_end.as_ref() {
Some(write_end) => {
let borrowed: &RefCell<OwnedFd> = write_end;
match write((*borrowed).borrow().as_fd(), buf) {
Ok(res) => Ok(res),
Err(e) => Err(io::Error::from_raw_os_error(e as i32)),
}
}
match self.write_end.as_mut() {
Some(write_end) => Ok(write_end.write(buf)?),
None => Err(io::Error::new(
ErrorKind::BrokenPipe,
"Write pipe end was already closed",

View File

@ -715,6 +715,8 @@ pub mod unix_shmem {
shm_fd: c_int,
}
unsafe impl Send for MmapShMem {}
impl MmapShMem {
/// Create a new [`MmapShMem`]
///
@ -1036,6 +1038,8 @@ pub mod unix_shmem {
map_size: usize,
}
unsafe impl Send for CommonUnixShMem {}
impl CommonUnixShMem {
/// Create a new shared memory mapping, using shmget/shmat
pub fn new(map_size: usize) -> Result<Self, Error> {
@ -1189,6 +1193,8 @@ pub mod unix_shmem {
map_size: usize,
}
unsafe impl Send for AshmemShMem {}
#[allow(non_camel_case_types)] // expect somehow breaks here
#[derive(Copy, Clone)]
#[repr(C)]
@ -1409,6 +1415,8 @@ pub mod unix_shmem {
map_size: usize,
}
unsafe impl Send for MemfdShMem {}
impl MemfdShMem {
/// Create a new shared memory mapping, using shmget/shmat
pub fn new(map_size: usize) -> Result<Self, Error> {
@ -1612,6 +1620,8 @@ pub mod win32_shmem {
map_size: usize,
}
unsafe impl Send for Win32ShMem {}
impl Debug for Win32ShMem {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("Win32ShMem")

View File

@ -9,7 +9,7 @@ readme = "README.md"
license = "MIT OR Apache-2.0"
keywords = ["fuzzing", "testing", "compiler"]
edition = "2024"
rust-version = "1.85"
rust-version = "1.87"
categories = [
"development-tools::testing",
"emulators",

View File

@ -2,7 +2,7 @@
name = "runtime_test"
version.workspace = true
edition = "2024"
rust-version = "1.85"
rust-version = "1.87"
authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"]
description = "Runtime test of LibAFL fuzzing with symbolic execution"
documentation = "https://docs.rs/libafl"

View File

@ -9,7 +9,7 @@ readme = "../README.md"
license = "MIT OR Apache-2.0"
keywords = ["fuzzing", "testing"]
edition = "2024"
rust-version = "1.85"
rust-version = "1.87"
categories = [
"development-tools::testing",
"emulators",

View File

@ -7,7 +7,7 @@ readme = "../README.md"
license = "MIT OR Apache-2.0"
keywords = ["fuzzing", "testing", "security"]
edition = "2024"
rust-version = "1.85"
rust-version = "1.87"
categories = ["development-tools::testing"]
include = [

View File

@ -6,7 +6,7 @@ resolver = "2"
version = "0.15.2"
license = "MIT OR Apache-2.0"
edition = "2024"
rust-version = "1.85"
rust-version = "1.87"
[profile.dev]
panic = "abort"

View File

@ -9,7 +9,7 @@ readme = "../README.md"
license = "MIT OR Apache-2.0"
keywords = ["fuzzing", "testing"]
edition = "2024"
rust-version = "1.85"
rust-version = "1.87"
categories = [
"development-tools::testing",
"emulators",