Fix GuestMaps iterator in LibAFL QEMU. (#2041)
* Fix maps iterator. * Use native QEMU structs instead of pointer casting. * Update stub bindings. * Maps operations stored in usermode.
This commit is contained in:
parent
8b9b5a8767
commit
ec935bf95f
@ -45,6 +45,9 @@ const WRAPPER_HEADER: &str = r#"
|
||||
#include "user/safe-syscall.h"
|
||||
#include "qemu/selfmap.h"
|
||||
#include "cpu_loop-common.h"
|
||||
#include "qemu/selfmap.h"
|
||||
|
||||
#include "libafl/user.h"
|
||||
|
||||
#else
|
||||
|
||||
@ -55,8 +58,8 @@ const WRAPPER_HEADER: &str = r#"
|
||||
#include "sysemu/tcg.h"
|
||||
#include "sysemu/replay.h"
|
||||
|
||||
#include "libafl_extras/syx-snapshot/device-save.h"
|
||||
#include "libafl_extras/syx-snapshot/syx-snapshot.h"
|
||||
#include "libafl/syx-snapshot/device-save.h"
|
||||
#include "libafl/syx-snapshot/syx-snapshot.h"
|
||||
|
||||
#endif
|
||||
|
||||
@ -76,9 +79,9 @@ const WRAPPER_HEADER: &str = r#"
|
||||
|
||||
#include "qemu/plugin-memory.h"
|
||||
|
||||
#include "libafl_extras/exit.h"
|
||||
#include "libafl_extras/hook.h"
|
||||
#include "libafl_extras/jit.h"
|
||||
#include "libafl/exit.h"
|
||||
#include "libafl/hook.h"
|
||||
#include "libafl/jit.h"
|
||||
|
||||
"#;
|
||||
|
||||
@ -142,6 +145,8 @@ pub fn generate(
|
||||
.allowlist_type("libafl_exit_reason_sync_backdoor")
|
||||
.allowlist_type("libafl_exit_reason_breakpoint")
|
||||
.allowlist_type("Syx.*")
|
||||
.allowlist_type("libafl_mapinfo")
|
||||
.allowlist_type("IntervalTreeRoot")
|
||||
.allowlist_function("qemu_user_init")
|
||||
.allowlist_function("target_mmap")
|
||||
.allowlist_function("target_mprotect")
|
||||
@ -159,6 +164,8 @@ pub fn generate(
|
||||
.allowlist_function("syx_.*")
|
||||
.allowlist_function("device_list_all")
|
||||
.allowlist_function("libafl_.*")
|
||||
.allowlist_function("read_self_maps")
|
||||
.allowlist_function("free_self_maps")
|
||||
.blocklist_function("main_loop_wait") // bindgen issue #1313
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()));
|
||||
|
||||
|
@ -8,7 +8,7 @@ use which::which;
|
||||
|
||||
const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
|
||||
const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
|
||||
const QEMU_REVISION: &str = "e99b9da6585504a8333f2846a61de487f94d3476";
|
||||
const QEMU_REVISION: &str = "50b0c90e0aab07643ccb58cfbbef742bcfb8b7d1";
|
||||
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
pub struct BuildResult {
|
||||
|
@ -98,7 +98,7 @@ macro_rules! extern_c_checked {
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use core::ops::BitAnd;
|
||||
use std::{ffi::c_void, slice::from_raw_parts, str::from_utf8_unchecked};
|
||||
use std::ffi::c_void;
|
||||
|
||||
#[cfg(all(not(feature = "clippy"), target_os = "linux"))]
|
||||
pub use bindings::*;
|
||||
@ -127,7 +127,7 @@ pub struct MapInfo {
|
||||
start: GuestAddr,
|
||||
end: GuestAddr,
|
||||
offset: GuestAddr,
|
||||
path: *const u8,
|
||||
path: Option<String>,
|
||||
flags: i32,
|
||||
is_priv: i32,
|
||||
}
|
||||
@ -227,17 +227,8 @@ impl MapInfo {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn path(&self) -> Option<&str> {
|
||||
if self.path.is_null() {
|
||||
None
|
||||
} else {
|
||||
unsafe {
|
||||
Some(from_utf8_unchecked(from_raw_parts(
|
||||
self.path,
|
||||
strlen(self.path),
|
||||
)))
|
||||
}
|
||||
}
|
||||
pub fn path(&self) -> Option<&String> {
|
||||
self.path.as_ref()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
@ -1,10 +1,10 @@
|
||||
use core::ffi::c_void;
|
||||
use core::{slice::from_raw_parts, str::from_utf8_unchecked};
|
||||
|
||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
use paste::paste;
|
||||
use strum_macros::EnumIter;
|
||||
|
||||
use crate::{extern_c_checked, GuestAddr, MapInfo};
|
||||
use crate::{extern_c_checked, libafl_mapinfo, strlen, GuestAddr, MapInfo};
|
||||
|
||||
extern_c_checked! {
|
||||
pub fn qemu_user_init(argc: i32, argv: *const *const u8, envp: *const *const u8) -> i32;
|
||||
@ -15,12 +15,6 @@ extern_c_checked! {
|
||||
pub fn libafl_get_brk() -> u64;
|
||||
pub fn libafl_set_brk(brk: u64) -> u64;
|
||||
|
||||
pub fn read_self_maps() -> *const c_void;
|
||||
pub fn free_self_maps(map_info: *const c_void);
|
||||
|
||||
pub fn libafl_maps_first(root: *const c_void) -> *const c_void;
|
||||
pub fn libafl_maps_next(node: *const c_void, ret: *mut MapInfo, is_root: bool) -> *const c_void;
|
||||
|
||||
pub static exec_path: *const u8;
|
||||
pub static guest_base: usize;
|
||||
pub static mut mmap_next_start: GuestAddr;
|
||||
@ -35,3 +29,30 @@ pub enum VerifyAccess {
|
||||
Read = libc::PROT_READ,
|
||||
Write = libc::PROT_READ | libc::PROT_WRITE,
|
||||
}
|
||||
|
||||
impl From<libafl_mapinfo> for MapInfo {
|
||||
fn from(map_info: libafl_mapinfo) -> Self {
|
||||
let path: Option<String> = if map_info.path.is_null() {
|
||||
None
|
||||
} else {
|
||||
unsafe {
|
||||
Some(
|
||||
from_utf8_unchecked(from_raw_parts(
|
||||
map_info.path as *const u8,
|
||||
strlen(map_info.path as *const u8),
|
||||
))
|
||||
.to_string(),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
MapInfo {
|
||||
start: map_info.start,
|
||||
end: map_info.end,
|
||||
offset: map_info.offset,
|
||||
path,
|
||||
flags: map_info.flags,
|
||||
is_priv: map_info.is_priv,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11650,6 +11650,95 @@ impl Default for RBNode {
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct RBRoot {
|
||||
pub rb_node: *mut RBNode,
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout_RBRoot() {
|
||||
const UNINIT: ::std::mem::MaybeUninit<RBRoot> = ::std::mem::MaybeUninit::uninit();
|
||||
let ptr = UNINIT.as_ptr();
|
||||
assert_eq!(
|
||||
::std::mem::size_of::<RBRoot>(),
|
||||
8usize,
|
||||
concat!("Size of: ", stringify!(RBRoot))
|
||||
);
|
||||
assert_eq!(
|
||||
::std::mem::align_of::<RBRoot>(),
|
||||
8usize,
|
||||
concat!("Alignment of ", stringify!(RBRoot))
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { ::std::ptr::addr_of!((*ptr).rb_node) as usize - ptr as usize },
|
||||
0usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(RBRoot),
|
||||
"::",
|
||||
stringify!(rb_node)
|
||||
)
|
||||
);
|
||||
}
|
||||
impl Default for RBRoot {
|
||||
fn default() -> Self {
|
||||
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
|
||||
unsafe {
|
||||
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
|
||||
s.assume_init()
|
||||
}
|
||||
}
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct RBRootLeftCached {
|
||||
pub rb_root: RBRoot,
|
||||
pub rb_leftmost: *mut RBNode,
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout_RBRootLeftCached() {
|
||||
const UNINIT: ::std::mem::MaybeUninit<RBRootLeftCached> = ::std::mem::MaybeUninit::uninit();
|
||||
let ptr = UNINIT.as_ptr();
|
||||
assert_eq!(
|
||||
::std::mem::size_of::<RBRootLeftCached>(),
|
||||
16usize,
|
||||
concat!("Size of: ", stringify!(RBRootLeftCached))
|
||||
);
|
||||
assert_eq!(
|
||||
::std::mem::align_of::<RBRootLeftCached>(),
|
||||
8usize,
|
||||
concat!("Alignment of ", stringify!(RBRootLeftCached))
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { ::std::ptr::addr_of!((*ptr).rb_root) as usize - ptr as usize },
|
||||
0usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(RBRootLeftCached),
|
||||
"::",
|
||||
stringify!(rb_root)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { ::std::ptr::addr_of!((*ptr).rb_leftmost) as usize - ptr as usize },
|
||||
8usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(RBRootLeftCached),
|
||||
"::",
|
||||
stringify!(rb_leftmost)
|
||||
)
|
||||
);
|
||||
}
|
||||
impl Default for RBRootLeftCached {
|
||||
fn default() -> Self {
|
||||
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
|
||||
unsafe {
|
||||
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
|
||||
s.assume_init()
|
||||
}
|
||||
}
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct IntervalTreeNode {
|
||||
pub rb: RBNode,
|
||||
pub start: u64,
|
||||
@ -11720,6 +11809,7 @@ impl Default for IntervalTreeNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
pub type IntervalTreeRoot = RBRootLeftCached;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct tb_tc {
|
||||
@ -11983,6 +12073,14 @@ extern "C" {
|
||||
extern "C" {
|
||||
pub fn target_munmap(start: abi_ulong, len: abi_ulong) -> ::std::os::raw::c_int;
|
||||
}
|
||||
extern "C" {
|
||||
#[doc = " read_self_maps:\n\n Read /proc/self/maps and return a tree of MapInfo structures."]
|
||||
pub fn read_self_maps() -> *mut IntervalTreeRoot;
|
||||
}
|
||||
extern "C" {
|
||||
#[doc = " free_self_maps:\n @info: an interval tree\n\n Free a tree of MapInfo structures."]
|
||||
pub fn free_self_maps(root: *mut IntervalTreeRoot);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn libafl_breakpoint_invalidate(cpu: *mut CPUState, pc: target_ulong);
|
||||
}
|
||||
@ -12311,6 +12409,109 @@ extern "C" {
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct libafl_mapinfo {
|
||||
pub start: target_ulong,
|
||||
pub end: target_ulong,
|
||||
pub offset: target_ulong,
|
||||
pub path: *const ::std::os::raw::c_char,
|
||||
pub flags: ::std::os::raw::c_int,
|
||||
pub is_priv: ::std::os::raw::c_int,
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout_libafl_mapinfo() {
|
||||
const UNINIT: ::std::mem::MaybeUninit<libafl_mapinfo> = ::std::mem::MaybeUninit::uninit();
|
||||
let ptr = UNINIT.as_ptr();
|
||||
assert_eq!(
|
||||
::std::mem::size_of::<libafl_mapinfo>(),
|
||||
40usize,
|
||||
concat!("Size of: ", stringify!(libafl_mapinfo))
|
||||
);
|
||||
assert_eq!(
|
||||
::std::mem::align_of::<libafl_mapinfo>(),
|
||||
8usize,
|
||||
concat!("Alignment of ", stringify!(libafl_mapinfo))
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { ::std::ptr::addr_of!((*ptr).start) as usize - ptr as usize },
|
||||
0usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(libafl_mapinfo),
|
||||
"::",
|
||||
stringify!(start)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { ::std::ptr::addr_of!((*ptr).end) as usize - ptr as usize },
|
||||
8usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(libafl_mapinfo),
|
||||
"::",
|
||||
stringify!(end)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { ::std::ptr::addr_of!((*ptr).offset) as usize - ptr as usize },
|
||||
16usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(libafl_mapinfo),
|
||||
"::",
|
||||
stringify!(offset)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { ::std::ptr::addr_of!((*ptr).path) as usize - ptr as usize },
|
||||
24usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(libafl_mapinfo),
|
||||
"::",
|
||||
stringify!(path)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { ::std::ptr::addr_of!((*ptr).flags) as usize - ptr as usize },
|
||||
32usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(libafl_mapinfo),
|
||||
"::",
|
||||
stringify!(flags)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { ::std::ptr::addr_of!((*ptr).is_priv) as usize - ptr as usize },
|
||||
36usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(libafl_mapinfo),
|
||||
"::",
|
||||
stringify!(is_priv)
|
||||
)
|
||||
);
|
||||
}
|
||||
impl Default for libafl_mapinfo {
|
||||
fn default() -> Self {
|
||||
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
|
||||
unsafe {
|
||||
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
|
||||
s.assume_init()
|
||||
}
|
||||
}
|
||||
}
|
||||
extern "C" {
|
||||
pub fn libafl_maps_first(map_info: *mut IntervalTreeRoot) -> *mut IntervalTreeNode;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn libafl_maps_next(
|
||||
node: *mut IntervalTreeNode,
|
||||
ret: *mut libafl_mapinfo,
|
||||
) -> *mut IntervalTreeNode;
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct AccelCPUClass {
|
||||
pub parent_class: ObjectClass,
|
||||
pub cpu_class_init: ::std::option::Option<unsafe extern "C" fn(cc: *mut CPUClass)>,
|
||||
@ -13669,6 +13870,9 @@ extern "C" {
|
||||
extern "C" {
|
||||
pub fn libafl_qemu_remove_new_thread_hook(num: usize) -> ::std::os::raw::c_int;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn libafl_tcg_gen_asan(addr: *mut TCGTemp, size: usize);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn libafl_jit_trace_edge_hitcount(data: u64, id: u64) -> usize;
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
use core::{ffi::c_void, mem::MaybeUninit, ptr::copy_nonoverlapping};
|
||||
use core::{mem::MaybeUninit, ptr::copy_nonoverlapping};
|
||||
use std::{cell::OnceCell, slice::from_raw_parts, str::from_utf8_unchecked};
|
||||
|
||||
use libafl_qemu_sys::{
|
||||
exec_path, free_self_maps, guest_base, libafl_dump_core_hook, libafl_force_dfl, libafl_get_brk,
|
||||
libafl_load_addr, libafl_maps_first, libafl_maps_next, libafl_qemu_run, libafl_set_brk,
|
||||
mmap_next_start, read_self_maps, strlen, GuestAddr, GuestUsize, MapInfo, MmapPerms,
|
||||
VerifyAccess,
|
||||
mmap_next_start, read_self_maps, strlen, GuestAddr, GuestUsize, IntervalTreeNode,
|
||||
IntervalTreeRoot, MapInfo, MmapPerms, VerifyAccess,
|
||||
};
|
||||
use libc::c_int;
|
||||
#[cfg(feature = "python")]
|
||||
@ -27,8 +27,8 @@ pub enum HandlerError {
|
||||
|
||||
#[cfg_attr(feature = "python", pyclass(unsendable))]
|
||||
pub struct GuestMaps {
|
||||
maps_root: *const c_void,
|
||||
maps_node: *const c_void,
|
||||
maps_root: *mut IntervalTreeRoot,
|
||||
maps_node: *mut IntervalTreeNode,
|
||||
}
|
||||
|
||||
// Consider a private new only for Emulator
|
||||
@ -51,13 +51,16 @@ impl Iterator for GuestMaps {
|
||||
|
||||
#[allow(clippy::uninit_assumed_init)]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.maps_node.is_null() {
|
||||
return None;
|
||||
}
|
||||
unsafe {
|
||||
let mut ret = MaybeUninit::uninit();
|
||||
self.maps_node = libafl_maps_next(self.maps_node, ret.as_mut_ptr(), false);
|
||||
Some(ret.assume_init())
|
||||
|
||||
self.maps_node = libafl_maps_next(self.maps_node, ret.as_mut_ptr());
|
||||
|
||||
if self.maps_node.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(ret.assume_init().into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user