From a5ae4c13e11de776779444eb69932802e102d7c4 Mon Sep 17 00:00:00 2001 From: Sergej Schumilo Date: Fri, 21 Jan 2022 04:06:17 +0100 Subject: [PATCH] upgrade libnyx: - add support for latest QEMU-Nyx backend - the size of all SHM buffers are now configurable - new aux buffer layout - remove dead code (fuzz runner) - running multiple instances in parallel works now - improved FFI API - new rust API --- acat/src/main.rs | 2 +- config/src/config.rs | 20 +- config/src/loader.rs | 7 + fuzz_runner/Cargo.toml | 5 +- fuzz_runner/src/forksrv/error.rs | 66 ----- fuzz_runner/src/forksrv/mod.rs | 246 ---------------- fuzz_runner/src/forksrv/newtypes.rs | 37 --- fuzz_runner/src/lib.rs | 211 -------------- fuzz_runner/src/nyx/aux_buffer.rs | 54 ++-- fuzz_runner/src/nyx/mod.rs | 10 +- fuzz_runner/src/nyx/params.rs | 71 +---- fuzz_runner/src/nyx/qemu_process.rs | 305 ++++++++++++++------ libnyx/src/ffi.rs | 205 +++++++++++++ libnyx/src/lib.rs | 430 +++++++++++++++------------- 14 files changed, 715 insertions(+), 954 deletions(-) delete mode 100644 fuzz_runner/src/forksrv/error.rs delete mode 100644 fuzz_runner/src/forksrv/mod.rs delete mode 100644 fuzz_runner/src/forksrv/newtypes.rs create mode 100644 libnyx/src/ffi.rs diff --git a/acat/src/main.rs b/acat/src/main.rs index c2b387d..cf5a30f 100644 --- a/acat/src/main.rs +++ b/acat/src/main.rs @@ -137,7 +137,7 @@ fn main() { .expect("couldn't open aux buffer file"); let aux_buffer = aux_buffer::AuxBuffer::new_readonly(aux_shm_f, true); - aux_buffer.validate_header(); + aux_buffer.validate_header().unwrap(); if matches.is_present("show_all"){ print_aux_buffer(&aux_buffer, &aux_buffer_file.to_string(), true, true, true, true, true, colered_output); diff --git a/config/src/config.rs b/config/src/config.rs index 31af018..ec6f4bc 100644 --- a/config/src/config.rs +++ b/config/src/config.rs @@ -18,13 +18,13 @@ fn into_absolute_path(path_to_sharedir: &str, path_to_file: String) -> String { } } -#[derive(Clone, Copy, Serialize, Deserialize)] +#[derive(Clone, Copy, Serialize, Deserialize, Debug)] pub struct IptFilter { pub a: u64, pub b: u64, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct QemuKernelConfig { pub qemu_binary: String, pub kernel: String, @@ -51,14 +51,14 @@ impl QemuKernelConfig{ } } -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, Debug)] pub enum SnapshotPath { Reuse(String), Create(String), DefaultPath, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct QemuSnapshotConfig { pub qemu_binary: String, pub hda: String, @@ -87,7 +87,7 @@ impl QemuSnapshotConfig{ } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum FuzzRunnerConfig { QemuKernel(QemuKernelConfig), QemuSnapshot(QemuSnapshotConfig), @@ -105,7 +105,7 @@ impl FuzzRunnerConfig{ } } -#[derive(Copy, Clone, Serialize, Deserialize)] +#[derive(Copy, Clone, Serialize, Deserialize, Debug)] #[serde(rename_all = "snake_case")] pub enum SnapshotPlacement { None, @@ -120,14 +120,14 @@ impl std::str::FromStr for SnapshotPlacement { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct FuzzerConfig { pub spec_path: String, pub workdir_path: String, pub bitmap_size: usize, + pub input_buffer_size: usize, pub mem_limit: usize, pub time_limit: Duration, - pub target_binary: Option, pub threads: usize, pub thread_id: usize, pub cpu_pin_start_at: usize, @@ -155,9 +155,9 @@ impl FuzzerConfig{ spec_path: format!("{}/spec.msgp",sharedir), workdir_path: config.workdir_path.or(default.workdir_path).expect("no workdir_path specified"), bitmap_size: config.bitmap_size.or(default.bitmap_size).expect("no bitmap_size specified"), + input_buffer_size: config.input_buffer_size, mem_limit: config.mem_limit.or(default.mem_limit).expect("no mem_limit specified"), time_limit: config.time_limit.or(default.time_limit).expect("no time_limit specified"), - target_binary: config.target_binary.or(default.target_binary), threads: config.threads.or(default.threads).expect("no threads specified"), thread_id: config.thread_id.or(default.thread_id).expect("no thread_id specified"), cpu_pin_start_at: config.cpu_pin_start_at.or(default.cpu_pin_start_at).expect("no cpu_pin_start_at specified"), @@ -178,7 +178,7 @@ impl FuzzerConfig{ } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Config { pub runner: FuzzRunnerConfig, pub fuzz: FuzzerConfig, diff --git a/config/src/loader.rs b/config/src/loader.rs index 2680484..98b91cb 100644 --- a/config/src/loader.rs +++ b/config/src/loader.rs @@ -53,6 +53,9 @@ pub struct FuzzerConfigLoader { pub workdir_path: Option, pub bitmap_size: Option, + + #[serde(default = "default_input_buffer_size")] + pub input_buffer_size: usize, pub mem_limit: Option, pub time_limit: Option, pub target_binary: Option, @@ -66,6 +69,10 @@ pub struct FuzzerConfigLoader { pub exit_after_first_crash: Option, } +fn default_input_buffer_size() -> usize { + 1 << 17 +} + fn default_write_protected_input_buffer() -> bool { false } diff --git a/fuzz_runner/Cargo.toml b/fuzz_runner/Cargo.toml index ed384cd..92df36c 100644 --- a/fuzz_runner/Cargo.toml +++ b/fuzz_runner/Cargo.toml @@ -10,8 +10,6 @@ edition = "2018" nix = "0.17.0" time = "0.2.9" subprocess = "0.2.4" -regex = "1.3.6" -lazy_static = "1.4.0" libc = "0.2.68" quick-error = "*" tempfile = "3.1.0" @@ -22,7 +20,8 @@ byteorder = "*" snafu = "0.6.3" timeout-readwrite = "0.3.1" glob="0.3.0" -hex="0.4.2" config={path="../config"} colored = "2.0.0" derivative = "2.1.1" +fs4 = "0.5.4" + diff --git a/fuzz_runner/src/forksrv/error.rs b/fuzz_runner/src/forksrv/error.rs deleted file mode 100644 index c939e0a..0000000 --- a/fuzz_runner/src/forksrv/error.rs +++ /dev/null @@ -1,66 +0,0 @@ -use nix; -use std; - -quick_error! { - #[derive(Debug, Clone)] - pub enum SpawnError { - Fork(err: nix::Error) { - from() - description("execve failed") - display("execve error: {}", err) - cause(err) - } - Path(desc: String){ - description("Invalid Path") - display("Problem with binary path: {}", desc) - } - - Exec(desc: String){ - description("Execution Failed") - display("Execution failed: {}", desc) - } - - FFINull(err: std::ffi::NulError) { - from() - description("argument/path contained Null byte") - display("Null error: {}", err) - cause(err) - } - DevNull(desc: String){ - description("failed to open /dev/null") - display("failed to open /dev/null: {}", desc) - } - } -} - -pub fn path_err(desc: &str) -> Result { - return Err(SpawnError::Path(desc.into())); -} - -quick_error! { - #[derive(Debug)] - pub enum SubprocessError { - Spawn(err: SpawnError) { - from() - description("spawning failed") - display("spawning failed: {}", err) - cause(err) - } - Unspecific(desc: String){ - description("Subprocess Failed") - display("Subprocess failed: {}", desc) - } - Io(err: std::io::Error){ - from() - cause(err) - } - Unix(err: nix::Error){ - from() - cause(err) - } - } -} - -pub fn descr_err(desc: &str) -> Result { - return Err(SubprocessError::Unspecific(desc.into())); -} diff --git a/fuzz_runner/src/forksrv/mod.rs b/fuzz_runner/src/forksrv/mod.rs deleted file mode 100644 index 2a20772..0000000 --- a/fuzz_runner/src/forksrv/mod.rs +++ /dev/null @@ -1,246 +0,0 @@ -/* -pub mod newtypes; - -use nix::fcntl; -use nix::libc::{ - __errno_location, shmat, shmctl, shmget, strerror, IPC_CREAT, IPC_EXCL, IPC_PRIVATE, IPC_RMID, -}; -use nix::sys::signal::{self, Signal}; -use nix::sys::stat; -use nix::sys::wait::WaitStatus; -use nix::unistd; -use nix::unistd::Pid; -use nix::unistd::{fork, ForkResult}; -use std::ffi::{CStr, CString}; -use std::os::unix::io::AsRawFd; -use std::os::unix::io::RawFd; - -use std::io::BufReader; -use std::ptr; - -use timeout_readwrite::TimeoutReader; - -use byteorder::{LittleEndian, ReadBytesExt}; -use std::fs::File; -use std::os::unix::io::FromRawFd; - -use crate::exitreason::ExitReason; -use newtypes::*; -use snafu::ResultExt; - -extern crate config; -use crate::config::{ForkServerConfig, FuzzerConfig}; - -pub struct ForkServer { - inp_file: File, - ctl_in: File, - shared_data: *mut [u8], - input_data: Vec, - input_size: usize, - st_out: std::io::BufReader>, -} - -impl ForkServer { - pub fn new(cfg: &ForkServerConfig, fuzz_cfg: &FuzzerConfig) -> Self { - let inp_file = tempfile::NamedTempFile::new().expect("couldn't create temp file"); - let (inp_file, in_path) = inp_file - .keep() - .expect("couldn't persists temp file for input"); - let inp_file_path = in_path - .to_str() - .expect("temp path should be unicode!") - .to_string(); - let args = cfg - .args - .iter() - .map(|s| { - if s == "@@" { - inp_file_path.clone() - } else { - s.to_string() - } - }) - .collect::>(); - let (ctl_out, ctl_in) = nix::unistd::pipe().expect("failed to create ctl_pipe"); - let (st_out, st_in) = nix::unistd::pipe().expect("failed to create st_pipe"); - let (shm_file, shared_data) = ForkServer::create_shm(fuzz_cfg.bitmap_size); - - match fork().expect("couldn't fork") { - // Parent returns - ForkResult::Parent { child: _, .. } => { - unistd::close(ctl_out).expect("coulnd't close ctl_out"); - unistd::close(st_in).expect("coulnd't close st_out"); - let mut st_out = BufReader::new(TimeoutReader::new( - unsafe { File::from_raw_fd(st_out) }, - fuzz_cfg.time_limit, - )); - st_out - .read_u32::() - .expect("couldn't read child hello"); - return Self { - inp_file: inp_file, - ctl_in: unsafe { File::from_raw_fd(ctl_in) }, - shared_data: shared_data, - st_out, - input_data: vec![0; cfg.input_size], - input_size: 0, - }; - } - //Child does complex stuff - ForkResult::Child => { - let forkserver_fd = 198; // from AFL config.h - unistd::dup2(ctl_out, forkserver_fd as RawFd) - .expect("couldn't dup2 ctl_our to FROKSRV_FD"); - unistd::dup2(st_in, (forkserver_fd + 1) as RawFd) - .expect("couldn't dup2 ctl_our to FROKSRV_FD+1"); - - unistd::dup2(inp_file.as_raw_fd(), 0).expect("couldn't dup2 input file to stdin"); - unistd::close(inp_file.as_raw_fd()).expect("couldn't close input file"); - - unistd::close(ctl_in).expect("couldn't close ctl_in"); - unistd::close(ctl_out).expect("couldn't close ctl_out"); - unistd::close(st_in).expect("couldn't close ctl_out"); - unistd::close(st_out).expect("couldn't close ctl_out"); - - let path = CString::new(fuzz_cfg.target_binary.as_ref().expect("forkserver requires target path").to_string()) - .expect("binary path must not contain zero"); - let args = args - .into_iter() - .map(|s| CString::new(s).expect("args must not contain zero")) - .collect::>(); - - let shm_id = CString::new(format!("__AFL_SHM_ID={}", shm_file)).unwrap(); - - //Asan options: set asan SIG to 223 and disable leak detection - let asan_settings = - CString::new("ASAN_OPTIONS=exitcode=223,abort_on_erro=true,detect_leaks=0") - .expect("RAND_2089158993"); - - let mut env = vec![shm_id, asan_settings]; - env.extend( - cfg.env - .iter() - .map(|str| CString::new(str.to_string()).unwrap()), - ); - - if cfg.hide_output { - let null = fcntl::open("/dev/null", fcntl::OFlag::O_RDWR, stat::Mode::empty()) - .expect("couldn't open /dev/null"); - unistd::dup2(null, 1 as RawFd).expect("couldn't dup2 /dev/null to stdout"); - unistd::dup2(null, 2 as RawFd).expect("couldn't dup2 /dev/null to stderr"); - unistd::close(null).expect("couldn't close /dev/null"); - } - - let arg_ref = &args.iter().map(|x| x.as_c_str()).collect::>()[..]; - let env_ref = &env.iter().map(|x| x.as_c_str()).collect::>()[..]; - - println!("EXECVE {:?} {:?} {:?}", fuzz_cfg.target_binary, arg_ref, env_ref); - unistd::execve(&path, arg_ref, env_ref).expect("couldn't execve target"); - unreachable!(); - } - } - } - - pub fn run_data(&mut self, data: &[u8]) -> Result { - self.input_data[0..data.len()].copy_from_slice(data); - self.input_size = data.len(); - return self.run(); - } - - pub fn run(&mut self) -> Result { - for i in self.get_bitmap_mut().iter_mut() { - *i = 0; - } - unistd::ftruncate(self.inp_file.as_raw_fd(), 0).context(QemuRunNix { - task: "Couldn't truncate inp_file", - })?; - unistd::lseek(self.inp_file.as_raw_fd(), 0, unistd::Whence::SeekSet).context( - QemuRunNix { - task: "Couldn't seek inp_file", - }, - )?; - unistd::write( - self.inp_file.as_raw_fd(), - &self.input_data[0..self.input_size], - ) - .context(QemuRunNix { - task: "Couldn't write data to inp_file", - })?; - unistd::lseek(self.inp_file.as_raw_fd(), 0, unistd::Whence::SeekSet).context( - QemuRunNix { - task: "Couldn't seek inp_file", - }, - )?; - - unistd::write(self.ctl_in.as_raw_fd(), &[0, 0, 0, 0]).context(QemuRunNix { - task: "Couldn't send start command", - })?; - - let pid = Pid::from_raw(self.st_out.read_i32::().context(QemuRunIO { - task: "Couldn't read target pid", - })?); - - if let Ok(status) = self.st_out.read_i32::() { - return Ok(ExitReason::from_wait_status( - WaitStatus::from_raw(pid, status).expect("402104968"), - )); - } - signal::kill(pid, Signal::SIGKILL).context(QemuRunNix { - task: "Couldn't kill timed out process", - })?; - self.st_out.read_u32::().context(QemuRunIO { - task: "couldn't read timeout exitcode", - })?; - return Ok(ExitReason::Timeout); - } - - pub fn get_bitmap_mut(&mut self) -> &mut [u8] { - unsafe { return &mut *self.shared_data } - } - pub fn get_bitmap(&self) -> &[u8] { - unsafe { return &*self.shared_data } - } - - pub fn get_input_mut(&mut self) -> &mut [u8] { - return &mut self.input_data[..]; - } - - pub fn get_input(&self) -> &[u8] { - return &self.input_data[..]; - } - - pub fn set_input_size(&mut self, size: usize) { - assert!(size <= self.input_data.len()); - self.input_size = size; - } - - fn create_shm(bitmap_size: usize) -> (i32, *mut [u8]) { - unsafe { - let shm_id = shmget(IPC_PRIVATE, bitmap_size, IPC_CREAT | IPC_EXCL | 0o600); - if shm_id < 0 { - panic!( - "shm_id {:?}", - CString::from_raw(strerror(*__errno_location())) - ); - } - - let trace_bits = shmat(shm_id, ptr::null(), 0); - if (trace_bits as isize) < 0 { - panic!( - "shmat {:?}", - CString::from_raw(strerror(*__errno_location())) - ); - } - - let res = shmctl(shm_id, IPC_RMID, 0 as *mut nix::libc::shmid_ds); - if res < 0 { - panic!( - "shmclt {:?}", - CString::from_raw(strerror(*__errno_location())) - ); - } - return (shm_id, trace_bits as *mut [u8; 1 << 16]); - } - } -} -*/ \ No newline at end of file diff --git a/fuzz_runner/src/forksrv/newtypes.rs b/fuzz_runner/src/forksrv/newtypes.rs deleted file mode 100644 index edea5c2..0000000 --- a/fuzz_runner/src/forksrv/newtypes.rs +++ /dev/null @@ -1,37 +0,0 @@ -use snafu::{Backtrace, Snafu}; - -use std::path::PathBuf; - -#[derive(Debug, Snafu)] -#[snafu(visibility = "pub")] -pub enum SubprocessError { - #[snafu(display("Could not handle qemu trace file to {} {}", path.display(), source))] - ReadQemuTrace { - path: PathBuf, - source: std::io::Error, - }, - - #[snafu(display("Could not parse integer in {} {}", line, source))] - ParseIntQemuTrace { - line: String, - source: std::num::ParseIntError, - }, - - #[snafu(display("Could not parse line {}", line))] - ParseLineQemuTrace { line: String, backtrace: Backtrace }, - - #[snafu(display("Qemu did not produce any output"))] - NoQemuOutput { backtrace: Backtrace }, - - #[snafu(display("Could not communicate with QemuForkServer {} {} ", task, source))] - QemuRunNix { task: String, source: nix::Error }, - - #[snafu(display("Could not communicate with QemuForkServer {} {} ", task, source))] - QemuRunIO { - task: String, - source: std::io::Error, - }, - - #[snafu(display("Could not disassemble {}", task))] - DisassemblyError { task: String, backtrace: Backtrace }, -} diff --git a/fuzz_runner/src/lib.rs b/fuzz_runner/src/lib.rs index ed04932..4577f5c 100644 --- a/fuzz_runner/src/lib.rs +++ b/fuzz_runner/src/lib.rs @@ -6,224 +6,13 @@ extern crate snafu; extern crate tempfile; extern crate timeout_readwrite; extern crate config; -#[macro_use] extern crate lazy_static; -extern crate regex; -extern crate hex; pub mod exitreason; pub use exitreason::ExitReason; -pub mod forksrv; //pub use forksrv::ForkServer; pub mod nyx; pub use nyx::QemuProcess; -use std::error::Error; - - -#[derive(Debug,Clone,Eq,PartialEq,Hash)] -pub struct TestInfo { - pub ops_used: u32, - pub exitreason: ExitReason -} - -#[derive(Debug,Clone,Eq,PartialEq,Hash)] -pub enum RedqueenBPType{ - Str, - Cmp, - Sub, -} - -impl RedqueenBPType{ - pub fn new(data:&str) -> Self { - match data { - "STR" => return Self::Str, - "CMP" => return Self::Cmp, - "SUB" => return Self::Sub, - _ => panic!("unknown reqdueen bp type {}",data), - } - } -} - -#[derive(Debug,Clone,Eq,PartialEq,Hash)] -pub struct RedqueenEvent{ - pub addr: u64, - pub bp_type: RedqueenBPType, - pub lhs: Vec, - pub rhs: Vec, - pub imm: bool, -} - -impl RedqueenEvent{ - pub fn new(line: &str) -> Self{ - lazy_static! { - static ref RE : regex::Regex = regex::Regex::new(r"([0-9a-fA-F]+)\s+(CMP|SUB|STR)\s+(\d+)\s+([0-9a-fA-F]+)-([0-9a-fA-F]+)(\sIMM)?").unwrap(); - } - if let Some(mat) = RE.captures(line){ - let addr_s = mat.get(1).unwrap().as_str(); - let type_s = mat.get(2).unwrap().as_str(); - //let bits_s =mat.get(3); - let lhs = mat.get(4).unwrap().as_str(); - let rhs = mat.get(5).unwrap().as_str(); - let imm = mat.get(6).map(|_x| true).unwrap_or(false); - return Self{addr: u64::from_str_radix(addr_s, 16).unwrap(), bp_type: RedqueenBPType::new(type_s), lhs: hex::decode(lhs).unwrap(), rhs: hex::decode(rhs).unwrap(), imm}; - } - panic!("couldn't parse redqueen line {}",line); - } -} - -#[derive(Debug,Clone,Eq,PartialEq,Hash)] -pub struct RedqueenInfo {pub bps: Vec} - -pub struct CFGInfo {} - -pub trait FuzzRunner { - fn run_test(&mut self) -> Result>; - fn run_redqueen(&mut self) -> Result>; - fn run_cfg(&mut self) -> Result>; - - fn run_create_snapshot(&mut self) -> bool; - fn delete_snapshot(&mut self) -> Result<(), Box>; - - fn shutdown(&mut self) -> Result<(), Box>; - - fn input_buffer(&mut self) -> &mut [u8]; - fn bitmap_buffer(&self) -> &[u8]; - fn ijon_max_buffer(&self) -> &[u64]; - fn set_input_size(&mut self, size: usize); - - fn parse_redqueen_data(&self, data: &str) -> RedqueenInfo{ - let bps = data.lines().map(|line| RedqueenEvent::new(line)).collect::>(); - return RedqueenInfo{bps} - } - fn parse_redqueen_file(&self, path: &str) -> RedqueenInfo{ - self.parse_redqueen_data(&std::fs::read_to_string(path).unwrap()) - } -} - -/* -impl FuzzRunner for ForkServer { - fn run_test(&mut self) -> Result> { - self.run().unwrap(); - - return Ok(TestInfo {ops_used: 0, exitreason: ExitReason::FuzzerError}); //TODO fix this! - } - - fn run_redqueen(&mut self) -> Result> { - unreachable!(); - //return Ok(parse_redqueen_file()); - } - - fn run_cfg(&mut self) -> Result> { - unreachable!() - //return Ok(CFGInfo {}); - } - fn run_create_snapshot(&mut self) -> bool{ - unreachable!(); - } - fn delete_snapshot(&mut self) -> Result<(), Box>{ - unreachable!(); - } - - fn shutdown(self) -> Result<(), Box> { - return Ok(()); - } - fn input_buffer(&mut self) -> &mut [u8] { - self.get_input_mut() - } - fn bitmap_buffer(&self) -> &[u8] { - self.get_bitmap() - } - fn ijon_max_buffer(&self) -> &[u64] { - unreachable!(); - } - fn set_input_size(&mut self, size: usize) { - ForkServer::set_input_size(self, size) - } -} -*/ -impl FuzzRunner for QemuProcess { - fn run_test(&mut self) -> Result> { - self.send_payload().unwrap(); - let ops_used = self.feedback_data.shared.interpreter.executed_opcode_num; - if self.aux.result.abort != 0 { - panic!("Abort called!\n"); - } - if self.aux.result.crash_found != 0 { - return Ok(TestInfo {ops_used, exitreason: ExitReason::Crash(self.aux.misc.as_slice().to_vec())}); - } - if self.aux.result.payload_write_attempt_found != 0{ - return Ok(TestInfo {ops_used, exitreason: ExitReason::InvalidWriteToPayload(self.aux.misc.as_slice().to_vec())}); - } - if self.aux.result.timeout_found != 0 { - return Ok(TestInfo {ops_used, exitreason: ExitReason::Timeout}); - } - if self.aux.result.asan_found != 0 { - return Ok(TestInfo {ops_used, exitreason: ExitReason::Asan}); - } - if self.aux.result.success != 0{ - return Ok(TestInfo {ops_used, exitreason: ExitReason::Normal(0)}); - } - println!("unknown exeuction result!!"); - return Ok(TestInfo {ops_used, exitreason: ExitReason::FuzzerError}); - } - - fn run_create_snapshot(&mut self) -> bool{ - assert_eq!(self.aux.result.tmp_snapshot_created,0); - self.send_payload().unwrap(); - return self.aux.result.tmp_snapshot_created == 1; - } - - fn delete_snapshot(&mut self) -> Result<(), Box>{ - if self.aux.result.tmp_snapshot_created != 0 { - self.aux.config.changed = 1; - self.aux.config.discard_tmp_snapshot = 1; - self.send_payload().unwrap(); - if self.aux.result.tmp_snapshot_created != 0 { - println!("AUX BUFFER {:#?}",self.aux); - } - assert_eq!( self.aux.result.tmp_snapshot_created, 0); - } - return Ok(()); - } - - - fn run_redqueen(&mut self) -> Result> { - self.aux.config.changed = 1; - self.aux.config.redqueen_mode=1; - self.send_payload().unwrap(); - self.aux.config.changed = 1; - self.aux.config.redqueen_mode=0; - let rq_file = format!("{}/redqueen_workdir_{}/redqueen_results.txt",self.params.workdir,self.params.qemu_id); - return Ok(self.parse_redqueen_file(&rq_file)); - } - - fn run_cfg(&mut self) -> Result> { - //println!("TRACE!!!!"); - self.aux.config.trace_mode=1; - self.aux.config.changed = 1; - self.send_payload().unwrap(); - self.aux.config.changed = 1; - self.aux.config.trace_mode=0; - return Ok(CFGInfo {}); - } - - fn shutdown(&mut self) -> Result<(), Box> { - self.shutdown(); - return Ok(()); - } - fn input_buffer(&mut self) -> &mut [u8] { - &mut self.payload[..] - } - fn bitmap_buffer(&self) -> &[u8] { - self.bitmap - } - fn ijon_max_buffer(&self) -> &[u64] { - &self.feedback_data.shared.ijon.max_data - } - fn set_input_size(&mut self, _size: usize) { - //self.payload[4..].copy_from_slice(&(size as u32).to_le_bytes()); - } -} diff --git a/fuzz_runner/src/nyx/aux_buffer.rs b/fuzz_runner/src/nyx/aux_buffer.rs index 6fe5a84..c51bcd2 100644 --- a/fuzz_runner/src/nyx/aux_buffer.rs +++ b/fuzz_runner/src/nyx/aux_buffer.rs @@ -12,13 +12,20 @@ use crate::nyx::mem_barrier::mem_barrier; use derivative::Derivative; +/* various Nyx exec codes (aux_buffer.result.exec_result_code) */ +pub const NYX_SUCCESS: u8 = 0; +pub const NYX_CRASH: u8 = 1; +pub const NYX_HPRINTF: u8 = 2; +pub const NYX_TIMEOUT: u8 = 3; +pub const NYX_INPUT_WRITE: u8 = 4; +pub const NYX_ABORT: u8 = 5; const AUX_BUFFER_SIZE: usize = 4096; const AUX_MAGIC: u64 = 0x54502d554d4551_u64; -const QEMU_PT_VERSION: u16 = 2; /* let's start at 1 for the initial version using the aux buffer */ -const QEMU_PT_HASH: u16 = 82; +const QEMU_PT_VERSION: u16 = 3; /* let's start at 1 for the initial version using the aux buffer */ +const QEMU_PT_HASH: u16 = 84; const HEADER_SIZE: usize = 128; const CAP_SIZE: usize = 256; @@ -114,6 +121,10 @@ pub struct auxilary_buffer_cap_s { pub agent_timeout_detection: u8, /* agent implements own timeout detection; host timeout detection is still in used, but treshold is increased by x2; */ pub agent_trace_bitmap: u8, /* agent implements own tracing mechanism; PT tracing is disabled */ pub agent_ijon_trace_bitmap: u8, /* agent uses the ijon shm buffer */ + + pub agent_input_buffer_size: u32, /* agent requests a custom input buffer size (if the size is 0, the minimum buffer size is used) */ + pub agent_coverage_bitmap_size: u32, /* agent requests a custom coverage bitmap size (if the size is 0, the minimum buffer size is used) */ + } #[derive(Debug, Copy, Clone)] #[repr(C, packed(1))] @@ -151,40 +162,23 @@ pub struct auxilary_buffer_result_s { 3 -> ready to fuzz */ pub state: u8, - /* snapshot extension */ + pub exec_done: u8, + pub exec_result_code: u8, + pub reloaded: u8, + + pub pt_overflow: u8, + pub page_not_found: u8, pub tmp_snapshot_created: u8, - - #[derivative(Debug="ignore")] - pub padding_1: u8, - #[derivative(Debug="ignore")] - pub padding_2: u8, - - pub bb_coverage: u32, - #[derivative(Debug="ignore")] pub padding_3: u8, - #[derivative(Debug="ignore")] - pub padding_4: u8, - pub hprintf: u8, - pub exec_done: u8, - pub crash_found: u8, - pub asan_found: u8, - pub timeout_found: u8, - pub reloaded: u8, - pub pt_overflow: u8, - - - pub runtime_sec: u8, - - pub page_not_found: u8, - pub success: u8, - pub runtime_usec: u32, pub page_not_found_addr: u64, pub dirty_pages: u32, - pub pt_trace_size: u32, - pub payload_write_attempt_found: u8, - pub abort: u8, + pub pt_trace_size: u32, + pub bb_coverage: u32, + pub runtime_usec: u32, + pub runtime_sec: u32, + } #[repr(C, packed(1))] diff --git a/fuzz_runner/src/nyx/mod.rs b/fuzz_runner/src/nyx/mod.rs index dbb1448..444262c 100644 --- a/fuzz_runner/src/nyx/mod.rs +++ b/fuzz_runner/src/nyx/mod.rs @@ -40,6 +40,7 @@ pub fn qemu_process_new_from_kernel(sharedir: String, cfg: &QemuKernelConfig, fu write_protected_input_buffer: fuzz_cfg.write_protected_input_buffer, cow_primary_size: fuzz_cfg.cow_primary_size, ipt_filters: fuzz_cfg.ipt_filters, + input_buffer_size: fuzz_cfg.input_buffer_size, }; let qemu_id = fuzz_cfg.thread_id; let qemu_params = params::QemuParams::new_from_kernel(&fuzz_cfg.workdir_path, qemu_id, ¶ms, fuzz_cfg.threads > 1); @@ -82,17 +83,10 @@ pub fn qemu_process_new_from_snapshot(sharedir: String, cfg: &QemuSnapshotConfig write_protected_input_buffer: fuzz_cfg.write_protected_input_buffer, cow_primary_size: fuzz_cfg.cow_primary_size, ipt_filters: fuzz_cfg.ipt_filters, + input_buffer_size: fuzz_cfg.input_buffer_size, }; let qemu_id = fuzz_cfg.thread_id; let qemu_params = params::QemuParams::new_from_snapshot(&fuzz_cfg.workdir_path, qemu_id, fuzz_cfg.cpu_pin_start_at, ¶ms, fuzz_cfg.threads > 1); - - /* - if qemu_id == 0{ - println!("------> WIPING EVERYTHING"); - qemu_process::QemuProcess::prepare_workdir(&fuzz_cfg.workdir_path, fuzz_cfg.seed_pattern.clone()); - println!("------> WIPING EVERYTHING DONE"); - } - */ return qemu_process::QemuProcess::new(qemu_params); } diff --git a/fuzz_runner/src/nyx/params.rs b/fuzz_runner/src/nyx/params.rs index faf184b..df63da5 100644 --- a/fuzz_runner/src/nyx/params.rs +++ b/fuzz_runner/src/nyx/params.rs @@ -1,4 +1,3 @@ -use std::path::Path; use crate::config::SnapshotPath; use crate::config::IptFilter; @@ -15,6 +14,7 @@ pub struct KernelVmParams { pub write_protected_input_buffer: bool, pub cow_primary_size: Option, pub ipt_filters: [IptFilter; 4], + pub input_buffer_size: usize, } pub struct SnapshotVmParams{ @@ -31,16 +31,13 @@ pub struct SnapshotVmParams{ pub write_protected_input_buffer: bool, pub cow_primary_size: Option, pub ipt_filters: [IptFilter; 4], - + pub input_buffer_size: usize, } pub struct QemuParams { pub cmd: Vec, pub qemu_aux_buffer_filename: String, pub control_filename: String, - pub bitmap_filename: String, - pub payload_filename: String, - pub binary_filename: String, pub workdir: String, pub qemu_id: usize, pub bitmap_size: usize, @@ -54,18 +51,8 @@ pub struct QemuParams { impl QemuParams { pub fn new_from_snapshot(workdir: &str, qemu_id: usize, cpu: usize, params: &SnapshotVmParams, create_snapshot_file: bool) -> QemuParams{ - let project_name = Path::new(workdir) - .file_name() - .expect("Couldn't get project name from workdir!") - .to_str() - .expect("invalid chars in workdir path") - .to_string(); - + let qemu_aux_buffer_filename = format!("{}/aux_buffer_{}", workdir, qemu_id); - let payload_filename = format!("/dev/shm/kafl_{}_qemu_payload_{}", project_name, qemu_id); - //let tracedump_filename = format!("/dev/shm/kafl_{}_pt_trace_dump_{}", project_name, qemu_id); - let binary_filename = format!("{}/program", workdir); - let bitmap_filename = format!("/dev/shm/kafl_{}_bitmap_{}", project_name, qemu_id); let control_filename = format!("{}/interface_{}", workdir, qemu_id); let mut cmd = vec![]; @@ -102,15 +89,14 @@ impl QemuParams { cmd.push("-chardev".to_string()); cmd.push(format!( - "socket,server,path={},id=kafl_interface", + "socket,server,path={},id=nyx_interface", control_filename )); - - // -fast_vm_reload path=/tmp/fuzz_workdir/snapshot/,load=off,pre_path=/home/kafl/ubuntu_snapshot cmd.push("-device".to_string()); - let mut nyx_ops = format!("kafl,chardev=kafl_interface"); - nyx_ops += &format!(",bitmap_size={}", params.bitmap_size+0x1000); + let mut nyx_ops = format!("nyx,chardev=nyx_interface"); + nyx_ops += &format!(",bitmap_size={}", params.bitmap_size); + nyx_ops += &format!(",input_buffer_size={}", params.input_buffer_size); nyx_ops += &format!(",worker_id={}", qemu_id); nyx_ops += &format!(",workdir={}", workdir); nyx_ops += &format!(",sharedir={}", params.sharedir); @@ -135,7 +121,6 @@ impl QemuParams { cmd.push("-cpu".to_string()); cmd.push("kAFL64-Hypervisor-v1".to_string()); - //cmd.push("kvm64-v1,".to_string()); match ¶ms.snapshot_path { SnapshotPath::Create(path) => { @@ -155,22 +140,14 @@ impl QemuParams { SnapshotPath::DefaultPath => panic!(), } - /* - bin = read_binary_file("/tmp/zsh_fuzz") - assert(len(bin)<= (128 << 20)) - atomic_write(binary_filename, bin) - */ return QemuParams { cmd, qemu_aux_buffer_filename, control_filename, - bitmap_filename, - payload_filename, - binary_filename, workdir: workdir.to_string(), qemu_id, bitmap_size: params.bitmap_size, - payload_size: (1 << 16), + payload_size: params.input_buffer_size, dump_python_code_for_inputs: params.dump_python_code_for_inputs, write_protected_input_buffer: params.write_protected_input_buffer, cow_primary_size: params.cow_primary_size, @@ -178,20 +155,8 @@ impl QemuParams { } pub fn new_from_kernel(workdir: &str, qemu_id: usize, params: &KernelVmParams, create_snapshot_file: bool) -> QemuParams { - //prepare_working_dir(workdir) - - let project_name = Path::new(workdir) - .file_name() - .expect("Couldn't get project name from workdir!") - .to_str() - .expect("invalid chars in workdir path") - .to_string(); let qemu_aux_buffer_filename = format!("{}/aux_buffer_{}", workdir, qemu_id); - let payload_filename = format!("/dev/shm/kafl_{}_qemu_payload_{}", project_name, qemu_id); - //let tracedump_filename = format!("/dev/shm/kafl_{}_pt_trace_dump_{}", project_name, qemu_id); - let binary_filename = format!("{}/program", workdir); - let bitmap_filename = format!("/dev/shm/kafl_{}_bitmap_{}", project_name, qemu_id); let control_filename = format!("{}/interface_{}", workdir, qemu_id); let mut cmd = vec![]; @@ -228,18 +193,17 @@ impl QemuParams { cmd.push("-m".to_string()); cmd.push(params.ram_size.to_string()); - //cmd.push//("-cdrom".to_string()); - //cmd.push("/home/kafl/rust_dev/nyx/syzkaller_spec/cd.iso".to_string()); cmd.push("-chardev".to_string()); cmd.push(format!( - "socket,server,path={},id=kafl_interface", + "socket,server,path={},id=nyx_interface", control_filename )); cmd.push("-device".to_string()); - let mut nyx_ops = format!("kafl,chardev=kafl_interface"); - nyx_ops += &format!(",bitmap_size={}", params.bitmap_size+0x1000); /* + ijon page */ + let mut nyx_ops = format!("nyx,chardev=nyx_interface"); + nyx_ops += &format!(",bitmap_size={}", params.bitmap_size); + nyx_ops += &format!(",input_buffer_size={}", params.input_buffer_size); nyx_ops += &format!(",worker_id={}", qemu_id); nyx_ops += &format!(",workdir={}", workdir); nyx_ops += &format!(",sharedir={}", params.sharedir); @@ -263,7 +227,6 @@ impl QemuParams { cmd.push("-cpu".to_string()); cmd.push("kAFL64-Hypervisor-v1,+vmx".to_string()); - //cmd.push("kvm64-v1,+vmx".to_string()); if create_snapshot_file { cmd.push("-fast_vm_reload".to_string()); @@ -274,22 +237,14 @@ impl QemuParams { } } - /* - bin = read_binary_file("/tmp/zsh_fuzz") - assert(len(bin)<= (128 << 20)) - atomic_write(binary_filename, bin) - */ return QemuParams { cmd, qemu_aux_buffer_filename, control_filename, - bitmap_filename, - payload_filename, - binary_filename, workdir: workdir.to_string(), qemu_id, bitmap_size: params.bitmap_size, - payload_size: (128 << 10), + payload_size: params.input_buffer_size, dump_python_code_for_inputs: params.dump_python_code_for_inputs, write_protected_input_buffer: params.write_protected_input_buffer, cow_primary_size: params.cow_primary_size, diff --git a/fuzz_runner/src/nyx/qemu_process.rs b/fuzz_runner/src/nyx/qemu_process.rs index b7f0862..ff4e432 100644 --- a/fuzz_runner/src/nyx/qemu_process.rs +++ b/fuzz_runner/src/nyx/qemu_process.rs @@ -1,4 +1,5 @@ use core::ffi::c_void; +use std::path::PathBuf; use nix::sys::mman::*; use std::fs; use std::io; @@ -6,11 +7,16 @@ use std::fs::{File, OpenOptions}; use std::io::prelude::*; use std::os::unix::fs::symlink; use std::os::unix::io::IntoRawFd; +use std::os::unix::io::AsRawFd; use std::os::unix::net::UnixStream; use std::path::Path; use std::process::Child; use std::process::Command; use std::{thread, time}; +use std::process; +use fs4::FileExt; + +use nix::unistd::gettid; use std::str; @@ -20,6 +26,8 @@ use colored::*; use crate::nyx::aux_buffer::AuxBuffer; +use crate::nyx::aux_buffer::{NYX_SUCCESS, NYX_CRASH, NYX_HPRINTF, NYX_TIMEOUT, NYX_ABORT, NYX_INPUT_WRITE}; + use crate::nyx::ijon_data::{SharedFeedbackData, FeedbackBuffer}; use crate::nyx::mem_barrier::mem_barrier; use crate::nyx::params::QemuParams; @@ -28,10 +36,16 @@ pub struct QemuProcess { pub process: Child, pub aux: AuxBuffer, pub feedback_data: FeedbackBuffer, + pub ijon_buffer: &'static mut [u8], pub ctrl: UnixStream, pub bitmap: &'static mut [u8], + pub bitmap_size: usize, + pub input_buffer_size: usize, pub payload: &'static mut [u8], pub params: QemuParams, + shm_work_dir: PathBuf, + #[allow(unused)] + shm_file_lock: File, } fn execute_qemu(ctrl: &mut UnixStream) -> io::Result<()>{ @@ -51,11 +65,11 @@ fn run_qemu(ctrl: &mut UnixStream) -> io::Result<()>{ Ok(()) } -fn make_shared_data(file: File, size: usize) -> &'static mut [u8] { +fn make_shared_data(file: &File, size: usize) -> &'static mut [u8] { let prot = ProtFlags::PROT_READ | ProtFlags::PROT_WRITE; let flags = MapFlags::MAP_SHARED; unsafe { - let ptr = mmap(0 as *mut c_void, size, prot, flags, file.into_raw_fd(), 0).unwrap(); + let ptr = mmap(0 as *mut c_void, size, prot, flags, file.as_raw_fd(), 0).unwrap(); let data = std::slice::from_raw_parts_mut(ptr as *mut u8, size); return data; @@ -66,12 +80,13 @@ fn make_shared_ijon_data(file: File, size: usize) -> FeedbackBuffer { let prot = ProtFlags::PROT_READ | ProtFlags::PROT_WRITE; let flags = MapFlags::MAP_SHARED; unsafe { - let ptr = mmap(std::ptr::null_mut::(), 0x1000, prot, flags, file.into_raw_fd(), size as i64).unwrap(); + let ptr = mmap(std::ptr::null_mut::(), size, prot, flags, file.into_raw_fd(), 0).unwrap(); FeedbackBuffer::new((ptr as *mut SharedFeedbackData).as_mut().unwrap()) } } impl QemuProcess { + pub fn new(params: QemuParams) -> Result { Self::prepare_redqueen_workdir(¶ms.workdir, params.qemu_id); @@ -79,55 +94,79 @@ impl QemuProcess { println!("[!] libnyx: spawning qemu with:\n {}", params.cmd.join(" ")); } + let (shm_work_dir, file_lock) = Self::create_shm_work_dir(); + let mut shm_work_dir_path = PathBuf::from(&shm_work_dir); + + shm_work_dir_path.push("bitmap"); + let bitmap_shm_f = OpenOptions::new() .create(true) .read(true) .write(true) - .open(¶ms.bitmap_filename) + .open(&shm_work_dir_path) .expect("couldn't open bitmap file"); - let mut payload_shm_f = OpenOptions::new() - .create(true) - .read(true) - .write(true) - .open(¶ms.payload_filename) - .expect("couldn't open payload file"); + if Path::new(&format!("{}/bitmap_{}", params.workdir, params.qemu_id)).exists(){ fs::remove_file(format!("{}/bitmap_{}", params.workdir, params.qemu_id)).unwrap(); } symlink( - ¶ms.bitmap_filename, + &shm_work_dir_path, format!("{}/bitmap_{}", params.workdir, params.qemu_id), ) .unwrap(); + shm_work_dir_path.pop(); + shm_work_dir_path.push("ijon"); + + let ijon_buffer_shm_f = OpenOptions::new() + .create(true) + .read(true) + .write(true) + .open(&shm_work_dir_path) + .expect("couldn't open bitmap file"); + + + if Path::new(&format!("{}/ijon_{}", params.workdir, params.qemu_id)).exists(){ + fs::remove_file(format!("{}/ijon_{}", params.workdir, params.qemu_id)).unwrap(); + } + + symlink( + &shm_work_dir_path, + format!("{}/ijon_{}", params.workdir, params.qemu_id), + ) + .unwrap(); + + shm_work_dir_path.pop(); + shm_work_dir_path.push("input"); + + let mut payload_shm_f = OpenOptions::new() + .create(true) + .read(true) + .write(true) + .open(&shm_work_dir_path) + .expect("couldn't open payload file"); + if Path::new(&format!("{}/payload_{}", params.workdir, params.qemu_id)).exists(){ fs::remove_file(format!("{}/payload_{}", params.workdir, params.qemu_id)).unwrap(); } symlink( - ¶ms.payload_filename, + &shm_work_dir_path, format!("{}/payload_{}", params.workdir, params.qemu_id), ) .unwrap(); - //println!("======================================SET NOT_INIT!!!!"); + payload_shm_f.write_all(b"not_init").unwrap(); bitmap_shm_f.set_len(params.bitmap_size as u64).unwrap(); - payload_shm_f.set_len(params.payload_size as u64 + 0x1000).unwrap(); + ijon_buffer_shm_f.set_len(0x1000).unwrap(); - let bitmap_shared = make_shared_data(bitmap_shm_f, params.bitmap_size); - let payload_shared = make_shared_data(payload_shm_f, params.payload_size); + let mut bitmap_shared = make_shared_data(&bitmap_shm_f, params.bitmap_size); + let mut payload_shared = make_shared_data(&payload_shm_f, params.payload_size); - - let bitmap_shm_f = OpenOptions::new() - .create(true) - .read(true) - .write(true) - .open(¶ms.bitmap_filename) - .expect("couldn't open bitmap file"); - - let ijon_shared = make_shared_ijon_data(bitmap_shm_f, params.bitmap_size); + let ijon_shared = make_shared_data(&ijon_buffer_shm_f, 0x1000); + let ijon_feedback_buffer = make_shared_ijon_data(ijon_buffer_shm_f, 0x1000); thread::sleep(time::Duration::from_secs(1)); @@ -154,23 +193,16 @@ impl QemuProcess { thread::sleep(time::Duration::from_millis(200*params.qemu_id as u64)); - //println!("CONNECT TO {}", params.control_filename); - - //control.settimeout(None) maybe needed? - //control.setblocking(1) let mut control = loop { match UnixStream::connect(¶ms.control_filename) { Ok(stream) => break stream, _ => { - //println!("TRY..."); /* broken af */ thread::sleep(time::Duration::from_millis(1)) }, } }; - // dry_run - //println!("TRHEAD {} run QEMU initial",params.qemu_id); if run_qemu(&mut control).is_err() { return Err(format!("cannot launch QEMU-Nyx...")); } @@ -206,30 +238,61 @@ impl QemuProcess { } loop { - if aux_buffer.result.abort == 1 { - let len = aux_buffer.misc.len; - let msg = format!("agent abort() -> \n\t{}", String::from_utf8_lossy(&aux_buffer.misc.data[0..len as usize]).red()); - /* get rid of this process */ - child.kill().unwrap(); - child.wait().unwrap(); + match aux_buffer.result.exec_result_code { + NYX_HPRINTF => { + let len = aux_buffer.misc.len; + print!("{}", String::from_utf8_lossy(&aux_buffer.misc.data[0..len as usize]).yellow()); + }, + NYX_ABORT => { + let len = aux_buffer.misc.len; + let msg = format!("agent abort() -> \n\t{}", String::from_utf8_lossy(&aux_buffer.misc.data[0..len as usize]).red()); - return Err(msg); + /* get rid of this process */ + child.kill().unwrap(); + child.wait().unwrap(); + + return Err(msg); + } + NYX_SUCCESS => {}, + x => { + panic!(" -> unkown type ? {}", x); + } } - if aux_buffer.result.hprintf == 1 { - let len = aux_buffer.misc.len; - print!("{}", String::from_utf8_lossy(&aux_buffer.misc.data[0..len as usize]).yellow()); - } - if aux_buffer.result.state == 3 { break; } if run_qemu(&mut control).is_err(){ return Err(format!("failed to establish fuzzing loop...")); } - //run_qemu(&mut control).unwrap(); } + + let mut bitmap_size = params.bitmap_size as usize; + //println!("[!] libnyx: {:x}", aux_buffer.cap.agent_coverage_bitmap_size); + if aux_buffer.cap.agent_coverage_bitmap_size != 0 { + //let file_len = bitmap_shm_f.metadata().unwrap().len(); + bitmap_size = aux_buffer.cap.agent_coverage_bitmap_size as usize; + if aux_buffer.cap.agent_coverage_bitmap_size as usize > bitmap_shared.len(){ + //println!("[!] libnyx: agent requests a differnt coverage bitmap size: {:x} (current: {:x})", aux_buffer.cap.agent_coverage_bitmap_size as u32, file_len); + bitmap_shared = make_shared_data(&bitmap_shm_f, aux_buffer.cap.agent_coverage_bitmap_size as usize); + } + } + + let mut input_buffer_size = params.payload_size as usize; + if aux_buffer.cap.agent_input_buffer_size != 0 { + input_buffer_size = aux_buffer.cap.agent_input_buffer_size as usize; + if aux_buffer.cap.agent_input_buffer_size as usize > payload_shared.len(){ + payload_shared = make_shared_data(&payload_shm_f, aux_buffer.cap.agent_input_buffer_size as usize); + } + } + + match aux_buffer.cap.agent_trace_bitmap { + 0 => println!("[!] libnyx: coverage mode: Intel-PT (KVM-Nyx and libxdc)"), + 1 => println!("[!] libnyx: coverage mode: compile-time instrumentation"), + _ => panic!("unkown aux_buffer.cap.agent_trace_bitmap value"), + }; + println!("[!] libnyx: qemu #{} is ready:", params.qemu_id); aux_buffer.config.reload_mode = 1; @@ -240,11 +303,16 @@ impl QemuProcess { return Ok(QemuProcess { process: child, aux: aux_buffer, - feedback_data: ijon_shared, + feedback_data: ijon_feedback_buffer, + ijon_buffer: ijon_shared, ctrl: control, bitmap: bitmap_shared, + bitmap_size: bitmap_size, + input_buffer_size: input_buffer_size, payload: payload_shared, params, + shm_work_dir, + shm_file_lock: file_lock, }); } @@ -260,33 +328,48 @@ impl QemuProcess { } mem_barrier(); - if self.aux.result.hprintf == 1 { - let len = self.aux.misc.len; - print!("{}", String::from_utf8_lossy(&self.aux.misc.data[0..len as usize]).yellow()); - continue; - } - - if self.aux.result.abort == 1 { - let len = self.aux.misc.len; - println!("[!] libnyx: agent abort() -> \"{}\"", String::from_utf8_lossy(&self.aux.misc.data[0..len as usize]).red()); - break; - } - - if self.aux.result.success != 0 || self.aux.result.crash_found != 0 || self.aux.result.asan_found != 0 || self.aux.result.payload_write_attempt_found != 0 { - break; - } - if self.aux.result.page_not_found != 0 { let v = self.aux.result.page_not_found_addr; - println!("PAGE NOT FOUND -> {:x}\n", v); - if old_address == self.aux.result.page_not_found_addr { - break; + if old_address != self.aux.result.page_not_found_addr { + //println!("libnyx: page is missing -> {:x}\n", v); + old_address = self.aux.result.page_not_found_addr; + self.aux.config.page_addr = self.aux.result.page_not_found_addr; + self.aux.config.page_dump_mode = 1; + self.aux.config.changed = 1; + + mem_barrier(); + match run_qemu(&mut self.ctrl) { + Err(x) => return Err(x), + Ok(_) => {}, + } + mem_barrier(); + + continue; } - old_address = self.aux.result.page_not_found_addr; - self.aux.config.page_addr = self.aux.result.page_not_found_addr; - self.aux.config.page_dump_mode = 1; - self.aux.config.changed = 1; - } + else{ + println!("libnyx: cannot dump missing page -> {:x}", v); + } + } + + + match self.aux.result.exec_result_code { + NYX_HPRINTF => { + let len = self.aux.misc.len; + print!("{}", String::from_utf8_lossy(&self.aux.misc.data[0..len as usize]).yellow()); + continue; + }, + NYX_ABORT => { + let len = self.aux.misc.len; + println!("[!] libnyx: agent abort() -> \"{}\"", String::from_utf8_lossy(&self.aux.misc.data[0..len as usize]).red()); + break; + }, + NYX_SUCCESS | NYX_CRASH | NYX_INPUT_WRITE | NYX_TIMEOUT => { + break; + }, + x => { + panic!("[!] libnyx: ERROR -> unkown Nyx exec result code: {}", x); + } + } } Ok(()) } @@ -301,10 +384,28 @@ impl QemuProcess { self.process.wait().unwrap(); } + fn remove_shm_work_dir(&mut self){ + + /* move originals into workdir (in case we need the data to debug stuff) */ + let shm_path = self.shm_work_dir.to_str().unwrap(); + fs::remove_file(&format!("{}/bitmap_{}", &self.params.workdir, self.params.qemu_id)).unwrap(); + fs::copy(&format!("{}/bitmap", shm_path), &format!("{}/bitmap_{}", &self.params.workdir, self.params.qemu_id)).unwrap(); + + fs::remove_file(&format!("{}/payload_{}", &self.params.workdir, self.params.qemu_id)).unwrap(); + fs::copy(&format!("{}/input", shm_path), &format!("{}/payload_{}", &self.params.workdir, self.params.qemu_id)).unwrap(); + + fs::remove_file(&format!("{}/ijon_{}", &self.params.workdir, self.params.qemu_id)).unwrap(); + fs::copy(&format!("{}/ijon", shm_path), &format!("{}/ijon_{}", &self.params.workdir, self.params.qemu_id)).unwrap(); + + /* remove this shm directory */ + fs::remove_dir_all(&self.shm_work_dir).unwrap(); + } + pub fn shutdown(&mut self) { println!("[!] libnyx: sending SIGKILL to QEMU-Nyx process..."); self.process.kill().unwrap(); self.wait(); + self.remove_shm_work_dir(); } pub fn wait_for_workdir(workdir: &str){ @@ -314,6 +415,7 @@ impl QemuProcess { "page_cache.lock", "page_cache.addr", "page_cache.addr", + "snapshot/fast_snapshot.qemu_state" ]; for file in files.iter() { while !Path::new(&format!("{}/{}", workdir, file)).exists(){ @@ -326,11 +428,9 @@ impl QemuProcess { Self::clear_workdir(workdir); let folders = vec![ "/corpus/normal", - "/metadata", "/corpus/crash", "/corpus/kasan", "/corpus/timeout", - "/bitmaps", "/imports", "/seeds", "/snapshot", @@ -379,26 +479,59 @@ impl QemuProcess { } fn prepare_redqueen_workdir(workdir: &str, qemu_id: usize) { - //println!("== preparing RQ folder: {}", qemu_id); fs::create_dir_all(format!("{}/redqueen_workdir_{}", workdir, qemu_id)) - .expect("couldn't initialize workdir"); - //println!("== preparing RQ folder: {} DONE", qemu_id); - + .expect("couldn't initialize workdir"); + } + + fn remove_unused_shm_work_dirs(){ + /* find and remove orphaned Nyx shm workdirs in /dev/shm */ + for p in glob::glob(&format!("/dev/shm/nyx_*")).expect("couldn't glob??"){ + let mut path = p.unwrap(); + + path.push("lock"); + if path.exists(){ + + let file_lock = OpenOptions::new() + .read(true) + .open(&path) + .expect("couldn't open shm work dir lock file"); + + path.pop(); + match file_lock.try_lock_exclusive(){ + Ok(_) => { + if path.starts_with("/dev/shm/") { + fs::remove_dir_all(path).unwrap(); + } + }, + Err(_) => {}, + } + } } + } fn clear_workdir(workdir: &str) { let _ = fs::remove_dir_all(workdir); + Self::remove_unused_shm_work_dirs() + } - let project_name = Path::new(workdir) - .file_name() - .expect("Couldn't get project name from workdir!") - .to_str() - .expect("invalid chars in workdir path") - .to_string(); + fn create_shm_work_dir() -> (PathBuf, File) { + let shm_work_dir_path_str = format!("/dev/shm/nyx_{}_{}/", process::id(), gettid()); + let shm_work_dir_path = PathBuf::from(&shm_work_dir_path_str); - for p in glob::glob(&format!("/dev/shm/kafl_{}_*", project_name)).expect("couldn't glob??") - { - fs::remove_file(p.expect("invalid path found")).unwrap(); - } + fs::create_dir_all(&shm_work_dir_path).expect("couldn't initialize shm work directory"); + + let file_lock_path_str = format!("/dev/shm/nyx_{}_{}/lock", process::id(), gettid()); + let file_lock_path = Path::new(&file_lock_path_str); + + let file_lock = OpenOptions::new() + .create(true) + .read(true) + .write(true) + .open(file_lock_path) + .expect("couldn't open shm work dir lock file"); + + file_lock.lock_exclusive().unwrap(); + + (shm_work_dir_path, file_lock) } } diff --git a/libnyx/src/ffi.rs b/libnyx/src/ffi.rs new file mode 100644 index 0000000..a92ff35 --- /dev/null +++ b/libnyx/src/ffi.rs @@ -0,0 +1,205 @@ +use libc::c_char; +use std::ffi::CStr; +use std::ffi::c_void; + +use fuzz_runner::nyx::aux_buffer::{NYX_CRASH, NYX_HPRINTF, NYX_ABORT}; +use super::*; + +#[no_mangle] +pub extern "C" fn nyx_load_config(sharedir: *const c_char) -> *mut c_void { + let sharedir_c_str = unsafe { + assert!(!sharedir.is_null()); + CStr::from_ptr(sharedir) + }; + + let sharedir_r_str = sharedir_c_str.to_str().unwrap(); + + let cfg: NyxConfig = match NyxConfig::load(sharedir_r_str){ + Ok(x) => x, + Err(msg) => { + println!("[!] libnyx config reader error: {}", msg); + return std::ptr::null_mut(); + } + }; + + Box::into_raw(Box::new(cfg)) as *mut c_void +} + +#[no_mangle] +pub extern "C" fn nyx_print_config(config: * mut c_void) { + unsafe{ + assert!(!config.is_null()); + assert!((config as usize) % std::mem::align_of::() == 0); + + let cfg = config as *mut NyxConfig; + println!("{}", *cfg); + } +} + +fn nyx_process_start(sharedir: *const c_char, workdir: *const c_char, worker_id: u32, cpu_id: u32, create_snapshot: bool, input_buffer_size: Option, input_buffer_write_protection: bool) -> * mut NyxProcess { + let sharedir_c_str = unsafe { + assert!(!sharedir.is_null()); + CStr::from_ptr(sharedir) + }; + + let workdir_c_str = unsafe { + assert!(!workdir.is_null()); + CStr::from_ptr(workdir) + }; + + let sharedir_r_str = sharedir_c_str.to_str().unwrap(); + let workdir_r_str = workdir_c_str.to_str().unwrap(); + + match NyxProcess::process_start(sharedir_r_str, workdir_r_str, worker_id, cpu_id, create_snapshot, input_buffer_size, input_buffer_write_protection) { + Ok(x) => Box::into_raw(Box::new(x)), + Err(msg) => { + println!("[!] libnyx failed to initialize QEMU-Nyx: {}", msg); + std::ptr::null_mut() as *mut NyxProcess + }, + } +} + +#[no_mangle] +pub extern "C" fn nyx_new(sharedir: *const c_char, workdir: *const c_char, cpu_id: u32, input_buffer_size: u32, input_buffer_write_protection: bool) -> * mut NyxProcess { + nyx_process_start(sharedir, workdir, 0, cpu_id, false, Some(input_buffer_size), input_buffer_write_protection) +} + + +#[no_mangle] +pub extern "C" fn nyx_new_parent(sharedir: *const c_char, workdir: *const c_char, cpu_id: u32, input_buffer_size: u32, input_buffer_write_protection: bool) -> * mut NyxProcess { + nyx_process_start(sharedir, workdir, 0, cpu_id, true, Some(input_buffer_size), input_buffer_write_protection) +} + +#[no_mangle] +pub extern "C" fn nyx_new_child(sharedir: *const c_char, workdir: *const c_char, cpu_id: u32, worker_id: u32) -> * mut NyxProcess { + if worker_id == 0 { + println!("[!] libnyx failed -> worker_id=0 cannot be used for child processes"); + std::ptr::null_mut() as *mut NyxProcess + } + else{ + nyx_process_start(sharedir, workdir, worker_id, cpu_id, true, None, false) + } +} + + +#[no_mangle] +pub extern "C" fn nyx_get_aux_buffer(nyx_process: * mut NyxProcess) -> *mut u8 { + unsafe{ + assert!(!nyx_process.is_null()); + assert!((nyx_process as usize) % std::mem::align_of::() == 0); + + return (*nyx_process).aux_buffer_as_mut_ptr(); + } +} + +#[no_mangle] +pub extern "C" fn nyx_get_input_buffer(nyx_process: * mut NyxProcess) -> *mut u8 { + unsafe{ + assert!(!nyx_process.is_null()); + assert!((nyx_process as usize) % std::mem::align_of::() == 0); + + return (*nyx_process).input_buffer_mut().as_mut_ptr(); + } +} + +#[no_mangle] +pub extern "C" fn nyx_get_bitmap_buffer(nyx_process: * mut NyxProcess) -> *mut u8 { + unsafe{ + assert!(!nyx_process.is_null()); + assert!((nyx_process as usize) % std::mem::align_of::() == 0); + + return (*nyx_process).bitmap_buffer_mut().as_mut_ptr(); + } +} + +#[no_mangle] +pub extern "C" fn nyx_get_bitmap_buffer_size(nyx_process: * mut NyxProcess) -> usize { + unsafe{ + assert!(!nyx_process.is_null()); + assert!((nyx_process as usize) % std::mem::align_of::() == 0); + //return (*nyx_process).process.bitmap.len(); + return (*nyx_process).bitmap_buffer_size(); + } +} + +#[no_mangle] +pub extern "C" fn nyx_shutdown(nyx_process: * mut NyxProcess) { + unsafe{ + assert!(!nyx_process.is_null()); + assert!((nyx_process as usize) % std::mem::align_of::() == 0); + + (*nyx_process).shutdown(); + } +} + +#[no_mangle] +pub extern "C" fn nyx_option_set_reload_mode(nyx_process: * mut NyxProcess, enable: bool) { + unsafe{ + assert!(!nyx_process.is_null()); + assert!((nyx_process as usize) % std::mem::align_of::() == 0); + + (*nyx_process).option_set_reload_mode(enable); + } +} + +#[no_mangle] +pub extern "C" fn nyx_option_set_timeout(nyx_process: * mut NyxProcess, timeout_sec: u8, timeout_usec: u32) { + unsafe{ + assert!(!nyx_process.is_null()); + assert!((nyx_process as usize) % std::mem::align_of::() == 0); + + (*nyx_process).option_set_timeout(timeout_sec, timeout_usec); + } +} + +#[no_mangle] +pub extern "C" fn nyx_option_apply(nyx_process: * mut NyxProcess) { + unsafe{ + assert!(!nyx_process.is_null()); + assert!((nyx_process as usize) % std::mem::align_of::() == 0); + + (*nyx_process).option_apply(); + } +} + +#[no_mangle] +pub extern "C" fn nyx_exec(nyx_process: * mut NyxProcess) -> NyxReturnValue { + + unsafe{ + assert!(!nyx_process.is_null()); + assert!((nyx_process as usize) % std::mem::align_of::() == 0); + + (*nyx_process).exec() + } +} + +#[no_mangle] +pub extern "C" fn nyx_set_afl_input(nyx_process: * mut NyxProcess, buffer: *mut u8, size: u32) { + + unsafe{ + assert!(!nyx_process.is_null()); + assert!((nyx_process as usize) % std::mem::align_of::() == 0); + assert!((buffer as usize) % std::mem::align_of::() == 0); + + std::ptr::copy(&size, (*nyx_process).process.payload.as_mut_ptr() as *mut u32, 1 as usize); + std::ptr::copy(buffer, (*nyx_process).process.payload[std::mem::size_of::()..].as_mut_ptr(), std::cmp::min(size as usize, (*nyx_process).input_buffer_size())); + } +} + + +#[no_mangle] +pub extern "C" fn nyx_print_aux_buffer(nyx_process: * mut NyxProcess) { + unsafe{ + assert!(!nyx_process.is_null()); + assert!((nyx_process as usize) % std::mem::align_of::() == 0); + + print!("{}", format!("{:#?}", (*nyx_process).process.aux.result)); + + match (*nyx_process).process.aux.result.exec_result_code { + NYX_CRASH | NYX_ABORT | NYX_HPRINTF => { + println!("{}", std::str::from_utf8(&(*nyx_process).process.aux.misc.data).unwrap()); + }, + _ => {}, + } + } +} \ No newline at end of file diff --git a/libnyx/src/lib.rs b/libnyx/src/lib.rs index c82bf90..5dab6a5 100644 --- a/libnyx/src/lib.rs +++ b/libnyx/src/lib.rs @@ -5,235 +5,269 @@ use config::{Config, FuzzRunnerConfig}; use fuzz_runner::nyx::qemu_process_new_from_kernel; use fuzz_runner::nyx::qemu_process_new_from_snapshot; use fuzz_runner::nyx::qemu_process::QemuProcess; +use fuzz_runner::nyx::aux_buffer::{NYX_SUCCESS, NYX_CRASH, NYX_TIMEOUT, NYX_INPUT_WRITE, NYX_ABORT}; -use libc::c_char; -use std::ffi::CStr; +use std::fmt; + +pub mod ffi; #[repr(C)] +#[derive(Debug)] pub enum NyxReturnValue { Normal, Crash, Asan, - Timout, + Timeout, InvalidWriteToPayload, Error, IoError, // QEMU process has died for some reason Abort, // Abort hypercall called } -#[no_mangle] -pub extern "C" fn nyx_new(sharedir: *const c_char, workdir: *const c_char, worker_id: u32, cpu_id: u32, create_snapshot: bool) -> * mut QemuProcess { - let sharedir_c_str = unsafe { - assert!(!sharedir.is_null()); - CStr::from_ptr(sharedir) - }; +pub struct NyxProcess { + process: QemuProcess, +} - let workdir_c_str = unsafe { - assert!(!workdir.is_null()); - CStr::from_ptr(workdir) - }; +#[derive(Clone, Debug)] +pub struct NyxConfig { + config: Config, +} - - let sharedir_r_str = sharedir_c_str.to_str().unwrap(); - let workdir_r_str = workdir_c_str.to_str().unwrap(); - - let cfg: Config = match Config::new_from_sharedir(&sharedir_r_str){ - Ok(x) => x, - Err(msg) => { - println!("[!] libnyx config reader error: {}", msg); - return std::ptr::null_mut() as *mut QemuProcess; +impl NyxConfig { + pub fn load(sharedir: &str) -> Result { + match Config::new_from_sharedir(&sharedir){ + Ok(x) => Ok(NyxConfig{ + config: x + }), + Err(x) => Err(x), } - }; + } - let mut config = cfg.fuzz; - let runner_cfg = cfg.runner; + pub fn qemu_binary_path(&self) -> Option{ + let process_cfg= match self.config.runner.clone() { + FuzzRunnerConfig::QemuKernel(cfg) => cfg, + _ => return None, + }; + return Some(process_cfg.qemu_binary); + } + pub fn kernel_image_path(&self) -> Option{ + let process_cfg= match self.config.runner.clone() { + FuzzRunnerConfig::QemuKernel(cfg) => cfg, + _ => return None, + }; + return Some(process_cfg.kernel); + } - /* todo: add sanity check */ - config.cpu_pin_start_at = cpu_id as usize; + pub fn ramfs_image_path(&self) -> Option{ + let process_cfg= match self.config.runner.clone() { + FuzzRunnerConfig::QemuKernel(cfg) => cfg, + _ => return None, + }; + return Some(process_cfg.ramfs); + } - config.thread_id = worker_id as usize; - config.threads = if create_snapshot { 2 as usize } else { 1 as usize }; + pub fn timeout(&self) -> std::time::Duration { + self.config.fuzz.time_limit + } + pub fn spec_path(&self) -> String{ + self.config.fuzz.spec_path.clone() + } + + pub fn bitmap_size(&self) -> usize{ + self.config.fuzz.bitmap_size + } + + pub fn workdir_path(&self) -> &str { + &self.config.fuzz.workdir_path + } + + pub fn dict(&self) -> Vec> { + self.config.fuzz.dict.clone() + } +} + +impl fmt::Display for NyxConfig { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({:#?})", self.config) + } +} + +impl NyxProcess { + + fn start_process(sharedir: &str, workdir: &str, fuzzer_config: Config, worker_id: u32) -> Result { + + let mut config = fuzzer_config.fuzz; + let runner_cfg = fuzzer_config.runner; - config.workdir_path = format!("{}", workdir_r_str); - - let sdir = sharedir_r_str.clone(); - - if worker_id == 0 { - QemuProcess::prepare_workdir(&config.workdir_path, config.seed_path.clone()); - } - else{ - QemuProcess::wait_for_workdir(&config.workdir_path); - } - - let runner = match runner_cfg.clone() { - FuzzRunnerConfig::QemuSnapshot(cfg) => { - qemu_process_new_from_snapshot(sdir.to_string(), &cfg, &config) - }, - FuzzRunnerConfig::QemuKernel(cfg) => { - qemu_process_new_from_kernel(sdir.to_string(), &cfg, &config) + config.workdir_path = format!("{}", workdir); + + let sdir = sharedir.clone(); + + if worker_id == 0 { + QemuProcess::prepare_workdir(&config.workdir_path, config.seed_path.clone()); + } + else{ + QemuProcess::wait_for_workdir(&config.workdir_path); } - }; - match runner { - Ok(x) => Box::into_raw(Box::new(x)), - Err(msg) => { - println!("[!] libnyx failed to initialize QEMU-Nyx: {}", msg); - std::ptr::null_mut() as *mut QemuProcess - }, + match runner_cfg.clone() { + FuzzRunnerConfig::QemuSnapshot(cfg) => { + qemu_process_new_from_snapshot(sdir.to_string(), &cfg, &config) + }, + FuzzRunnerConfig::QemuKernel(cfg) => { + qemu_process_new_from_kernel(sdir.to_string(), &cfg, &config) + } + } } -} -#[no_mangle] -pub extern "C" fn nyx_get_aux_buffer(qemu_process: * mut QemuProcess) -> *mut u8 { - unsafe{ - assert!(!qemu_process.is_null()); - assert!((qemu_process as usize) % std::mem::align_of::() == 0); - - //return (*qemu_process).aux.get_raw_ptr(); - //return &((*qemu_process).aux.header).as_mut_ptr(); - return std::ptr::addr_of!((*qemu_process).aux.header.magic) as *mut u8; - } -} - -#[no_mangle] -pub extern "C" fn nyx_get_payload_buffer(qemu_process: * mut QemuProcess) -> *mut u8 { - unsafe{ - assert!(!qemu_process.is_null()); - assert!((qemu_process as usize) % std::mem::align_of::() == 0); - - return (*qemu_process).payload.as_mut_ptr(); - } -} - -#[no_mangle] -pub extern "C" fn nyx_get_bitmap_buffer(qemu_process: * mut QemuProcess) -> *mut u8 { - unsafe{ - assert!(!qemu_process.is_null()); - assert!((qemu_process as usize) % std::mem::align_of::() == 0); - - return (*qemu_process).bitmap.as_mut_ptr(); - } -} - -#[no_mangle] -pub extern "C" fn nyx_get_bitmap_buffer_size(qemu_process: * mut QemuProcess) -> usize { - unsafe{ - assert!(!qemu_process.is_null()); - assert!((qemu_process as usize) % std::mem::align_of::() == 0); - return (*qemu_process).bitmap.len(); - } -} - -#[no_mangle] -pub extern "C" fn nyx_shutdown(qemu_process: * mut QemuProcess) { - unsafe{ - assert!(!qemu_process.is_null()); - assert!((qemu_process as usize) % std::mem::align_of::() == 0); - - (*qemu_process).shutdown(); - } -} - -#[no_mangle] -pub extern "C" fn nyx_option_set_reload_mode(qemu_process: * mut QemuProcess, enable: bool) { - unsafe{ - assert!(!qemu_process.is_null()); - assert!((qemu_process as usize) % std::mem::align_of::() == 0); - - (*qemu_process).aux.config.reload_mode = if enable {1} else {0}; - } -} - -#[no_mangle] -pub extern "C" fn nyx_option_set_timeout(qemu_process: * mut QemuProcess, timeout_sec: u8, timeout_usec: u32) { - unsafe{ - assert!(!qemu_process.is_null()); - assert!((qemu_process as usize) % std::mem::align_of::() == 0); - - (*qemu_process).aux.config.timeout_sec = timeout_sec; - (*qemu_process).aux.config.timeout_usec = timeout_usec; - } -} - -#[no_mangle] -pub extern "C" fn nyx_option_apply(qemu_process: * mut QemuProcess) { - unsafe{ - assert!(!qemu_process.is_null()); - assert!((qemu_process as usize) % std::mem::align_of::() == 0); - - (*qemu_process).aux.config.changed = 1; - } -} - -#[no_mangle] -pub extern "C" fn nyx_exec(qemu_process: * mut QemuProcess) -> NyxReturnValue { + fn process_start(sharedir: &str, workdir: &str, worker_id: u32, cpu_id: u32, create_snapshot: bool, input_buffer_size: Option, input_buffer_write_protection: bool) -> Result { + let mut cfg: Config = match Config::new_from_sharedir(&sharedir){ + Ok(x) => x, + Err(msg) => { + return Err(format!("[!] libnyx config reader error: {}", msg)); + } + }; - unsafe{ - assert!(!qemu_process.is_null()); - assert!((qemu_process as usize) % std::mem::align_of::() == 0); + cfg.fuzz.write_protected_input_buffer = input_buffer_write_protection; + + /* todo: add sanity check */ + cfg.fuzz.cpu_pin_start_at = cpu_id as usize; + + match input_buffer_size{ + Some(x) => { cfg.fuzz.input_buffer_size = x as usize; }, + None => {}, + } + + cfg.fuzz.thread_id = worker_id as usize; + cfg.fuzz.threads = if create_snapshot { 2 as usize } else { 1 as usize }; + + cfg.fuzz.workdir_path = format!("{}", workdir); - match (*qemu_process).send_payload(){ - Err(_) => return NyxReturnValue::IoError, + match Self::start_process(sharedir, workdir, cfg, worker_id){ + Ok(x) => Ok(NyxProcess{ + process: x, + }), + Err(x) => Err(x), + } + } + + pub fn from_config(sharedir: &str, config: &NyxConfig, worker_id: u32, create_snapshot: bool) -> Result{ + let workdir = config.config.fuzz.workdir_path.clone(); + + let mut config= config.clone(); + config.config.fuzz.threads = if create_snapshot { 2 as usize } else { 1 as usize }; + config.config.fuzz.thread_id = worker_id as usize; + + match Self::start_process(sharedir, &workdir, config.config.clone(), worker_id) { + Ok(x) => Ok(NyxProcess{ + process: x, + }), + Err(x) => Err(x), + } + } + + pub fn new(sharedir: &str, workdir: &str, cpu_id: u32, input_buffer_size: u32, input_buffer_write_protection: bool) -> Result { + Self::process_start(sharedir, workdir, 0, cpu_id, false, Some(input_buffer_size), input_buffer_write_protection) + } + + pub fn new_parent(sharedir: &str, workdir: &str, cpu_id: u32, input_buffer_size: u32, input_buffer_write_protection: bool) -> Result { + Self::process_start(sharedir, workdir, 0, cpu_id, true, Some(input_buffer_size), input_buffer_write_protection) + } + + pub fn new_child(sharedir: &str, workdir: &str, cpu_id: u32, worker_id: u32) -> Result { + if worker_id == 0 { + println!("[!] libnyx failed -> worker_id=0 cannot be used for child processes"); + Err("worker_id=0 cannot be used for child processes".to_string()) + } + else{ + Self::process_start(sharedir, workdir, worker_id, cpu_id, true, None, false) + } + } + + pub fn aux_buffer_as_mut_ptr(&self) -> *mut u8 { + std::ptr::addr_of!(self.process.aux.header.magic) as *mut u8 + } + + pub fn input_buffer(&self) -> &[u8] { + self.process.payload + } + + pub fn input_buffer_mut(&mut self) -> &mut [u8] { + self.process.payload + } + + pub fn input_buffer_size(&mut self) -> usize { + self.process.payload.len() + } + + pub fn bitmap_buffer(&self) -> &[u8] { + self.process.bitmap + } + + pub fn bitmap_buffer_mut(&mut self) -> &mut [u8] { + self.process.bitmap + } + + pub fn bitmap_buffer_size(&self) -> usize { + self.process.bitmap_size + } + + pub fn ijon_buffer(&self) -> &[u8] { + self.process.ijon_buffer + } + + pub fn shutdown(&mut self) { + self.process.shutdown(); + } + + pub fn option_set_reload_mode(&mut self, enable: bool) { + self.process.aux.config.reload_mode = if enable {1} else {0}; + } + + pub fn option_set_redqueen_mode(&mut self, enable: bool) { + self.process.aux.config.redqueen_mode = if enable {1} else {0}; + } + + pub fn option_set_trace_mode(&mut self, enable: bool) { + self.process.aux.config.trace_mode = if enable {1} else {0}; + } + + pub fn option_set_delete_incremental_snapshot(&mut self, enable: bool) { + self.process.aux.config.discard_tmp_snapshot = if enable {1} else {0}; + } + + pub fn option_set_timeout(&mut self, timeout_sec: u8, timeout_usec: u32) { + self.process.aux.config.timeout_sec = timeout_sec; + self.process.aux.config.timeout_usec = timeout_usec; + } + + pub fn option_apply(&mut self) { + self.process.aux.config.changed = 1; + } + + pub fn aux_misc(&self) -> Vec{ + self.process.aux.misc.as_slice().to_vec() + } + + pub fn aux_tmp_snapshot_created(&self) -> bool { + self.process.aux.result.tmp_snapshot_created != 0 + } + + pub fn exec(&mut self) -> NyxReturnValue { + match self.process.send_payload(){ + Err(_) => NyxReturnValue::IoError, Ok(_) => { - if (*qemu_process).aux.result.abort != 0 { - return NyxReturnValue::Abort; + match self.process.aux.result.exec_result_code { + NYX_SUCCESS => NyxReturnValue::Normal, + NYX_CRASH => NyxReturnValue::Crash, + NYX_TIMEOUT => NyxReturnValue::Timeout, + NYX_INPUT_WRITE => NyxReturnValue::InvalidWriteToPayload, + NYX_ABORT => NyxReturnValue::Abort, + _ => NyxReturnValue::Error, } - if (*qemu_process).aux.result.crash_found != 0 { - return NyxReturnValue::Crash; - } - if (*qemu_process).aux.result.asan_found != 0 { - return NyxReturnValue::Asan; - } - if (*qemu_process).aux.result.timeout_found != 0 { - return NyxReturnValue::Timout; - } - if (*qemu_process).aux.result.payload_write_attempt_found != 0 { - return NyxReturnValue::InvalidWriteToPayload; - } - if (*qemu_process).aux.result.success != 0 { - return NyxReturnValue::Normal; - } - return NyxReturnValue::Error; } } } } - -#[no_mangle] -pub extern "C" fn nyx_set_afl_input(qemu_process: * mut QemuProcess, buffer: *mut u8, size: u32) { - - unsafe{ - assert!(!qemu_process.is_null()); - assert!((qemu_process as usize) % std::mem::align_of::() == 0); - assert!((buffer as usize) % std::mem::align_of::() == 0); - - std::ptr::copy(&size, (*qemu_process).payload.as_mut_ptr() as *mut u32, 1 as usize); - std::ptr::copy(buffer, (*qemu_process).payload[std::mem::size_of::()..].as_mut_ptr(), std::cmp::min(size as usize, 0x10000)); - } -} - - -#[no_mangle] -pub extern "C" fn nyx_print_aux_buffer(qemu_process: * mut QemuProcess) { - unsafe{ - assert!(!qemu_process.is_null()); - assert!((qemu_process as usize) % std::mem::align_of::() == 0); - - print!("{}", format!("{:#?}", (*qemu_process).aux.result)); - if (*qemu_process).aux.result.crash_found != 0 || (*qemu_process).aux.result.asan_found != 0 || (*qemu_process).aux.result.hprintf != 0 { - println!("{}", std::str::from_utf8(&(*qemu_process).aux.misc.data).unwrap()); - } - } -} - - - - -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - } -}