diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 4b14083f6f..8f3a25b874 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -641,7 +641,7 @@ jobs: - name: Clippy run: cargo clippy --tests --all --exclude libafl_nyx --exclude symcc_runtime --exclude runtime_test - android: + ubuntu-cross-android-arm64: runs-on: ubuntu-24.04 steps: - uses: dtolnay/rust-toolchain@stable @@ -657,6 +657,27 @@ jobs: - name: Build Android run: cd libafl && PYO3_CROSS_PYTHON_VERSION=$(python3 -c "print('{}.{}'.format(__import__('sys').version_info.major, __import__('sys').version_info.minor))") cargo ndk -t arm64-v8a build --release + ubuntu-cross-android-x86_64: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - uses: ./.github/workflows/ubuntu-prepare + - uses: Swatinem/rust-cache@v2 + - uses: nttld/setup-ndk@v1 + id: setup-ndk + with: + ndk-version: r27c + add-to-path: false + - name: cargo-ndk + run: cargo install cargo-ndk + - name: cargo android targets + run: | + rustup target add x86_64-linux-android + - name: Build Android + env: + ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} + ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }} + run: cargo ndk -t x86_64 build #run: cargo build --target aarch64-linux-android # TODO: Figure out how to properly build stuff with clang #- name: Add clang path to $PATH env diff --git a/fuzzers/forkserver/forkserver_libafl_cc/src/lib.rs b/fuzzers/forkserver/forkserver_libafl_cc/src/lib.rs index ac4b7e54fb..22ad657413 100644 --- a/fuzzers/forkserver/forkserver_libafl_cc/src/lib.rs +++ b/fuzzers/forkserver/forkserver_libafl_cc/src/lib.rs @@ -1,19 +1,31 @@ +use libafl_bolts::shmem::{ShMemProvider, StdShMemProvider}; use libafl_targets::{ map_input_shared_memory, map_shared_memory, start_forkserver, MaybePersistentForkserverParent, }; #[no_mangle] pub extern "C" fn libafl_start_forkserver() { + let mut shm_provider = match StdShMemProvider::new() { + Ok(shm_provider) => shm_provider, + Err(err) => { + eprintln!("Forkserver failed to create shared memory provider: {err}"); + std::process::exit(1); + } + }; + // Map shared memory region for the edge coverage map - if map_shared_memory().is_err() { + if let Err(err) = map_shared_memory(&mut shm_provider) { + eprintln!("Forkserver failed to create edge map: {err}"); std::process::exit(1); } // Map shared memory region for input and its len - if map_input_shared_memory().is_err() { + if let Err(err) = map_input_shared_memory(&mut shm_provider) { + eprintln!("Forkserver failed to create input map: {err}"); std::process::exit(1); - }; + } // Start the forkserver - if start_forkserver(&mut MaybePersistentForkserverParent::new()).is_err() { + if let Err(err) = start_forkserver(&mut MaybePersistentForkserverParent::new()) { + eprintln!("Forkserver unexpected error: {err}"); std::process::exit(1); - }; + } } diff --git a/fuzzers/forkserver/fuzzbench_forkserver_sand/src/lib.rs b/fuzzers/forkserver/fuzzbench_forkserver_sand/src/lib.rs index ac4b7e54fb..22ad657413 100644 --- a/fuzzers/forkserver/fuzzbench_forkserver_sand/src/lib.rs +++ b/fuzzers/forkserver/fuzzbench_forkserver_sand/src/lib.rs @@ -1,19 +1,31 @@ +use libafl_bolts::shmem::{ShMemProvider, StdShMemProvider}; use libafl_targets::{ map_input_shared_memory, map_shared_memory, start_forkserver, MaybePersistentForkserverParent, }; #[no_mangle] pub extern "C" fn libafl_start_forkserver() { + let mut shm_provider = match StdShMemProvider::new() { + Ok(shm_provider) => shm_provider, + Err(err) => { + eprintln!("Forkserver failed to create shared memory provider: {err}"); + std::process::exit(1); + } + }; + // Map shared memory region for the edge coverage map - if map_shared_memory().is_err() { + if let Err(err) = map_shared_memory(&mut shm_provider) { + eprintln!("Forkserver failed to create edge map: {err}"); std::process::exit(1); } // Map shared memory region for input and its len - if map_input_shared_memory().is_err() { + if let Err(err) = map_input_shared_memory(&mut shm_provider) { + eprintln!("Forkserver failed to create input map: {err}"); std::process::exit(1); - }; + } // Start the forkserver - if start_forkserver(&mut MaybePersistentForkserverParent::new()).is_err() { + if let Err(err) = start_forkserver(&mut MaybePersistentForkserverParent::new()) { + eprintln!("Forkserver unexpected error: {err}"); std::process::exit(1); - }; + } } diff --git a/libafl/src/executors/forkserver.rs b/libafl/src/executors/forkserver.rs index 16b0f964b4..743da6fc55 100644 --- a/libafl/src/executors/forkserver.rs +++ b/libafl/src/executors/forkserver.rs @@ -143,9 +143,11 @@ fn report_error_and_exit(status: i32) -> Result<(), Error> { } /// The length of header bytes which tells shmem size -const SHMEM_FUZZ_HDR_SIZE: usize = 4; -const MAX_INPUT_SIZE_DEFAULT: usize = 1024 * 1024; -const MIN_INPUT_SIZE_DEFAULT: usize = 1; +pub const SHMEM_FUZZ_HDR_SIZE: usize = 4; +/// Maximum default length for input +pub const MAX_INPUT_SIZE_DEFAULT: usize = 1024 * 1024; +/// Minimum default length for input +pub const MIN_INPUT_SIZE_DEFAULT: usize = 1; /// Environment variable key for shared memory id for input and its len pub const SHM_FUZZ_ENV_VAR: &str = "__AFL_SHM_FUZZ_ID"; /// Environment variable key for the page size (at least/usually `testcase_size_max + sizeof::()`) diff --git a/libafl_targets/src/forkserver.rs b/libafl_targets/src/forkserver.rs index 3c9d8e41be..738f374c8c 100644 --- a/libafl_targets/src/forkserver.rs +++ b/libafl_targets/src/forkserver.rs @@ -6,24 +6,31 @@ use std::{ sync::OnceLock, }; +#[cfg(any(target_os = "linux", target_vendor = "apple"))] +use libafl::executors::forkserver::FS_NEW_OPT_AUTODTCT; +#[cfg(feature = "cmplog")] +use libafl::executors::forkserver::SHM_CMPLOG_ENV_VAR; use libafl::{ Error, executors::forkserver::{ - FORKSRV_FD, FS_ERROR_SHM_OPEN, FS_NEW_OPT_AUTODTCT, FS_NEW_OPT_MAPSIZE, - FS_NEW_OPT_SHDMEM_FUZZ, FS_NEW_VERSION_MAX, FS_OPT_ERROR, SHM_CMPLOG_ENV_VAR, SHM_ENV_VAR, - SHM_FUZZ_ENV_VAR, + AFL_MAP_SIZE_ENV_VAR, FORKSRV_FD, FS_ERROR_SHM_OPEN, FS_NEW_OPT_MAPSIZE, + FS_NEW_OPT_SHDMEM_FUZZ, FS_NEW_VERSION_MAX, FS_OPT_ERROR, MAX_INPUT_SIZE_DEFAULT, + SHM_ENV_VAR, SHM_FUZZ_ENV_VAR, SHM_FUZZ_MAP_SIZE_ENV_VAR, SHMEM_FUZZ_HDR_SIZE, }, }; -use libafl_bolts::os::{ChildHandle, ForkResult}; +use libafl_bolts::{ + os::{ChildHandle, ForkResult}, + shmem::{ShMem, ShMemId, ShMemProvider}, +}; use nix::{ sys::signal::{SigHandler, Signal}, unistd::Pid, }; -#[cfg(feature = "cmplog")] -use crate::cmps::CMPLOG_MAP_PTR; #[cfg(feature = "cmplog_extended_instrumentation")] use crate::cmps::EXTENDED_CMPLOG_MAP_PTR; +#[cfg(feature = "cmplog")] +use crate::cmps::{AflppCmpLogMap, CMPLOG_MAP_PTR}; use crate::coverage::{__afl_map_size, EDGES_MAP_PTR, INPUT_LENGTH_PTR, INPUT_PTR, SHM_FUZZING}; #[cfg(any(target_os = "linux", target_vendor = "apple"))] @@ -54,6 +61,7 @@ fn write_to_forkserver(message: &[u8]) -> Result<(), Error> { } Ok(()) } +#[cfg(any(target_os = "linux", target_vendor = "apple"))] fn write_all_to_forkserver(message: &[u8]) -> Result<(), Error> { let mut remain_len = message.len(); while remain_len > 0 { @@ -89,6 +97,39 @@ fn read_u32_from_forkserver() -> Result { Ok(u32::from_ne_bytes(buf)) } +/// Consume current shared memory structure, and get the raw pointer to +/// this shared memory. +/// +/// Note that calling this method will result in a memory leak. +fn shmem_into_raw(shmem: impl ShMem) -> *mut T { + let mut manually_dropped = core::mem::ManuallyDrop::new(shmem); + manually_dropped.as_mut_ptr().cast() +} + +fn map_shared_memory_common( + shmem_provider: &mut SHM, + map_env_var: &str, + map_size_env_var: &str, + map_size_default_fallback: usize, +) -> Result<*mut u8, Error> { + let Ok(id_str) = std::env::var(map_env_var) else { + write_error_to_forkserver(FS_ERROR_SHM_OPEN)?; + return Err(Error::illegal_argument(format!( + "Error: shared memory variable {map_env_var} is not set" + ))); + }; + let map_size = if let Ok(map_size_str) = std::env::var(map_size_env_var) { + map_size_str + .parse() + .map_err(|_| Error::illegal_argument(format!("Invalid {map_size_env_var} value")))? + } else { + map_size_default_fallback + }; + let shmem = shmem_provider.shmem_from_id_and_size(ShMemId::from_string(&id_str), map_size)?; + + Ok(shmem_into_raw(shmem)) +} + /// Guard [`map_shared_memory`] is invoked only once static SHM_MAP_GUARD: OnceLock<()> = OnceLock::new(); @@ -97,31 +138,18 @@ static SHM_MAP_GUARD: OnceLock<()> = OnceLock::new(); /// /// If anything failed, the forkserver will be notified with /// [`FS_ERROR_SHM_OPEN`]. -pub fn map_shared_memory() -> Result<(), Error> { +pub fn map_shared_memory(shmem_provider: &mut SHM) -> Result<(), Error> { if SHM_MAP_GUARD.set(()).is_err() { return Err(Error::illegal_state("shared memory has been mapped before")); } - map_shared_memory_internal() + map_shared_memory_internal(shmem_provider) } -fn map_shared_memory_internal() -> Result<(), Error> { - let Ok(id_str) = std::env::var(SHM_ENV_VAR) else { - write_error_to_forkserver(FS_ERROR_SHM_OPEN)?; - return Err(Error::illegal_argument( - "Error: variable for edge coverage shared memory is not set", - )); - }; - let Ok(shm_id) = id_str.parse() else { - write_error_to_forkserver(FS_ERROR_SHM_OPEN)?; - return Err(Error::illegal_argument("Invalid __AFL_SHM_ID value")); - }; - let map = unsafe { libc::shmat(shm_id, core::ptr::null(), 0) }; - if map.is_null() || core::ptr::eq(map, libc::MAP_FAILED) { - write_error_to_forkserver(FS_ERROR_SHM_OPEN)?; - return Err(Error::illegal_state("shmat for map")); - } +fn map_shared_memory_internal(shmem_provider: &mut SHM) -> Result<(), Error> { + let target_ptr = + map_shared_memory_common(shmem_provider, SHM_ENV_VAR, AFL_MAP_SIZE_ENV_VAR, 65536)?; unsafe { - EDGES_MAP_PTR = map.cast(); + EDGES_MAP_PTR = target_ptr; } Ok(()) } @@ -134,32 +162,23 @@ static INPUT_SHM_MAP_GUARD: OnceLock<()> = OnceLock::new(); /// /// If anything failed, the forkserver will be notified with /// [`FS_ERROR_SHM_OPEN`]. -pub fn map_input_shared_memory() -> Result<(), Error> { +pub fn map_input_shared_memory(shmem_provider: &mut SHM) -> Result<(), Error> { if INPUT_SHM_MAP_GUARD.set(()).is_err() { return Err(Error::illegal_state("shared memory has been mapped before")); } - map_input_shared_memory_internal() + map_input_shared_memory_internal(shmem_provider) } -fn map_input_shared_memory_internal() -> Result<(), Error> { - let Ok(id_str) = std::env::var(SHM_FUZZ_ENV_VAR) else { - write_error_to_forkserver(FS_ERROR_SHM_OPEN)?; - return Err(Error::illegal_argument( - "Error: variable for fuzzing shared memory is not set", - )); - }; - let Ok(shm_id) = id_str.parse() else { - write_error_to_forkserver(FS_ERROR_SHM_OPEN)?; - return Err(Error::illegal_argument("Invalid __AFL_SHM_FUZZ_ID value")); - }; - let map = unsafe { libc::shmat(shm_id, core::ptr::null(), 0) }; - if map.is_null() || core::ptr::eq(map, libc::MAP_FAILED) { - write_error_to_forkserver(FS_ERROR_SHM_OPEN)?; - return Err(Error::illegal_state( - "Could not access fuzzing shared memory", - )); - } - let map: *mut u32 = map.cast(); +fn map_input_shared_memory_internal( + shmem_provider: &mut SHM, +) -> Result<(), Error> { + let target_ptr = map_shared_memory_common( + shmem_provider, + SHM_FUZZ_ENV_VAR, + SHM_FUZZ_MAP_SIZE_ENV_VAR, + MAX_INPUT_SIZE_DEFAULT + SHMEM_FUZZ_HDR_SIZE, + )?; + let map: *mut u32 = target_ptr.cast(); unsafe { INPUT_LENGTH_PTR = map; INPUT_PTR = map.add(1).cast(); @@ -177,36 +196,33 @@ static CMPLOG_SHM_MAP_GUARD: OnceLock<()> = OnceLock::new(); /// If anything failed, the forkserver will be notified with /// [`FS_ERROR_SHM_OPEN`]. #[cfg(feature = "cmplog")] -pub fn map_cmplog_shared_memory() -> Result<(), Error> { +pub fn map_cmplog_shared_memory(shmem_provider: &mut SHM) -> Result<(), Error> { if CMPLOG_SHM_MAP_GUARD.set(()).is_err() { return Err(Error::illegal_state("shared memory has been mapped before")); } - map_cmplog_shared_memory_internal() + map_cmplog_shared_memory_internal(shmem_provider) } #[cfg(feature = "cmplog")] -fn map_cmplog_shared_memory_internal() -> Result<(), Error> { +fn map_cmplog_shared_memory_internal( + shmem_provider: &mut SHM, +) -> Result<(), Error> { let Ok(id_str) = std::env::var(SHM_CMPLOG_ENV_VAR) else { write_error_to_forkserver(FS_ERROR_SHM_OPEN)?; - return Err(Error::illegal_argument( - "Error: variable for cmplog shared memory is not set", - )); + return Err(Error::illegal_argument(format!( + "Error: shared memory variable {SHM_CMPLOG_ENV_VAR} is not set" + ))); }; - let Ok(shm_id) = id_str.parse() else { - write_error_to_forkserver(FS_ERROR_SHM_OPEN)?; - return Err(Error::illegal_argument("Invalid __AFL_CMPLOG_SHM_ID value")); - }; - let map = unsafe { libc::shmat(shm_id, core::ptr::null(), 0) }; - if map.is_null() || core::ptr::eq(map, libc::MAP_FAILED) { - write_error_to_forkserver(FS_ERROR_SHM_OPEN)?; - return Err(Error::illegal_state("shmat for map")); - } + let map_size = size_of::(); + let shmem = shmem_provider.shmem_from_id_and_size(ShMemId::from_string(&id_str), map_size)?; + + let target_ptr = shmem_into_raw(shmem); unsafe { - CMPLOG_MAP_PTR = map.cast(); + CMPLOG_MAP_PTR = target_ptr; } #[cfg(feature = "cmplog_extended_instrumentation")] unsafe { - EXTENDED_CMPLOG_MAP_PTR = map.cast(); + EXTENDED_CMPLOG_MAP_PTR = target_ptr; } Ok(()) }