LibAFL_qemu: Return errors from Emulator::new instead of asserting (#1197)

* qemu: Return errors from Emulator::new instead of asserting

Libraries should not `assert!` except in cases of unrecoverable (library)
programmer error. These errors are all potentially recoverable, and aren't
internal errors in `libafl_qemu` itself.

* Respond to review comments
This commit is contained in:
Langston Barrett 2023-04-09 15:27:27 -04:00 committed by GitHub
parent 21ee8d2cae
commit aa3f126100
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 66 additions and 23 deletions

View File

@ -147,7 +147,7 @@ fn fuzz(
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
let env: Vec<(String, String)> = env::vars().collect(); let env: Vec<(String, String)> = env::vars().collect();
let emu = Emulator::new(&args, &env); let emu = Emulator::new(&args, &env)?;
let mut elf_buffer = Vec::new(); let mut elf_buffer = Vec::new();
let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer)?; let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer)?;

View File

@ -171,7 +171,7 @@ fn fuzz(
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
let env: Vec<(String, String)> = env::vars().collect(); let env: Vec<(String, String)> = env::vars().collect();
let emu = Emulator::new(&args, &env); let emu = Emulator::new(&args, &env).unwrap();
//let emu = init_with_asan(&mut args, &mut env); //let emu = init_with_asan(&mut args, &mut env);
let mut elf_buffer = Vec::new(); let mut elf_buffer = Vec::new();

View File

@ -54,7 +54,7 @@ pub fn fuzz() {
env::remove_var("LD_LIBRARY_PATH"); env::remove_var("LD_LIBRARY_PATH");
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
let env: Vec<(String, String)> = env::vars().collect(); let env: Vec<(String, String)> = env::vars().collect();
let emu = Emulator::new(&args, &env); let emu = Emulator::new(&args, &env).unwrap();
let mut elf_buffer = Vec::new(); let mut elf_buffer = Vec::new();
let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer).unwrap(); let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer).unwrap();

View File

@ -54,7 +54,7 @@ pub fn fuzz() {
env::remove_var("LD_LIBRARY_PATH"); env::remove_var("LD_LIBRARY_PATH");
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
let env: Vec<(String, String)> = env::vars().collect(); let env: Vec<(String, String)> = env::vars().collect();
let emu = Emulator::new(&args, &env); let emu = Emulator::new(&args, &env).unwrap();
let mut elf_buffer = Vec::new(); let mut elf_buffer = Vec::new();
let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer).unwrap(); let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer).unwrap();

View File

