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:
parent
ecbcb2d723
commit
a5ae4c13e1
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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()));
|
||||
}
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
@ -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 },
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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))]
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 ¶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,
|
||||
|
@ -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(¶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)
|
||||
}
|
||||
}
|
||||
|
205
libnyx/src/ffi.rs
Normal file
205
libnyx/src/ffi.rs
Normal 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());
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
@ -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() {
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user