extern crate libc;
use libnyx_config::{Config, FuzzRunnerConfig, QemuNyxRole};
use libnyx_fuzz_runner::nyx::qemu_process::QemuProcess;
use libnyx_fuzz_runner::nyx::aux_buffer::{NYX_SUCCESS, NYX_CRASH, NYX_TIMEOUT, NYX_INPUT_WRITE, NYX_ABORT};
use libc::fcntl;
use std::fmt;
pub mod ffi;
#[repr(C)]
#[derive(Debug)]
pub enum NyxReturnValue {
Normal,
Crash,
Asan,
Timeout,
InvalidWriteToPayload,
Error,
IoError, Abort, }
#[repr(C)]
#[derive(Debug)]
pub enum NyxProcessRole {
StandAlone,
Parent,
Child,
}
impl fmt::Display for NyxReturnValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let nyx_return_value_str = match self {
NyxReturnValue::Normal => "Normal",
NyxReturnValue::Crash => "Crash",
NyxReturnValue::Timeout => "Timeout",
NyxReturnValue::InvalidWriteToPayload => "InvalidWriteToPayload",
NyxReturnValue::Abort => "Abort",
NyxReturnValue::Error => "Error",
_ => "Unknown",
};
write!(f, "{}", nyx_return_value_str)
}
}
pub struct NyxProcess {
process: QemuProcess,
}
#[derive(Clone, Debug)]
pub struct NyxConfig {
config: Config,
sharedir_path: String,
}
impl NyxConfig {
pub fn load(sharedir: &str) -> Result<NyxConfig, String> {
match Config::new_from_sharedir(&sharedir){
Ok(x) => Ok(NyxConfig{
config: x,
sharedir_path: sharedir.to_string()
}),
Err(x) => Err(x),
}
}
pub fn print(&self){
println!("[*] Nyx config (share-dir: {}):", self.sharedir_path());
println!(" - workdir_path -> {}", self.workdir_path());
println!(" - input_buffer_size -> {}", self.input_buffer_size());
println!(" - input_buffer_write_protection -> {}", self.input_buffer_write_protection());
println!(" - hprintf_fd -> {}", self.hprintf_fd());
println!(" - process_role: -> {:?}", self.process_role());
}
pub fn sharedir_path(&self) -> String {
self.sharedir_path.clone()
}
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);
}
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);
}
pub fn target_hash(&self) -> Option<[u8; 20]> {
self.config.fuzz.target_hash
}
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 input_buffer_size(&self) -> usize {
self.config.fuzz.input_buffer_size
}
pub fn input_buffer_write_protection(&self) -> bool {
self.config.fuzz.write_protected_input_buffer
}
pub fn set_workdir_path(&mut self, path: String) {
self.config.fuzz.workdir_path = path;
}
pub fn set_input_buffer_size(&mut self, size: usize) {
if size % 0x1000 != 0 {
panic!("[ ] Input buffer size must be a multiple of x86_64_PAGE_SIZE (4096)!");
}
self.config.fuzz.input_buffer_size = size;
}
pub fn set_input_buffer_write_protection(&mut self, write_protected: bool) {
self.config.fuzz.write_protected_input_buffer = write_protected;
}
pub fn hprintf_fd(&self) -> i32 {
match self.config.runtime.hprintf_fd() {
Some(fd) => fd,
None => -1,
}
}
pub fn set_hprintf_fd(&mut self, fd: i32) {
self.config.runtime.set_hpintf_fd(fd);
}
pub fn set_process_role(&mut self, role: NyxProcessRole) {
let _role = match role {
NyxProcessRole::Parent => QemuNyxRole::Parent,
NyxProcessRole::Child => QemuNyxRole::Child,
NyxProcessRole::StandAlone => QemuNyxRole::StandAlone,
};
self.config.runtime.set_process_role(_role);
}
pub fn set_reuse_snapshot_path(&mut self, path: String) {
self.config.runtime.set_reuse_snapshot_path(path);
}
pub fn process_role(&self) -> &QemuNyxRole {
self.config.runtime.process_role()
}
pub fn worker_id(&self) -> usize {
self.config.runtime.worker_id()
}
pub fn set_worker_id(&mut self, worker_id: usize) {
self.config.runtime.set_worker_id(worker_id);
}
pub fn set_aux_buffer_size(&mut self, size: usize) -> bool{
return self.config.runtime.set_aux_buffer_size(size);
}
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 {
pub fn new(config: &mut NyxConfig, worker_id: usize) -> Result<NyxProcess, String> {
let sharedir = config.sharedir_path();
config.set_worker_id(worker_id);
match libnyx_fuzz_runner::nyx::qemu_process_new(sharedir.to_string(), &config.config){
Ok(x) => Ok(NyxProcess{
process: x,
}),
Err(x) => Err(x),
}
}
pub fn aux_buffer_as_mut_ptr(&self) -> *mut u8 {
std::ptr::addr_of!(self.process.aux_buffer().header.magic) as *mut u8
}
pub fn aux_buffer_size(&self) -> usize {
self.process.aux_buffer().size()
}
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[.. self.process.bitmap_size]
}
pub fn bitmap_buffer_mut(&mut self) -> &mut [u8] {
&mut self.process.bitmap[.. self.process.bitmap_size]
}
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_buffer_mut().config.reload_mode = if enable {1} else {0};
}
pub fn option_set_redqueen_mode(&mut self, enable: bool) {
self.process.aux_buffer_mut().config.redqueen_mode = if enable {1} else {0};
}
pub fn option_set_trace_mode(&mut self, enable: bool) {
self.process.aux_buffer_mut().config.trace_mode = if enable {1} else {0};
}
pub fn option_set_delete_incremental_snapshot(&mut self, enable: bool) {
self.process.aux_buffer_mut().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_buffer_mut().config.timeout_sec = timeout_sec;
self.process.aux_buffer_mut().config.timeout_usec = timeout_usec;
}
pub fn option_apply(&mut self) {
self.process.aux_buffer_mut().config.changed = 1;
}
pub fn aux_misc(&self) -> Vec<u8>{
self.process.aux_buffer().misc_slice().to_vec()
}
pub fn aux_data_misc(&self) -> Vec<u8>{
self.process.aux_buffer().misc_data_slice().to_vec()
}
pub fn aux_tmp_snapshot_created(&self) -> bool {
self.process.aux_buffer().result.tmp_snapshot_created != 0
}
pub fn aux_string(&self) -> String {
let len = self.process.aux_buffer().misc.len;
String::from_utf8_lossy(&self.process.aux_buffer().misc_data_slice()[0..len as usize]).to_string()
}
pub fn exec(&mut self) -> NyxReturnValue {
match self.process.send_payload(){
Err(_) => NyxReturnValue::IoError,
Ok(_) => {
match self.process.aux_buffer().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,
}
}
}
}
pub fn set_input_ptr(&mut self, buffer: *const u8, size: u32) {
unsafe{
std::ptr::copy(&size, self.process.payload.as_mut_ptr() as *mut u32, 1 as usize);
std::ptr::copy(buffer, self.process.payload[std::mem::size_of::<u32>()..].as_mut_ptr(), std::cmp::min(size as usize, self.input_buffer_size()));
}
}
pub fn set_input(&mut self, buffer: &[u8], size: u32) {
self.set_input_ptr(buffer.as_ptr(), size);
}
pub fn set_hprintf_fd(&mut self, fd: i32) {
unsafe {
assert!(fcntl(fd, libc::F_GETFD) != -1);
};
self.process.set_hprintf_fd(fd);
}
}
pub fn remove_work_dir(workdir: &str) -> Result<(), String> {
libnyx_fuzz_runner::nyx::qemu_process::remove_workdir_safe(workdir)
}