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
This commit is contained in:
Sergej Schumilo 2022-01-21 04:06:17 +01:00
parent ecbcb2d723
commit a5ae4c13e1
14 changed files with 715 additions and 954 deletions

View File

@ -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);

View File

@ -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<String>,
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,

View File

@ -53,6 +53,9 @@ pub struct FuzzerConfigLoader {
pub workdir_path: Option<String>,
pub bitmap_size: Option<usize>,
#[serde(default = "default_input_buffer_size")]
pub input_buffer_size: usize,
pub mem_limit: Option<usize>,
pub time_limit: Option<Duration>,
pub target_binary: Option<String>,
@ -66,6 +69,10 @@ pub struct FuzzerConfigLoader {
pub exit_after_first_crash: Option<bool>,
}
fn default_input_buffer_size() -> usize {
1 << 17
}
fn default_write_protected_input_buffer() -> bool {
false
}

View File

@ -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"

View File

@ -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<T>(desc: &str) -> Result<T, SpawnError> {
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<T>(desc: &str) -> Result<T, SubprocessError> {
return Err(SubprocessError::Unspecific(desc.into()));
}

View File

@ -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<u8>,
input_size: usize,
st_out: std::io::BufReader<TimeoutReader<File>>,
}
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::<Vec<_>>();
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::<LittleEndian>()
.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::<Vec<_>>();
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::<Vec<&CStr>>()[..];
let env_ref = &env.iter().map(|x| x.as_c_str()).collect::<Vec<&CStr>>()[..];
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<ExitReason, SubprocessError> {
self.input_data[0..data.len()].copy_from_slice(data);
self.input_size = data.len();
return self.run();
}
pub fn run(&mut self) -> Result<ExitReason, SubprocessError> {
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::<LittleEndian>().context(QemuRunIO {
task: "Couldn't read target pid",
})?);
if let Ok(status) = self.st_out.read_i32::<LittleEndian>() {
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::<LittleEndian>().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]);
}
}
}
*/

View File

@ -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 },
}

View File

@ -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<u8>,
pub rhs: Vec<u8>,
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<RedqueenEvent>}
pub struct CFGInfo {}
pub trait FuzzRunner {
fn run_test(&mut self) -> Result<TestInfo, Box<dyn Error>>;
fn run_redqueen(&mut self) -> Result<RedqueenInfo, Box<dyn Error>>;
fn run_cfg(&mut self) -> Result<CFGInfo, Box<dyn Error>>;
fn run_create_snapshot(&mut self) -> bool;
fn delete_snapshot(&mut self) -> Result<(), Box<dyn Error>>;
fn shutdown(&mut self) -> Result<(), Box<dyn Error>>;
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::<Vec<_>>();
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<TestInfo, Box<dyn Error>> {
self.run().unwrap();
return Ok(TestInfo {ops_used: 0, exitreason: ExitReason::FuzzerError}); //TODO fix this!
}
fn run_redqueen(&mut self) -> Result<RedqueenInfo, Box<dyn Error>> {
unreachable!();
//return Ok(parse_redqueen_file());
}
fn run_cfg(&mut self) -> Result<CFGInfo, Box<dyn Error>> {
unreachable!()
//return Ok(CFGInfo {});
}
fn run_create_snapshot(&mut self) -> bool{
unreachable!();
}
fn delete_snapshot(&mut self) -> Result<(), Box<dyn Error>>{
unreachable!();
}
fn shutdown(self) -> Result<(), Box<dyn Error>> {
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<TestInfo, Box<dyn Error>> {
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<dyn Error>>{
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<RedqueenInfo, Box<dyn Error>> {
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<CFGInfo, Box<dyn Error>> {
//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<dyn Error>> {
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());
}
}

View File

@ -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))]

View File

@ -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, &params, 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, &params, 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);
}

View File

@ -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<u64>,
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<u64>,
pub ipt_filters: [IptFilter; 4],
pub input_buffer_size: usize,
}
pub struct QemuParams {
pub cmd: Vec<String>,
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 &params.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,

View File

@ -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::<c_void>(), 0x1000, prot, flags, file.into_raw_fd(), size as i64).unwrap();
let ptr = mmap(std::ptr::null_mut::<c_void>(), 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<QemuProcess, String> {
Self::prepare_redqueen_workdir(&params.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(&params.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(&params.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(
&params.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(
&params.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(&params.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(&params.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)
}
}

205
libnyx/src/ffi.rs Normal file
View File

@ -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::<NyxConfig>() == 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<u32>, 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::<NyxProcess>() == 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::<NyxProcess>() == 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::<NyxProcess>() == 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::<NyxProcess>() == 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::<NyxProcess>() == 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::<NyxProcess>() == 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::<NyxProcess>() == 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::<NyxProcess>() == 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::<NyxProcess>() == 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::<NyxProcess>() == 0);
assert!((buffer as usize) % std::mem::align_of::<u8>() == 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::<u32>()..].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::<NyxProcess>() == 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());
},
_ => {},
}
}
}

View File

@ -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<NyxConfig, String> {
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<String>{
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<String>{
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<String>{
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<Vec<u8>> {
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<QemuProcess, String> {
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::<QemuProcess>() == 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::<QemuProcess>() == 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::<QemuProcess>() == 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::<QemuProcess>() == 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::<QemuProcess>() == 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::<QemuProcess>() == 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::<QemuProcess>() == 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::<QemuProcess>() == 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<u32>, input_buffer_write_protection: bool) -> Result<NyxProcess, String> {
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::<QemuProcess>() == 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<NyxProcess, String>{
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<NyxProcess, String> {
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<NyxProcess, String> {
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<NyxProcess, String> {
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<u8>{
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::<QemuProcess>() == 0);
assert!((buffer as usize) % std::mem::align_of::<u8>() == 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::<u32>()..].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::<QemuProcess>() == 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() {
}
}