@ -80,7 +80,7 @@ pub fn fuzz() {
// Initialize QEMU // Initialize QEMU
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
let env: Vec<(String, String)> = env::vars().collect(); let env: Vec<(String, String)> = env::vars().collect();
let emu = Emulator::new(&args, &env); let emu = Emulator::new(&args, &env).unwrap();
emu.set_breakpoint(main_addr); emu.set_breakpoint(main_addr);
unsafe { unsafe {

View File

@ -41,7 +41,7 @@
//! //!
//! let env: Vec<(String, String)> = env::vars().collect(); //! let env: Vec<(String, String)> = env::vars().collect();
//! //!
//! let emu = Emulator::new(&mut options.qemu_args.to_vec(), &mut env); //! let emu = Emulator::new(&mut options.qemu_args.to_vec(), &mut env).unwrap();
//! // do other stuff... //! // do other stuff...
//! } //! }
//! //!

View File

@ -16,7 +16,7 @@ use meminterval::{Interval, IntervalTree};
use num_enum::{IntoPrimitive, TryFromPrimitive}; use num_enum::{IntoPrimitive, TryFromPrimitive};
use crate::{ use crate::{
emu::{Emulator, MemAccessInfo, SyscallHookResult}, emu::{EmuError, Emulator, MemAccessInfo, SyscallHookResult},
helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
hooks::QemuHooks, hooks::QemuHooks,
GuestAddr, GuestAddr,
@ -454,8 +454,10 @@ impl AsanGiovese {
static mut ASAN_INITED: bool = false; static mut ASAN_INITED: bool = false;
pub fn init_with_asan(args: &mut Vec<String>, env: &mut [(String, String)]) -> Emulator { pub fn init_with_asan(
assert!(!args.is_empty()); args: &mut Vec<String>,
env: &mut [(String, String)],
) -> Result<Emulator, EmuError> {
let current = env::current_exe().unwrap(); let current = env::current_exe().unwrap();
let asan_lib = fs::canonicalize(current) let asan_lib = fs::canonicalize(current)
.unwrap() .unwrap()

View File

@ -3,6 +3,7 @@
use core::{ use core::{
convert::Into, convert::Into,
ffi::c_void, ffi::c_void,
fmt,
mem::MaybeUninit, mem::MaybeUninit,
ptr::{addr_of, copy_nonoverlapping, null}, ptr::{addr_of, copy_nonoverlapping, null},
}; };
@ -673,28 +674,68 @@ pub struct Emulator {
_private: (), _private: (),
} }
#[derive(Debug)]
pub enum EmuError {
MultipleInstances,
EmptyArgs,
TooManyArgs(usize),
}
impl std::error::Error for EmuError {}
impl fmt::Display for EmuError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
EmuError::MultipleInstances => {
write!(f, "Only one instance of the QEMU Emulator is permitted")
}
EmuError::EmptyArgs => {
write!(f, "QEMU emulator args cannot be empty")
}
EmuError::TooManyArgs(n) => {
write!(
f,
"Too many arguments passed to QEMU emulator ({n} > i32::MAX)"
)
}
}
}
}
impl From<EmuError> for libafl::Error {
fn from(err: EmuError) -> Self {
libafl::Error::unknown(format!("{err}"))
}
}
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
impl Emulator { impl Emulator {
#[allow(clippy::must_use_candidate, clippy::similar_names)] #[allow(clippy::must_use_candidate, clippy::similar_names)]
pub fn new(args: &[String], env: &[(String, String)]) -> Emulator { pub fn new(args: &[String], env: &[(String, String)]) -> Result<Emulator, EmuError> {
unsafe { unsafe {
assert!( if EMULATOR_IS_INITIALIZED {
!EMULATOR_IS_INITIALIZED, return Err(EmuError::MultipleInstances);
"Only an instance of Emulator is permitted"
);
} }
assert!(!args.is_empty()); }
if args.is_empty() {
return Err(EmuError::EmptyArgs);
}
let argc = args.len();
if i32::try_from(argc).is_err() {
return Err(EmuError::TooManyArgs(argc));
}
#[allow(clippy::cast_possible_wrap)]
let argc = argc as i32;
let args: Vec<String> = args.iter().map(|x| x.clone() + "\0").collect(); let args: Vec<String> = args.iter().map(|x| x.clone() + "\0").collect();
let argv: Vec<*const u8> = args.iter().map(|x| x.as_bytes().as_ptr()).collect(); let argv: Vec<*const u8> = args.iter().map(|x| x.as_bytes().as_ptr()).collect();
assert!(argv.len() < i32::MAX as usize);
let env_strs: Vec<String> = env let env_strs: Vec<String> = env
.iter() .iter()
.map(|(k, v)| format!("{}={}\0", &k, &v)) .map(|(k, v)| format!("{}={}\0", &k, &v))
.collect(); .collect();
let mut envp: Vec<*const u8> = env_strs.iter().map(|x| x.as_bytes().as_ptr()).collect(); let mut envp: Vec<*const u8> = env_strs.iter().map(|x| x.as_bytes().as_ptr()).collect();
envp.push(null()); envp.push(null());
#[allow(clippy::cast_possible_wrap)]
let argc = argv.len() as i32;
unsafe { unsafe {
#[cfg(emulation_mode = "usermode")] #[cfg(emulation_mode = "usermode")]
qemu_user_init( qemu_user_init(
@ -714,7 +755,7 @@ impl Emulator {
} }
EMULATOR_IS_INITIALIZED = true; EMULATOR_IS_INITIALIZED = true;
} }
Emulator { _private: () } Ok(Emulator { _private: () })
} }
#[must_use] #[must_use]
@ -1174,10 +1215,10 @@ pub mod pybind {
impl Emulator { impl Emulator {
#[allow(clippy::needless_pass_by_value)] #[allow(clippy::needless_pass_by_value)]
#[new] #[new]
fn new(args: Vec<String>, env: Vec<(String, String)>) -> Emulator { fn new(args: Vec<String>, env: Vec<(String, String)>) -> PyResult<Emulator> {
Emulator { let emu = super::Emulator::new(&args, &env)
emu: super::Emulator::new(&args, &env), .map_err(|e| PyValueError::new_err(format!("{e}")))?;
} Ok(Emulator { emu })
} }
fn write_mem(&self, addr: GuestAddr, buf: &[u8]) { fn write_mem(&self, addr: GuestAddr, buf: &[u8]) {