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:
parent
21ee8d2cae
commit
aa3f126100
@ -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)?;
|
||||||
|
@ -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();
|
||||||
|
@ -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();
|
||||||
|
@ -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();
|
||||||
|
@ -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 {
|
||||||
|
@ -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...
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
|
@ -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()
|
||||||
|
@ -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]) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user