From a199ed31e7ff325488f6cde762b85c835ef32329 Mon Sep 17 00:00:00 2001 From: Sergej Schumilo Date: Sat, 18 Dec 2021 15:10:39 +0100 Subject: [PATCH] add AFL++ support and other improvements --- config/src/config.rs | 47 +++++++-- config/src/loader.rs | 16 +++ fuzz_runner/src/lib.rs | 13 ++- fuzz_runner/src/nyx/aux_buffer.rs | 20 ++-- fuzz_runner/src/nyx/mod.rs | 6 +- fuzz_runner/src/nyx/params.rs | 29 ++++-- fuzz_runner/src/nyx/qemu_process.rs | 151 +++++++++++++++------------- libnyx/src/lib.rs | 91 +++++++++++------ 8 files changed, 243 insertions(+), 130 deletions(-) diff --git a/config/src/config.rs b/config/src/config.rs index 0fce309..31af018 100644 --- a/config/src/config.rs +++ b/config/src/config.rs @@ -18,6 +18,12 @@ fn into_absolute_path(path_to_sharedir: &str, path_to_file: String) -> String { } } +#[derive(Clone, Copy, Serialize, Deserialize)] +pub struct IptFilter { + pub a: u64, + pub b: u64, +} + #[derive(Clone)] pub struct QemuKernelConfig { pub qemu_binary: String, @@ -132,6 +138,7 @@ pub struct FuzzerConfig { pub exit_after_first_crash: bool, pub write_protected_input_buffer: bool, pub cow_primary_size: Option, + pub ipt_filters: [IptFilter;4], } impl FuzzerConfig{ pub fn new_from_loader(sharedir: &str, default: FuzzerConfigLoader, config: FuzzerConfigLoader) -> Self { @@ -161,6 +168,12 @@ impl FuzzerConfig{ exit_after_first_crash: config.exit_after_first_crash.unwrap_or(default.exit_after_first_crash.unwrap_or(false)), write_protected_input_buffer: config.write_protected_input_buffer, cow_primary_size: if config.cow_primary_size != 0 { Some( config.cow_primary_size as u64) } else { None }, + ipt_filters: [ + config.ip0, + config.ip1, + config.ip2, + config.ip3, + ], } } } @@ -179,18 +192,38 @@ impl Config{ } } - pub fn new_from_sharedir(sharedir: &str) -> Self { + pub fn new_from_sharedir(sharedir: &str) -> Result { let path_to_config = format!("{}/config.ron", sharedir); - let cfg_file = File::open(&path_to_config).expect("could not open config file"); - let mut cfg: ConfigLoader = ron::de::from_reader(cfg_file).unwrap(); - let default_path = into_absolute_path(sharedir, cfg.include_default_config_path.unwrap()); + let cfg_file = match File::open(&path_to_config){ + Ok(x) => {x}, + Err(_) => return Err(format!("file or folder not found ({})!", path_to_config)), + }; + + let mut cfg: ConfigLoader = match ron::de::from_reader(cfg_file){ + Ok(x) => {x}, + Err(x) => return Err(format!("invalid configuration ({})!", x)), + }; + + let include_default_config_path = match cfg.include_default_config_path{ + Some(x) => {x}, + None => return Err(format!("no path to default configuration given!")), + }; + + let default_path = into_absolute_path(sharedir, include_default_config_path); let default_config_folder = Path::new(&default_path).parent().unwrap().to_str().unwrap(); cfg.include_default_config_path = Some(default_path.clone()); - let default_file = File::open(cfg.include_default_config_path.as_ref().expect("no default config given")).expect("could not open config file"); - let default: ConfigLoader = ron::de::from_reader(default_file).unwrap(); + let default_file = match File::open(default_path.clone()){ + Ok(x) => x, + Err(_) => return Err(format!("default config not found ({})!", default_path)), + }; + + let default: ConfigLoader = match ron::de::from_reader(default_file){ + Ok(x) => {x}, + Err(x) => return Err(format!("invalid default configuration ({})!", x)), + }; - Self::new_from_loader(&sharedir, &default_config_folder, default, cfg) + Ok(Self::new_from_loader(&sharedir, &default_config_folder, default, cfg)) } } diff --git a/config/src/loader.rs b/config/src/loader.rs index 6a25f59..2680484 100644 --- a/config/src/loader.rs +++ b/config/src/loader.rs @@ -42,6 +42,15 @@ pub struct FuzzerConfigLoader { #[serde(default = "default_cow_primary_size")] pub cow_primary_size: u64, + #[serde(default = "default_ipt_filter")] + pub ip0: IptFilter, + #[serde(default = "default_ipt_filter")] + pub ip1: IptFilter, + #[serde(default = "default_ipt_filter")] + pub ip2: IptFilter, + #[serde(default = "default_ipt_filter")] + pub ip3: IptFilter, + pub workdir_path: Option, pub bitmap_size: Option, pub mem_limit: Option, @@ -65,6 +74,13 @@ fn default_cow_primary_size() -> u64 { 0 } +fn default_ipt_filter() -> IptFilter { + IptFilter{ + a: 0, + b: 0, + } +} + #[derive(Clone, Serialize, Deserialize)] pub struct ConfigLoader { pub include_default_config_path: Option, diff --git a/fuzz_runner/src/lib.rs b/fuzz_runner/src/lib.rs index 181792c..ed04932 100644 --- a/fuzz_runner/src/lib.rs +++ b/fuzz_runner/src/lib.rs @@ -146,8 +146,11 @@ impl FuzzRunner for ForkServer { */ impl FuzzRunner for QemuProcess { fn run_test(&mut self) -> Result> { - self.send_payload(); + 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())}); } @@ -169,7 +172,7 @@ impl FuzzRunner for QemuProcess { fn run_create_snapshot(&mut self) -> bool{ assert_eq!(self.aux.result.tmp_snapshot_created,0); - self.send_payload(); + self.send_payload().unwrap(); return self.aux.result.tmp_snapshot_created == 1; } @@ -177,7 +180,7 @@ impl FuzzRunner for QemuProcess { if self.aux.result.tmp_snapshot_created != 0 { self.aux.config.changed = 1; self.aux.config.discard_tmp_snapshot = 1; - self.send_payload(); + self.send_payload().unwrap(); if self.aux.result.tmp_snapshot_created != 0 { println!("AUX BUFFER {:#?}",self.aux); } @@ -190,7 +193,7 @@ impl FuzzRunner for QemuProcess { fn run_redqueen(&mut self) -> Result> { self.aux.config.changed = 1; self.aux.config.redqueen_mode=1; - self.send_payload(); + 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); @@ -201,7 +204,7 @@ impl FuzzRunner for QemuProcess { //println!("TRACE!!!!"); self.aux.config.trace_mode=1; self.aux.config.changed = 1; - self.send_payload(); + self.send_payload().unwrap(); self.aux.config.changed = 1; self.aux.config.trace_mode=0; return Ok(CFGInfo {}); diff --git a/fuzz_runner/src/nyx/aux_buffer.rs b/fuzz_runner/src/nyx/aux_buffer.rs index 4443e2d..6fe5a84 100644 --- a/fuzz_runner/src/nyx/aux_buffer.rs +++ b/fuzz_runner/src/nyx/aux_buffer.rs @@ -17,7 +17,8 @@ use derivative::Derivative; const AUX_BUFFER_SIZE: usize = 4096; const AUX_MAGIC: u64 = 0x54502d554d4551_u64; -const QEMU_PT_VERSION: u16 = 1; /* let's start at 1 for the initial version using the aux buffer */ +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 HEADER_SIZE: usize = 128; const CAP_SIZE: usize = 256; @@ -82,14 +83,21 @@ impl AuxBuffer { return AuxBuffer::new_readonly(file, false); } - pub fn validate_header(&self) { + pub fn validate_header(&self) -> Result<(), String> { mem_barrier(); let mgc = self.header.magic; - assert_eq!(mgc, AUX_MAGIC); + if mgc != AUX_MAGIC { + return Err(format!("aux buffer magic mismatch {} != {}...\n[!] Probably the AUX buffer is corrupted?!", AUX_MAGIC, mgc)); + } let version = self.header.version; - assert_eq!(version, QEMU_PT_VERSION); + if version != QEMU_PT_VERSION { + return Err(format!("aux buffer version mismatch {} != {}...\n[!] You are probably using either an outdated version of libnyx or QEMU-Nyx...", QEMU_PT_VERSION, version)); + } let hash = self.header.hash; - assert_eq!(hash, 81); + if hash != QEMU_PT_HASH { + return Err(format!("aux buffer hash mismatch {} != {}...\n[!] You are probably using either an outdated version of libnyx or QEMU-Nyx...", QEMU_PT_HASH, hash)); + } + Ok(()) } } #[derive(Debug, Copy, Clone)] @@ -176,7 +184,7 @@ pub struct auxilary_buffer_result_s { pub dirty_pages: u32, pub pt_trace_size: u32, pub payload_write_attempt_found: u8, - + pub abort: u8, } #[repr(C, packed(1))] diff --git a/fuzz_runner/src/nyx/mod.rs b/fuzz_runner/src/nyx/mod.rs index 1556207..dbb1448 100644 --- a/fuzz_runner/src/nyx/mod.rs +++ b/fuzz_runner/src/nyx/mod.rs @@ -24,7 +24,7 @@ fn into_absolute_path(sharedir: &str) -> String{ } } -pub fn qemu_process_new_from_kernel(sharedir: String, cfg: &QemuKernelConfig, fuzz_cfg: &FuzzerConfig) -> qemu_process::QemuProcess { +pub fn qemu_process_new_from_kernel(sharedir: String, cfg: &QemuKernelConfig, fuzz_cfg: &FuzzerConfig) -> Result { let params = params::KernelVmParams { qemu_binary: cfg.qemu_binary.to_string(), kernel: cfg.kernel.to_string(), @@ -39,6 +39,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, }; 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); @@ -51,7 +52,7 @@ pub fn qemu_process_new_from_kernel(sharedir: String, cfg: &QemuKernelConfig, fu return qemu_process::QemuProcess::new(qemu_params); } -pub fn qemu_process_new_from_snapshot(sharedir: String, cfg: &QemuSnapshotConfig, fuzz_cfg: &FuzzerConfig) -> qemu_process::QemuProcess { +pub fn qemu_process_new_from_snapshot(sharedir: String, cfg: &QemuSnapshotConfig, fuzz_cfg: &FuzzerConfig) -> Result { let snapshot_path = match &cfg.snapshot_path{ SnapshotPath::Create(_x) => panic!(), @@ -80,6 +81,7 @@ 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, }; 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); diff --git a/fuzz_runner/src/nyx/params.rs b/fuzz_runner/src/nyx/params.rs index 9f423c9..faf184b 100644 --- a/fuzz_runner/src/nyx/params.rs +++ b/fuzz_runner/src/nyx/params.rs @@ -1,5 +1,6 @@ use std::path::Path; use crate::config::SnapshotPath; +use crate::config::IptFilter; pub struct KernelVmParams { pub qemu_binary: String, @@ -13,6 +14,7 @@ pub struct KernelVmParams { pub dump_python_code_for_inputs: bool, pub write_protected_input_buffer: bool, pub cow_primary_size: Option, + pub ipt_filters: [IptFilter; 4], } pub struct SnapshotVmParams{ @@ -28,6 +30,8 @@ pub struct SnapshotVmParams{ pub dump_python_code_for_inputs: bool, pub write_protected_input_buffer: bool, pub cow_primary_size: Option, + pub ipt_filters: [IptFilter; 4], + } pub struct QemuParams { @@ -50,7 +54,6 @@ pub struct QemuParams { impl QemuParams { pub fn new_from_snapshot(workdir: &str, qemu_id: usize, cpu: usize, params: &SnapshotVmParams, create_snapshot_file: bool) -> QemuParams{ - assert!(!(!create_snapshot_file && qemu_id == 1)); let project_name = Path::new(workdir) .file_name() .expect("Couldn't get project name from workdir!") @@ -112,13 +115,19 @@ impl QemuParams { nyx_ops += &format!(",workdir={}", workdir); nyx_ops += &format!(",sharedir={}", params.sharedir); + + let mut i = 0; + for filter in params.ipt_filters{ + if filter.a != 0 && filter.b != 0 { + nyx_ops += &format!(",ip{}_a={},ip{}_b={}", i, filter.a, i, filter.b); + i += 1; + } + } + if params.cow_primary_size.is_some(){ nyx_ops += &format!(",cow_primary_size={}", params.cow_primary_size.unwrap()); } - //nyx_ops += &format!(",ip0_a=0x1000,ip0_b=0x7ffffffff000"); - //nyx_ops += &format!(",ip0_a=ffff800000000000,ip0_b=ffffffffffffffff"); - cmd.push(nyx_ops); cmd.push("-machine".to_string()); @@ -171,7 +180,6 @@ impl QemuParams { pub fn new_from_kernel(workdir: &str, qemu_id: usize, params: &KernelVmParams, create_snapshot_file: bool) -> QemuParams { //prepare_working_dir(workdir) - assert!(!(!create_snapshot_file && qemu_id == 1)); let project_name = Path::new(workdir) .file_name() .expect("Couldn't get project name from workdir!") @@ -236,13 +244,18 @@ impl QemuParams { nyx_ops += &format!(",workdir={}", workdir); nyx_ops += &format!(",sharedir={}", params.sharedir); + let mut i = 0; + for filter in params.ipt_filters{ + if filter.a != 0 && filter.b != 0 { + nyx_ops += &format!(",ip{}_a={:x},ip{}_b={:x}", i, filter.a, i, filter.b); + i += 1; + } + } + if params.cow_primary_size.is_some(){ nyx_ops += &format!(",cow_primary_size={}", params.cow_primary_size.unwrap()); } - //nyx_ops += &format!(",ip0_a=0x1000,ip0_b=0x7ffffffff000"); - //nyx_ops += &format!(",ip0_a=ffff800000000000,ip0_b=ffffffffffffffff"); - cmd.push(nyx_ops); cmd.push("-machine".to_string()); diff --git a/fuzz_runner/src/nyx/qemu_process.rs b/fuzz_runner/src/nyx/qemu_process.rs index de61095..b7f0862 100644 --- a/fuzz_runner/src/nyx/qemu_process.rs +++ b/fuzz_runner/src/nyx/qemu_process.rs @@ -1,6 +1,7 @@ use core::ffi::c_void; use nix::sys::mman::*; use std::fs; +use std::io; use std::fs::{File, OpenOptions}; use std::io::prelude::*; use std::os::unix::fs::symlink; @@ -31,21 +32,23 @@ pub struct QemuProcess { pub bitmap: &'static mut [u8], pub payload: &'static mut [u8], pub params: QemuParams, - hprintf_log: File, } -fn execute_qemu(ctrl: &mut UnixStream) { - ctrl.write_all(&[120_u8]).unwrap(); +fn execute_qemu(ctrl: &mut UnixStream) -> io::Result<()>{ + ctrl.write_all(&[120_u8])?; + Ok(()) } -fn wait_qemu(ctrl: &mut UnixStream) { +fn wait_qemu(ctrl: &mut UnixStream) -> io::Result<()>{ let mut buf = [0]; - ctrl.read_exact(&mut buf).unwrap(); + ctrl.read_exact(&mut buf)?; + Ok(()) } -fn run_qemu(ctrl: &mut UnixStream) { - execute_qemu(ctrl); - wait_qemu(ctrl); +fn run_qemu(ctrl: &mut UnixStream) -> io::Result<()>{ + execute_qemu(ctrl)?; + wait_qemu(ctrl)?; + Ok(()) } fn make_shared_data(file: File, size: usize) -> &'static mut [u8] { @@ -69,7 +72,7 @@ fn make_shared_ijon_data(file: File, size: usize) -> FeedbackBuffer { } impl QemuProcess { - pub fn new(params: QemuParams) -> QemuProcess { + pub fn new(params: QemuParams) -> Result { Self::prepare_redqueen_workdir(¶ms.workdir, params.qemu_id); if params.qemu_id == 0{ @@ -89,11 +92,20 @@ impl QemuProcess { .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, format!("{}/bitmap_{}", params.workdir, params.qemu_id), ) .unwrap(); + + 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, format!("{}/payload_{}", params.workdir, params.qemu_id), @@ -123,7 +135,7 @@ impl QemuProcess { thread::sleep(time::Duration::from_millis(200*params.qemu_id as u64)); - let child = if params.dump_python_code_for_inputs{ + let mut child = if params.dump_python_code_for_inputs{ Command::new(¶ms.cmd[0]) .args(¶ms.cmd[1..]) .env("DUMP_PAYLOAD_MODE", "TRUE") @@ -159,7 +171,9 @@ impl QemuProcess { // dry_run //println!("TRHEAD {} run QEMU initial",params.qemu_id); - run_qemu(&mut control); + if run_qemu(&mut control).is_err() { + return Err(format!("cannot launch QEMU-Nyx...")); + } let aux_shm_f = OpenOptions::new() .read(true) @@ -175,7 +189,14 @@ impl QemuProcess { .expect("couldn't open aux buffer file"); let mut aux_buffer = AuxBuffer::new(aux_shm_f); - aux_buffer.validate_header(); + match aux_buffer.validate_header(){ + Err(x) => { + child.kill().unwrap(); + child.wait().unwrap(); + return Err(x); + }, + Ok(_) => {}, + } if params.write_protected_input_buffer{ if params.qemu_id == 0 { println!("[!] libnyx: input buffer is write protected"); @@ -185,22 +206,30 @@ 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(); + + return Err(msg); + } + 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()); - } - else{ - //println!("QEMU NOT READY"); - } + } if aux_buffer.result.state == 3 { break; } - //println!("QEMU NOT READY"); - //println!("TRHEAD {} run QEMU NOT READY",params.qemu_id); - run_qemu(&mut control); + if run_qemu(&mut control).is_err(){ + return Err(format!("failed to establish fuzzing loop...")); + } + //run_qemu(&mut control).unwrap(); } - //println!("QEMU READY"); println!("[!] libnyx: qemu #{} is ready:", params.qemu_id); aux_buffer.config.reload_mode = 1; @@ -208,16 +237,7 @@ impl QemuProcess { aux_buffer.config.timeout_usec = 500_000; aux_buffer.config.changed = 1; - //run_qemu(&mut control); - //run_qemu(&mut control); - - let mut option = OpenOptions::new(); - option.read(true); - option.write(true); - option.create(true); - let hprintf_log = option.open(format!("{}/hprintf_log_{}", params.workdir, params.qemu_id)).unwrap(); - - return QemuProcess { + return Ok(QemuProcess { process: child, aux: aux_buffer, feedback_data: ijon_shared, @@ -225,49 +245,32 @@ impl QemuProcess { bitmap: bitmap_shared, payload: payload_shared, params, - hprintf_log, - }; + }); } - pub fn send_payload(&mut self) { + pub fn send_payload(&mut self) -> io::Result<()>{ let mut old_address: u64 = 0; - //use rand::Rng; - //println!("RUN INPUT"); - //std::thread::sleep(std::time::Duration::from_secs(1)); - //let time = std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH).unwrap().as_nanos(); - //self.hprintf_log.write_all(&format!("===({})===\n", time).as_bytes()).unwrap(); + loop { mem_barrier(); - run_qemu(&mut self.ctrl); + match run_qemu(&mut self.ctrl) { + Err(x) => return Err(x), + Ok(_) => {}, + } mem_barrier(); - if self.aux.result.hprintf != 0 { - self.hprintf_log.write_all(&format!("{}\n", self.aux.misc.as_string()).as_bytes()).unwrap(); - //println!("HPRINTF {}", self.aux.misc.as_string()); + 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()); - //print!("{}", "".clear()); - println!("TEST\n"); - continue; } - //println!("pt trace size {:x} bytes",self.aux.result.pt_trace_size); - //println!("{:} dirty pages",self.aux.result.dirty_pages); - //println!("interpreter ran {} ops",self.feedback_data.shared.interpreter.executed_opcode_num); - //let max_v = 0; - //let max_i = 0; - //for (i,v) in self.feedback_data.shared.ijon.max_data.iter().enumerate(){ - // if *v > max_v{ - // max_v=*v; - // max_i=i; - // - // } - //} - //println!("found IJON MAX: {}\t{:x}",max_i,max_v); + + 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; @@ -284,15 +287,8 @@ impl QemuProcess { self.aux.config.page_dump_mode = 1; self.aux.config.changed = 1; } - //else { - // break; - //} - } - //std::thread::sleep(std::time::Duration::from_secs(1)); - //if self.aux.result.tmp_snapshot_created != 0 { - // //println!("created snapshot!!!!!!\n"); - //} + Ok(()) } pub fn set_timeout(&mut self, timeout: std::time::Duration){ @@ -306,11 +302,26 @@ impl QemuProcess { } pub fn shutdown(&mut self) { - println!("Let's kill QEMU!"); + println!("[!] libnyx: sending SIGKILL to QEMU-Nyx process..."); self.process.kill().unwrap(); self.wait(); } + pub fn wait_for_workdir(workdir: &str){ + println!("[!] libnyx: waiting for workdir to be created by parent process..."); + + let files = vec![ + "page_cache.lock", + "page_cache.addr", + "page_cache.addr", + ]; + for file in files.iter() { + while !Path::new(&format!("{}/{}", workdir, file)).exists(){ + thread::sleep(time::Duration::from_secs(1)); + } + } + } + pub fn prepare_workdir(workdir: &str, seed_path: Option) { Self::clear_workdir(workdir); let folders = vec![ diff --git a/libnyx/src/lib.rs b/libnyx/src/lib.rs index 57fa7b9..c82bf90 100644 --- a/libnyx/src/lib.rs +++ b/libnyx/src/lib.rs @@ -16,11 +16,13 @@ pub enum NyxReturnValue { Asan, Timout, InvalidWriteToPayload, - Error + 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, create_snapshot: bool) -> * mut QemuProcess { +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) @@ -35,18 +37,20 @@ pub extern "C" fn nyx_new(sharedir: *const c_char, workdir: *const c_char, worke let sharedir_r_str = sharedir_c_str.to_str().unwrap(); let workdir_r_str = workdir_c_str.to_str().unwrap(); - println!("r_str: {}", sharedir_r_str); - let cfg: Config = Config::new_from_sharedir(&sharedir_r_str); - println!("config {}", cfg.fuzz.bitmap_size); - - + 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; + } + }; let mut config = cfg.fuzz; let runner_cfg = cfg.runner; /* todo: add sanity check */ - config.cpu_pin_start_at = worker_id as usize; + config.cpu_pin_start_at = cpu_id as usize; config.thread_id = worker_id as usize; config.threads = if create_snapshot { 2 as usize } else { 1 as usize }; @@ -59,16 +63,25 @@ pub extern "C" fn nyx_new(sharedir: *const c_char, workdir: *const c_char, worke if worker_id == 0 { QemuProcess::prepare_workdir(&config.workdir_path, config.seed_path.clone()); } + else{ + QemuProcess::wait_for_workdir(&config.workdir_path); + } - match runner_cfg.clone() { + let runner = match runner_cfg.clone() { FuzzRunnerConfig::QemuSnapshot(cfg) => { - let runner = qemu_process_new_from_snapshot(sdir.to_string(), &cfg, &config); - return Box::into_raw(Box::new(runner)); - } + qemu_process_new_from_snapshot(sdir.to_string(), &cfg, &config) + }, FuzzRunnerConfig::QemuKernel(cfg) => { - let runner = qemu_process_new_from_kernel(sdir.to_string(), &cfg, &config); - return Box::into_raw(Box::new(runner)); + qemu_process_new_from_kernel(sdir.to_string(), &cfg, &config) } + }; + + 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 + }, } } @@ -104,6 +117,15 @@ pub extern "C" fn nyx_get_bitmap_buffer(qemu_process: * mut QemuProcess) -> *mut } } +#[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{ @@ -152,25 +174,30 @@ pub extern "C" fn nyx_exec(qemu_process: * mut QemuProcess) -> NyxReturnValue { assert!(!qemu_process.is_null()); assert!((qemu_process as usize) % std::mem::align_of::() == 0); - (*qemu_process).send_payload(); - - if (*qemu_process).aux.result.crash_found != 0 { - return NyxReturnValue::Crash; + match (*qemu_process).send_payload(){ + Err(_) => return NyxReturnValue::IoError, + Ok(_) => { + if (*qemu_process).aux.result.abort != 0 { + return NyxReturnValue::Abort; + } + 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; + } } - 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; - } - println!("unknown exeuction result!!"); - return NyxReturnValue::Error; } }