New forkserver (#2213)

* step 1

* done

* cmplog?

* targets

* check if working and add env_logger

* typo
This commit is contained in:
Dongjia "toka" Zhang 2024-05-21 15:20:28 +02:00 committed by GitHub
parent b7e10ca7af
commit 19ef29ed60
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 216 additions and 153 deletions

View File

@ -27,6 +27,7 @@ libafl = { path = "../../libafl/" }
libafl_bolts = { path = "../../libafl_bolts/" } libafl_bolts = { path = "../../libafl_bolts/" }
libafl_cc = { path = "../../libafl_cc/" } libafl_cc = { path = "../../libafl_cc/" }
libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer", "pointer_maps"] } libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer", "pointer_maps"] }
env_logger = "0.10"
[lib] [lib]
name = "libforkserver_libafl_cc" name = "libforkserver_libafl_cc"

View File

@ -85,6 +85,8 @@ struct Opt {
#[allow(clippy::similar_names)] #[allow(clippy::similar_names)]
pub fn main() { pub fn main() {
env_logger::init();
const MAP_SIZE: usize = EDGES_MAP_SIZE_IN_USE; //65536; const MAP_SIZE: usize = EDGES_MAP_SIZE_IN_USE; //65536;
let opt = Opt::parse(); let opt = Opt::parse();

View File

@ -16,6 +16,7 @@ codegen-units = 1
opt-level = 3 opt-level = 3
[dependencies] [dependencies]
env_logger = "0.10"
libafl = { path = "../../libafl/", features = ["std", "derive"] } libafl = { path = "../../libafl/", features = ["std", "derive"] }
libafl_bolts = { path = "../../libafl_bolts/" } libafl_bolts = { path = "../../libafl_bolts/" }
clap = { version = "4.0", features = ["derive"] } clap = { version = "4.0", features = ["derive"] }

View File

@ -85,6 +85,7 @@ struct Opt {
#[allow(clippy::similar_names)] #[allow(clippy::similar_names)]
pub fn main() { pub fn main() {
env_logger::init();
const MAP_SIZE: usize = 65536; const MAP_SIZE: usize = 65536;
let opt = Opt::parse(); let opt = Opt::parse();

View File

@ -48,24 +48,56 @@ use crate::{
const FORKSRV_FD: i32 = 198; const FORKSRV_FD: i32 = 198;
#[allow(clippy::cast_possible_wrap)] #[allow(clippy::cast_possible_wrap)]
const FS_OPT_ENABLED: i32 = 0x80000001_u32 as i32; const FS_NEW_ERROR: i32 = 0xeffe0000_u32 as i32;
#[allow(clippy::cast_possible_wrap)]
const FS_OPT_MAPSIZE: i32 = 0x40000000_u32 as i32;
#[allow(clippy::cast_possible_wrap)]
const FS_OPT_SHDMEM_FUZZ: i32 = 0x01000000_u32 as i32;
#[allow(clippy::cast_possible_wrap)]
const FS_OPT_AUTODICT: i32 = 0x10000000_u32 as i32;
// #[allow(clippy::cast_possible_wrap)] const FS_NEW_VERSION_MIN: u32 = 1;
// const FS_OPT_MAX_MAPSIZE: i32 = ((0x00fffffe_u32 >> 1) + 1) as i32; // 8388608 const FS_NEW_VERSION_MAX: u32 = 1;
const fn fs_opt_get_mapsize(x: i32) -> i32 { #[allow(clippy::cast_possible_wrap)]
((x & 0x00fffffe) >> 1) + 1 const FS_NEW_OPT_MAPSIZE: i32 = 1_u32 as i32;
#[allow(clippy::cast_possible_wrap)]
const FS_NEW_OPT_SHDMEM_FUZZ: i32 = 2_u32 as i32;
#[allow(clippy::cast_possible_wrap)]
const FS_NEW_OPT_AUTODICT: i32 = 0x00000800_u32 as i32;
#[allow(clippy::cast_possible_wrap)]
const FS_ERROR_MAP_SIZE: i32 = 1_u32 as i32;
#[allow(clippy::cast_possible_wrap)]
const FS_ERROR_MAP_ADDR: i32 = 2_u32 as i32;
#[allow(clippy::cast_possible_wrap)]
const FS_ERROR_SHM_OPEN: i32 = 4_u32 as i32;
#[allow(clippy::cast_possible_wrap)]
const FS_ERROR_SHMAT: i32 = 8_u32 as i32;
#[allow(clippy::cast_possible_wrap)]
const FS_ERROR_MMAP: i32 = 16_u32 as i32;
#[allow(clippy::cast_possible_wrap)]
const FS_ERROR_OLD_CMPLOG: i32 = 32_u32 as i32;
#[allow(clippy::cast_possible_wrap)]
const FS_ERROR_OLD_CMPLOG_QEMU: i32 = 64_u32 as i32;
fn report_error_and_exit(status: i32) -> Result<(), Error> {
/* Report on the error received via the forkserver controller and exit */
match status {
FS_ERROR_MAP_SIZE =>
Err(Error::unknown(
"AFL_MAP_SIZE is not set and fuzzing target reports that the required size is very large. Solution: Run the fuzzing target stand-alone with the environment variable AFL_DEBUG=1 set and set the value for __afl_final_loc in the AFL_MAP_SIZE environment variable for afl-fuzz.".to_string())),
FS_ERROR_MAP_ADDR =>
Err(Error::unknown(
"the fuzzing target reports that hardcoded map address might be the reason the mmap of the shared memory failed. Solution: recompile the target with either afl-clang-lto and do not set AFL_LLVM_MAP_ADDR or recompile with afl-clang-fast.".to_string())),
FS_ERROR_SHM_OPEN =>
Err(Error::unknown("the fuzzing target reports that the shm_open() call failed.".to_string())),
FS_ERROR_SHMAT =>
Err(Error::unknown("the fuzzing target reports that the shmat() call failed.".to_string())),
FS_ERROR_MMAP =>
Err(Error::unknown("the fuzzing target reports that the mmap() call to the shared memory failed.".to_string())),
FS_ERROR_OLD_CMPLOG =>
Err(Error::unknown(
"the -c cmplog target was instrumented with an too old AFL++ version, you need to recompile it.".to_string())),
FS_ERROR_OLD_CMPLOG_QEMU =>
Err(Error::unknown("The AFL++ QEMU/FRIDA loaders are from an older version, for -c you need to recompile it.".to_string())),
_ =>
Err(Error::unknown(format!("unknown error code {status} from fuzzing target!"))),
}
} }
/* const fn fs_opt_set_mapsize(x: usize) -> usize {
if x <= 1 {
if x > FS_OPT_MAX_MAPSIZE { 0 } else { (x - 1) << 1 }
} else { 0 }
} */
/// The length of header bytes which tells shmem size /// The length of header bytes which tells shmem size
const SHMEM_FUZZ_HDR_SIZE: usize = 4; const SHMEM_FUZZ_HDR_SIZE: usize = 4;
@ -583,7 +615,6 @@ pub struct ForkserverExecutorBuilder<'a, SP> {
shmem_provider: Option<&'a mut SP>, shmem_provider: Option<&'a mut SP>,
max_input_size: usize, max_input_size: usize,
map_size: Option<usize>, map_size: Option<usize>,
real_map_size: i32,
kill_signal: Option<Signal>, kill_signal: Option<Signal>,
timeout: Option<Duration>, timeout: Option<Duration>,
#[cfg(feature = "regex")] #[cfg(feature = "regex")]
@ -755,15 +786,54 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
} }
}; };
let (rlen, status) = forkserver.read_st()?; // Initial handshake, read 4-bytes hello message from the forkserver. let (rlen, version_status) = forkserver.read_st()?; // Initial handshake, read 4-bytes hello message from the forkserver.
if rlen != 4 { if rlen != 4 {
return Err(Error::unknown("Failed to start a forkserver".to_string())); return Err(Error::unknown("Failed to start a forkserver".to_string()));
} }
log::info!("All right - fork server is up.");
if status & FS_OPT_ENABLED == FS_OPT_ENABLED && status & FS_OPT_MAPSIZE == FS_OPT_MAPSIZE { if (version_status & FS_NEW_ERROR) == FS_NEW_ERROR {
let mut map_size = fs_opt_get_mapsize(status); report_error_and_exit(version_status & 0x0000ffff)?;
}
let keep = version_status;
let version: u32 = version_status as u32 - 0x41464c00_u32;
if (0x41464c00..=0x41464cff).contains(&version_status) {
match version {
0 => {
return Err(Error::unknown("Fork server version is not assigned, this should not happen. Recompile target."));
}
FS_NEW_VERSION_MIN..=FS_NEW_VERSION_MAX => {
// good, do nothing
}
_ => {
return Err(Error::unknown(
"Fork server version is not supported. Recompile the target.",
));
}
}
}
let xored_version_status = (version_status as u32 ^ 0xffffffff) as i32;
let send_len = forkserver.write_ctl(xored_version_status)?;
if send_len != 4 {
return Err(Error::unknown("Writing to forkserver failed.".to_string()));
}
log::info!(
"All right - new fork server model version {} is up",
version
);
let (read_len, status) = forkserver.read_st()?;
if read_len != 4 {
return Err(Error::unknown(
"Reading from forkserver failed.".to_string(),
));
}
if status & FS_NEW_OPT_MAPSIZE == FS_NEW_OPT_MAPSIZE {
// When 0, we assume that map_size was filled by the user or const // When 0, we assume that map_size was filled by the user or const
/* TODO autofill map size from the observer /* TODO autofill map size from the observer
@ -771,8 +841,13 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
self.map_size = Some(map_size as usize); self.map_size = Some(map_size as usize);
} }
*/ */
let (read_len, mut map_size) = forkserver.read_st()?;
if read_len != 4 {
return Err(Error::unknown(
"Failed to read map size from forkserver".to_string(),
));
}
self.real_map_size = map_size;
if map_size % 64 != 0 { if map_size % 64 != 0 {
map_size = ((map_size + 63) >> 6) << 6; map_size = ((map_size + 63) >> 6) << 6;
} }
@ -780,68 +855,57 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
// TODO set AFL_MAP_SIZE // TODO set AFL_MAP_SIZE
assert!(self.map_size.is_none() || map_size as usize <= self.map_size.unwrap()); assert!(self.map_size.is_none() || map_size as usize <= self.map_size.unwrap());
// we'll use this later when we truncate the observer
self.map_size = Some(map_size as usize); self.map_size = Some(map_size as usize);
} }
// Only with SHMEM or AUTODICT we can send send_status back or it breaks! if status & FS_NEW_OPT_SHDMEM_FUZZ != 0 {
// If forkserver is responding, we then check if there's any option enabled. if map.is_some() {
// We'll send 4-bytes message back to the forkserver to tell which features to use
// The forkserver is listening to our response if either shmem fuzzing is enabled or auto dict is enabled
// <https://github.com/AFLplusplus/AFLplusplus/blob/147654f8715d237fe45c1657c87b2fe36c4db22a/instrumentation/afl-compiler-rt.o.c#L1026>
if status & FS_OPT_ENABLED == FS_OPT_ENABLED
&& (status & FS_OPT_SHDMEM_FUZZ == FS_OPT_SHDMEM_FUZZ
|| status & FS_OPT_AUTODICT == FS_OPT_AUTODICT)
{
let mut send_status = FS_OPT_ENABLED;
if (status & FS_OPT_SHDMEM_FUZZ == FS_OPT_SHDMEM_FUZZ) && map.is_some() {
log::info!("Using SHARED MEMORY FUZZING feature."); log::info!("Using SHARED MEMORY FUZZING feature.");
send_status |= FS_OPT_SHDMEM_FUZZ;
self.uses_shmem_testcase = true; self.uses_shmem_testcase = true;
} else {
return Err(Error::unknown(
"Target requested sharedmem fuzzing, but you didn't prepare shmem",
));
}
}
if status & FS_NEW_OPT_AUTODICT != 0 {
// Here unlike shmem input fuzzing, we are forced to read things
// hence no self.autotokens.is_some() to check if we proceed
let (read_len, dict_size) = forkserver.read_st()?;
if read_len != 4 {
return Err(Error::unknown(
"Failed to read dictionary size from forkserver".to_string(),
));
} }
if (status & FS_OPT_AUTODICT == FS_OPT_AUTODICT) && self.autotokens.is_some() { if !(2..=0xffffff).contains(&dict_size) {
log::info!("Using AUTODICT feature"); return Err(Error::illegal_state(
send_status |= FS_OPT_AUTODICT; "Dictionary has an illegal size".to_string(),
));
} }
log::info!("Autodict size {dict_size:x}");
let (rlen, buf) = forkserver.read_st_size(dict_size as usize)?;
if send_status != FS_OPT_ENABLED { if rlen != dict_size as usize {
// if send_status is not changed (Options are available but we didn't use any), then don't send the next write_ctl message. return Err(Error::unknown("Failed to load autodictionary".to_string()));
// This is important
let send_len = forkserver.write_ctl(send_status)?;
if send_len != 4 {
return Err(Error::unknown("Writing to forkserver failed.".to_string()));
}
if (send_status & FS_OPT_AUTODICT) == FS_OPT_AUTODICT {
let (read_len, dict_size) = forkserver.read_st()?;
if read_len != 4 {
return Err(Error::unknown(
"Reading from forkserver failed.".to_string(),
));
}
if !(2..=0xffffff).contains(&dict_size) {
return Err(Error::illegal_state(
"Dictionary has an illegal size".to_string(),
));
}
log::info!("Autodict size {dict_size:x}");
let (rlen, buf) = forkserver.read_st_size(dict_size as usize)?;
if rlen != dict_size as usize {
return Err(Error::unknown("Failed to load autodictionary".to_string()));
}
if let Some(t) = &mut self.autotokens {
t.parse_autodict(&buf, dict_size as usize);
}
}
} }
} else { if let Some(t) = &mut self.autotokens {
log::warn!("Forkserver Options are not available."); t.parse_autodict(&buf, dict_size as usize);
}
}
let (read_len, aflx) = forkserver.read_st()?;
if read_len != 4 {
return Err(Error::unknown("Reading from forkserver failed".to_string()));
}
if aflx != version_status {
return Err(Error::unknown(format!(
"Error in forkserver communication ({:x}=>{:x})",
keep, aflx
)));
} }
Ok((forkserver, input_file, map)) Ok((forkserver, input_file, map))
@ -1066,7 +1130,6 @@ impl<'a> ForkserverExecutorBuilder<'a, UnixShMemProvider> {
input_filename: None, input_filename: None,
shmem_provider: None, shmem_provider: None,
map_size: None, map_size: None,
real_map_size: 0,
max_input_size: MAX_INPUT_SIZE_DEFAULT, max_input_size: MAX_INPUT_SIZE_DEFAULT,
kill_signal: None, kill_signal: None,
timeout: None, timeout: None,
@ -1093,7 +1156,6 @@ impl<'a> ForkserverExecutorBuilder<'a, UnixShMemProvider> {
input_filename: self.input_filename, input_filename: self.input_filename,
shmem_provider: Some(shmem_provider), shmem_provider: Some(shmem_provider),
map_size: self.map_size, map_size: self.map_size,
real_map_size: self.real_map_size,
max_input_size: MAX_INPUT_SIZE_DEFAULT, max_input_size: MAX_INPUT_SIZE_DEFAULT,
kill_signal: None, kill_signal: None,
timeout: None, timeout: None,

View File

@ -516,12 +516,10 @@ impl AFLppCmpValuesMetadata {
/// - reserved: Reserved for future use /// - reserved: Reserved for future use
pub struct AFLppCmpLogHeader { pub struct AFLppCmpLogHeader {
/// The header values /// The header values
#[bitfield(name = "hits", ty = "u32", bits = "0..=23")] #[bitfield(name = "hits", ty = "u32", bits = "0..=5")] // 6 bits up to 63 entries, we have CMP_MAP_H = 32 (so using half of it)
#[bitfield(name = "id", ty = "u32", bits = "24..=47")] #[bitfield(name = "shape", ty = "u32", bits = "6..=10")] // 31 + 1 bytes max
#[bitfield(name = "shape", ty = "u32", bits = "48..=52")] #[bitfield(name = "_type", ty = "u8", bits = "11..=11")] // 2: cmp, rtn
#[bitfield(name = "_type", ty = "u32", bits = "53..=54")] #[bitfield(name = "attribute", ty = "u32", bits = "12..=15")]
#[bitfield(name = "attribute", ty = "u32", bits = "55..=58")] // 16 types for arithmetic comparison types
#[bitfield(name = "overflow", ty = "u32", bits = "59..=59")] pub data: [u8; 2],
#[bitfield(name = "reserved", ty = "u32", bits = "60..=63")]
pub data: [u8; 8],
} }

View File

@ -27,10 +27,6 @@
#define CMPLOG_KIND_INS 0 #define CMPLOG_KIND_INS 0
#define CMPLOG_KIND_RTN 1 #define CMPLOG_KIND_RTN 1
// Same, difference between aflpp and libafl
#define AFL_CMP_TYPE_INS 1
#define AFL_CMP_TYPE_RTN 2
typedef struct CmpLogHeader { typedef struct CmpLogHeader {
uint16_t hits; uint16_t hits;
uint8_t shape; uint8_t shape;
@ -39,23 +35,17 @@ typedef struct CmpLogHeader {
#ifndef _WIN32 #ifndef _WIN32
typedef struct CmpLogHeaderExtended { typedef struct CmpLogHeaderExtended {
unsigned hits : 24; unsigned hits : 6;
unsigned id : 24;
unsigned shape : 5; unsigned shape : 5;
unsigned type : 2; unsigned type : 1;
unsigned attribute : 4; unsigned attribute : 4;
unsigned overflow : 1;
unsigned reserved : 4;
} __attribute__((packed)) CmpLogHeaderExtended; } __attribute__((packed)) CmpLogHeaderExtended;
#else #else
__pragma(pack(push, 1)) typedef struct CmpLogHeaderExtended { __pragma(pack(push, 1)) typedef struct CmpLogHeaderExtended {
unsigned hits : 24; unsigned hits : 6;
unsigned id : 24;
unsigned shape : 5; unsigned shape : 5;
unsigned type : 2; unsigned type : 1;
unsigned attribute : 4; unsigned attribute : 4;
unsigned overflow : 1;
unsigned reserved : 4;
} CmpLogHeaderExtended; } CmpLogHeaderExtended;
__pragma(pack(pop)) __pragma(pack(pop))
#endif #endif
@ -146,8 +136,8 @@ static inline void cmplog_instructions_extended_checked(
// printf("%ld %ld %ld\n", k, arg1, arg2); // printf("%ld %ld %ld\n", k, arg1, arg2);
uint16_t hits; uint16_t hits;
if (libafl_cmplog_map_extended_ptr->headers[k].type != AFL_CMP_TYPE_INS) { if (libafl_cmplog_map_extended_ptr->headers[k].type != CMPLOG_KIND_INS) {
libafl_cmplog_map_extended_ptr->headers[k].type = AFL_CMP_TYPE_INS; libafl_cmplog_map_extended_ptr->headers[k].type = CMPLOG_KIND_INS;
libafl_cmplog_map_extended_ptr->headers[k].hits = 1; libafl_cmplog_map_extended_ptr->headers[k].hits = 1;
libafl_cmplog_map_extended_ptr->headers[k].shape = shape; libafl_cmplog_map_extended_ptr->headers[k].shape = shape;
hits = 0; hits = 0;
@ -207,8 +197,8 @@ static inline void cmplog_routines_checked_extended(uintptr_t k,
libafl_cmplog_enabled = false; libafl_cmplog_enabled = false;
uint32_t hits; uint32_t hits;
// printf("RTN: %ld %ld %ld %ld\n", k, *ptr1, *ptr2, len); // printf("RTN: %ld %ld %ld %ld\n", k, *ptr1, *ptr2, len);
if (libafl_cmplog_map_extended_ptr->headers[k].type != AFL_CMP_TYPE_RTN) { if (libafl_cmplog_map_extended_ptr->headers[k].type != CMPLOG_KIND_RTN) {
libafl_cmplog_map_extended_ptr->headers[k].type = AFL_CMP_TYPE_RTN; libafl_cmplog_map_extended_ptr->headers[k].type = CMPLOG_KIND_RTN;
libafl_cmplog_map_extended_ptr->headers[k].hits = 1; libafl_cmplog_map_extended_ptr->headers[k].hits = 1;
libafl_cmplog_map_extended_ptr->headers[k].shape = len; libafl_cmplog_map_extended_ptr->headers[k].shape = len;
hits = 0; hits = 0;

View File

@ -41,11 +41,6 @@ pub const CMPLOG_KIND_INS: u8 = 0;
/// `CmpLog` routine kind /// `CmpLog` routine kind
pub const CMPLOG_KIND_RTN: u8 = 1; pub const CMPLOG_KIND_RTN: u8 = 1;
/// The AFL++ `CMP_TYPE_INS`
pub const AFL_CMP_TYPE_INS: u32 = 1;
/// The AFL++ `CMP_TYPE_RTN`
pub const AFL_CMP_TYPE_RTN: u32 = 2;
// EXTERNS, GLOBALS // EXTERNS, GLOBALS
#[cfg(feature = "cmplog")] #[cfg(feature = "cmplog")]
@ -415,7 +410,7 @@ pub static mut libafl_cmplog_map: CmpLogMap = CmpLogMap {
#[cfg(feature = "cmplog_extended_instrumentation")] #[cfg(feature = "cmplog_extended_instrumentation")]
#[allow(clippy::large_stack_arrays)] #[allow(clippy::large_stack_arrays)]
pub static mut libafl_cmplog_map_extended: AFLppCmpLogMap = AFLppCmpLogMap { pub static mut libafl_cmplog_map_extended: AFLppCmpLogMap = AFLppCmpLogMap {
headers: [AFLppCmpLogHeader { data: [0; 8] }; CMPLOG_MAP_W], headers: [AFLppCmpLogHeader { data: [0; 2] }; CMPLOG_MAP_W],
vals: AFLppCmpLogVals { vals: AFLppCmpLogVals {
operands: [[AFLppCmpLogOperands { operands: [[AFLppCmpLogOperands {
v0: 0, v0: 0,
@ -507,7 +502,7 @@ impl CmpMap for AFLppCmpLogMap {
} }
fn usable_executions_for(&self, idx: usize) -> usize { fn usable_executions_for(&self, idx: usize) -> usize {
if self.headers[idx]._type() == AFL_CMP_TYPE_INS { if self.headers[idx]._type() == CMPLOG_KIND_INS {
if self.executions_for(idx) < CMPLOG_MAP_H { if self.executions_for(idx) < CMPLOG_MAP_H {
self.executions_for(idx) self.executions_for(idx)
} else { } else {
@ -521,7 +516,7 @@ impl CmpMap for AFLppCmpLogMap {
} }
fn values_of(&self, idx: usize, execution: usize) -> Option<CmpValues> { fn values_of(&self, idx: usize, execution: usize) -> Option<CmpValues> {
if self.headers[idx]._type() == AFL_CMP_TYPE_INS { if self.headers[idx]._type() == CMPLOG_KIND_INS {
unsafe { unsafe {
match self.headers[idx].shape() { match self.headers[idx].shape() {
0 => Some(CmpValues::U8(( 0 => Some(CmpValues::U8((
@ -559,7 +554,7 @@ impl CmpMap for AFLppCmpLogMap {
fn reset(&mut self) -> Result<(), Error> { fn reset(&mut self) -> Result<(), Error> {
// For performance, we reset just the headers // For performance, we reset just the headers
self.headers.fill(AFLppCmpLogHeader { data: [0; 8] }); self.headers.fill(AFLppCmpLogHeader { data: [0; 2] });
Ok(()) Ok(())
} }

View File

@ -1,6 +1,7 @@
#include "common.h" #include "common.h"
#include "android-ashmem.h" #include "android-ashmem.h"
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -39,6 +40,11 @@
#define FS_ERROR_OLD_CMPLOG 32 #define FS_ERROR_OLD_CMPLOG 32
#define FS_ERROR_OLD_CMPLOG_QEMU 64 #define FS_ERROR_OLD_CMPLOG_QEMU 64
#define FS_NEW_VERSION_MAX 1
#define FS_NEW_OPT_MAPSIZE 0x1
#define FS_NEW_OPT_SHDMEM_FUZZ 0x2
#define FS_NEW_OPT_AUTODICT 0x800
/* Reporting options */ /* Reporting options */
#define FS_OPT_ENABLED 0x80000001 #define FS_OPT_ENABLED 0x80000001
#define FS_OPT_MAPSIZE 0x40000000 #define FS_OPT_MAPSIZE 0x40000000
@ -219,71 +225,78 @@ void __afl_start_forkserver(void) {
old_sigterm_handler = orig_action.sa_handler; old_sigterm_handler = orig_action.sa_handler;
signal(SIGTERM, at_exit); signal(SIGTERM, at_exit);
uint8_t tmp[4] = {0, 0, 0, 0};
uint32_t status_for_fsrv = 0;
uint32_t already_read_first = 0; uint32_t already_read_first = 0;
uint32_t was_killed; uint32_t was_killed;
uint32_t version = 0x41464c00 + FS_NEW_VERSION_MAX;
uint32_t tmp = version ^ 0xffffffff;
uint32_t status = version;
uint32_t status2 = version;
uint8_t *msg = (uint8_t *)&status;
uint8_t *reply = (uint8_t *)&status2;
uint8_t child_stopped = 0; uint8_t child_stopped = 0;
void (*old_sigchld_handler)(int) = signal(SIGCHLD, SIG_DFL); void (*old_sigchld_handler)(int) = signal(SIGCHLD, SIG_DFL);
if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) {
status_for_fsrv |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE);
}
int autodict_on = __token_start != NULL && __token_stop != NULL; int autodict_on = __token_start != NULL && __token_stop != NULL;
if (autodict_on) { status_for_fsrv |= FS_OPT_AUTODICT; }
if (__afl_sharedmem_fuzzing != 0) { status_for_fsrv |= FS_OPT_SHDMEM_FUZZ; }
if (status_for_fsrv) { status_for_fsrv |= FS_OPT_ENABLED; }
memcpy(tmp, &status_for_fsrv, 4);
/* Phone home and tell the parent that we're OK. If parent isn't there, /* Phone home and tell the parent that we're OK. If parent isn't there,
assume we're not running in forkserver mode and just execute program. */ assume we're not running in forkserver mode and just execute program. */
if (write(FORKSRV_FD + 1, tmp, 4) != 4) { return; } // return because possible non-forkserver usage
if (write(FORKSRV_FD + 1, msg, 4) != 4) { return; }
if (__afl_sharedmem_fuzzing || autodict_on) { if (read(FORKSRV_FD, reply, 4) != 4) { _exit(1); }
if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1);
if ((was_killed & (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) == if (tmp != status2) {
(FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) { write_error("wrong forkserver message from AFL++ tool");
map_input_shared_memory(); _exit(1);
}
status = FS_NEW_OPT_MAPSIZE;
if (__afl_sharedmem_fuzzing) { status |= FS_NEW_OPT_SHDMEM_FUZZ; }
if (autodict_on) { status |= FS_NEW_OPT_AUTODICT; }
if (write(FORKSRV_FD + 1, msg, 4) != 4) { _exit(1); }
// Now send the parameters for the set options, increasing by option number
// FS_NEW_OPT_MAPSIZE - we always send the map size
status = __afl_map_size;
if (write(FORKSRV_FD + 1, msg, 4) != 4) { _exit(1); }
// FS_NEW_OPT_AUTODICT - send autodictionary
if (autodict_on) {
// pass the dictionary through the forkserver FD
uint32_t len = (__token_stop - __token_start), offset = 0;
if (write(FORKSRV_FD + 1, &len, 4) != 4) {
write(2, "Error: could not send dictionary len\n",
strlen("Error: could not send dictionary len\n"));
_exit(1);
} }
if ((was_killed & (FS_OPT_ENABLED | FS_OPT_AUTODICT)) == while (len != 0) {
(FS_OPT_ENABLED | FS_OPT_AUTODICT) && int32_t ret;
autodict_on) { ret = write(FORKSRV_FD + 1, __token_start + offset, len);
// great lets pass the dictionary through the forkserver FD
uint32_t len = (__token_stop - __token_start), offset = 0;
if (write(FORKSRV_FD + 1, &len, 4) != 4) { if (ret < 1) {
write_error("could not send dictionary len"); write_error("could not send dictionary");
_exit(1); _exit(1);
} }
while (len != 0) { len -= ret;
int32_t ret; offset += ret;
ret = write(FORKSRV_FD + 1, __token_start + offset, len);
if (ret < 1) {
write_error("could not send dictionary");
_exit(1);
}
len -= ret;
offset += ret;
}
} else {
// uh this forkserver does not understand extended option passing
// or does not want the dictionary
if (!__afl_fuzz_ptr) already_read_first = 1;
} }
} }
// send welcome message as final message
status = version;
if (write(FORKSRV_FD + 1, msg, 4) != 4) { _exit(1); }
if (__afl_sharedmem_fuzzing) { map_input_shared_memory(); }
while (1) { while (1) {
int status; int status;