From 935100e7a7b230549576f7c9511715157c76d8ff Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Thu, 2 May 2024 09:31:54 +0200 Subject: [PATCH] Qemu update to latest version + various fixes (#2119) * Fix maps iterator * Added paranoid debug feature. * Fix snapshot bugs. * Update qemu-libafl-bridge. * Added auto-generation of `x86_64_stub_bindings.rs` . * Fix sync_exit calling to stopped CPU. --- fuzzers/qemu_cmin/src/fuzzer.rs | 1 - libafl_qemu/Cargo.toml | 1 + libafl_qemu/libafl_qemu_build/Cargo.toml | 2 + libafl_qemu/libafl_qemu_build/src/bindings.rs | 30 +- libafl_qemu/libafl_qemu_build/src/build.rs | 62 ++- libafl_qemu/libafl_qemu_build/src/lib.rs | 47 ++ libafl_qemu/libafl_qemu_sys/Cargo.toml | 3 + libafl_qemu/libafl_qemu_sys/build_linux.rs | 32 +- libafl_qemu/libafl_qemu_sys/src/lib.rs | 1 + .../src/x86_64_stub_bindings.rs | 513 +++++++++--------- libafl_qemu/src/command.rs | 19 +- libafl_qemu/src/emu/mod.rs | 35 +- libafl_qemu/src/emu/usermode.rs | 30 +- libafl_qemu/src/helpers/asan.rs | 4 +- libafl_qemu/src/helpers/snapshot.rs | 132 ++++- libafl_qemu/src/sync_exit.rs | 26 +- 16 files changed, 587 insertions(+), 351 deletions(-) diff --git a/fuzzers/qemu_cmin/src/fuzzer.rs b/fuzzers/qemu_cmin/src/fuzzer.rs index c696e95524..18f4cbc7bf 100644 --- a/fuzzers/qemu_cmin/src/fuzzer.rs +++ b/fuzzers/qemu_cmin/src/fuzzer.rs @@ -29,7 +29,6 @@ use libafl_bolts::{ use libafl_qemu::{ edges::{QemuEdgeCoverageChildHelper, EDGES_MAP_PTR, EDGES_MAP_SIZE_IN_USE}, elf::EasyElf, - emu::Emulator, ArchExtras, CallingConvention, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExitReason, QemuExitReasonError, QemuForkExecutor, QemuHooks, QemuShutdownCause, Regs, }; diff --git a/libafl_qemu/Cargo.toml b/libafl_qemu/Cargo.toml index ba3390a6b3..116607ed8e 100644 --- a/libafl_qemu/Cargo.toml +++ b/libafl_qemu/Cargo.toml @@ -19,6 +19,7 @@ rustdoc-args = ["--cfg", "docsrs"] default = ["fork", "build_libgasan", "build_libqasan", "serdeany_autoreg", "injections"] clippy = [] # special feature for clippy, don't use in normal projects§ document-features = ["dep:document-features"] +paranoid_debug = ["libafl_qemu_sys/paranoid_debug"] # Will perform as many checks as possible. The target will be greatly slowed down. #! # Feature Flags #! ### General Features diff --git a/libafl_qemu/libafl_qemu_build/Cargo.toml b/libafl_qemu/libafl_qemu_build/Cargo.toml index f7d9d39bcd..e864e4c0d3 100644 --- a/libafl_qemu/libafl_qemu_build/Cargo.toml +++ b/libafl_qemu/libafl_qemu_build/Cargo.toml @@ -26,6 +26,8 @@ slirp = [] # build qemu with host libslirp (for user networking) clippy = [] # special feature for clippy, don't use in normal projects§ +paranoid_debug = [] # Will perform as many checks as possible. The target will be greatly slowed down. + [dependencies] bindgen = "0.69.4" which = "4.4" diff --git a/libafl_qemu/libafl_qemu_build/src/bindings.rs b/libafl_qemu/libafl_qemu_build/src/bindings.rs index 162452cdf6..bf676273c5 100644 --- a/libafl_qemu/libafl_qemu_build/src/bindings.rs +++ b/libafl_qemu/libafl_qemu_build/src/bindings.rs @@ -1,7 +1,9 @@ -use std::{collections::hash_map, fs, hash::Hasher, io::Read, path::Path}; +use std::path::Path; use bindgen::{BindgenError, Bindings}; +use crate::store_generated_content_if_different; + const WRAPPER_HEADER: &str = r#" // https://github.com/rust-lang/rust-bindgen/issues/2500 @@ -91,31 +93,8 @@ pub fn generate( clang_args: Vec, ) -> Result { let wrapper_h = build_dir.join("wrapper.h"); - let existing_wrapper_h = fs::File::open(&wrapper_h); - let mut must_rewrite_wrapper = true; - // Check if equivalent wrapper file already exists without relying on filesystem timestamp. - if let Ok(mut wrapper_file) = existing_wrapper_h { - let mut existing_wrapper_content = Vec::with_capacity(WRAPPER_HEADER.len()); - wrapper_file - .read_to_end(existing_wrapper_content.as_mut()) - .unwrap(); - - let mut existing_wrapper_hasher = hash_map::DefaultHasher::new(); - existing_wrapper_hasher.write(existing_wrapper_content.as_ref()); - - let mut wrapper_h_hasher = hash_map::DefaultHasher::new(); - wrapper_h_hasher.write(WRAPPER_HEADER.as_bytes()); - - // Check if wrappers are the same - if existing_wrapper_hasher.finish() == wrapper_h_hasher.finish() { - must_rewrite_wrapper = false; - } - } - - if must_rewrite_wrapper { - fs::write(&wrapper_h, WRAPPER_HEADER).expect("Unable to write wrapper.h"); - } + store_generated_content_if_different(&wrapper_h, WRAPPER_HEADER.as_bytes()); let bindings = bindgen::Builder::default() .derive_debug(true) @@ -166,6 +145,7 @@ pub fn generate( .allowlist_function("libafl_.*") .allowlist_function("read_self_maps") .allowlist_function("free_self_maps") + .allowlist_function("pageflags_get_root") .blocklist_function("main_loop_wait") // bindgen issue #1313 .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())); diff --git a/libafl_qemu/libafl_qemu_build/src/build.rs b/libafl_qemu/libafl_qemu_build/src/build.rs index 45412390c8..62e7afd48c 100644 --- a/libafl_qemu/libafl_qemu_build/src/build.rs +++ b/libafl_qemu/libafl_qemu_build/src/build.rs @@ -2,6 +2,7 @@ use std::{ env, fs, path::{Path, PathBuf}, process::Command, + str::FromStr, }; use which::which; @@ -10,7 +11,7 @@ use crate::cargo_add_rpath; const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge"; const QEMU_DIRNAME: &str = "qemu-libafl-bridge"; -const QEMU_REVISION: &str = "c9519ee8b6cb1ba54b7df1001f7f39f07218d514"; +const QEMU_REVISION: &str = "538e6b02c36838e0de469b39dd03fef05678444f"; #[allow(clippy::module_name_repetitions)] pub struct BuildResult { @@ -70,28 +71,32 @@ fn configure_qemu( ) -> Command { let mut cmd = Command::new("./configure"); + let linker_interceptor = qemu_path.join("linker_interceptor.py"); + let linker_interceptor_plus_plus = qemu_path.join("linker_interceptor++.py"); + + println!("cargo:rerun-if-changed={}", linker_interceptor.display()); + println!( + "cargo:rerun-if-changed={}", + linker_interceptor_plus_plus.display() + ); + // Set common options for usermode and systemmode cmd.current_dir(qemu_path) .env("__LIBAFL_QEMU_CONFIGURE", "") .env("__LIBAFL_QEMU_BUILD_OUT", build_dir.join("linkinfo.json")) .env("__LIBAFL_QEMU_BUILD_CC", cc_compiler.path()) .env("__LIBAFL_QEMU_BUILD_CXX", cpp_compiler.path()) - .arg(&format!( - "--cc={}", - qemu_path.join("linker_interceptor.py").display() - )) - .arg(&format!( - "--cxx={}", - qemu_path.join("linker_interceptor++.py").display() - )) + .arg(&format!("--cc={}", linker_interceptor.display())) + .arg(&format!("--cxx={}", linker_interceptor_plus_plus.display())) .arg("--as-shared-lib") .arg(&format!("--target-list={cpu_target}-{target_suffix}")) - .arg("--disable-bsd-user") - .arg("--disable-capstone"); + // .arg("--disable-capstone") + .arg("--disable-bsd-user"); - if cfg!(debug_assertions) { - // cmd.arg("--enable-debug"); - // .arg("--enable-debug-tcg"); + if cfg!(feature = "paranoid_debug") { + cmd.arg("--enable-debug") + .arg("--enable-debug-tcg") + .arg("--enable-sanitizers"); } if is_usermode { @@ -273,6 +278,7 @@ pub fn build( println!("cargo:rerun-if-env-changed=LIBAFL_QEMU_DIR"); println!("cargo:rerun-if-env-changed=LIBAFL_QEMU_CLONE_DIR"); + println!("cargo:rerun-if-env-changed=LIBAFL_QEMU_FORCE_BUILD"); println!("cargo:rerun-if-env-changed=LIBAFL_QEMU_FORCE_CONFIGURE"); println!("cargo:rerun-if-env-changed=LIBAFL_QEMU_NO_BUILD"); @@ -449,6 +455,23 @@ pub fn build( } */ + let compile_commands_string = &fs::read_to_string(libafl_qemu_build_dir.join("linkinfo.json")) + .expect("Failed to read linkinfo.json"); + + let linkinfo = json::parse(compile_commands_string).expect("Failed to parse linkinfo.json"); + + for source in linkinfo["sources"].members() { + let source_path = PathBuf::from_str(source.as_str().unwrap()).unwrap(); + + let source_path = if source_path.is_relative() { + libafl_qemu_build_dir.join(source_path) + } else { + source_path + }; + + println!("cargo:rerun-if-changed={}", source_path.display()); + } + if cfg!(feature = "shared") { let qemu_build_dir_str = libafl_qemu_build_dir .to_str() @@ -457,12 +480,6 @@ pub fn build( println!("cargo:rustc-link-lib=dylib={output_lib_link}"); cargo_add_rpath(qemu_build_dir_str); } else { - let compile_commands_string = - &fs::read_to_string(libafl_qemu_build_dir.join("linkinfo.json")) - .expect("Failed to read linkinfo.json"); - - let linkinfo = json::parse(compile_commands_string).expect("Failed to parse linkinfo.json"); - let mut cmd = vec![]; for arg in linkinfo["cmd"].members() { cmd.push( @@ -619,6 +636,11 @@ pub fn build( } } + if cfg!(feature = "paranoid_debug") { + println!("cargo:rustc-link-lib=ubsan"); + println!("cargo:rustc-link-lib=asan"); + } + /* println!("cargo:rustc-link-lib=rt"); println!("cargo:rustc-link-lib=gmodule-2.0"); diff --git a/libafl_qemu/libafl_qemu_build/src/lib.rs b/libafl_qemu/libafl_qemu_build/src/lib.rs index 7643c11d99..0bb58d9443 100644 --- a/libafl_qemu/libafl_qemu_build/src/lib.rs +++ b/libafl_qemu/libafl_qemu_build/src/lib.rs @@ -1,6 +1,10 @@ #![allow(clippy::missing_panics_doc)] use std::{ + collections::hash_map, env, fs, + fs::File, + hash::Hasher, + io::{Read, Seek, SeekFrom, Write}, path::{Path, PathBuf}, process::Command, ptr::addr_of_mut, @@ -243,3 +247,46 @@ fn include_path(build_dir: &Path, path: &str) -> String { build_dir.join(include_path).display().to_string() } } + +pub fn store_generated_content_if_different(file_to_update: &PathBuf, fresh_content: &[u8]) { + let mut must_rewrite_file = true; + + // Check if equivalent file already exists without relying on filesystem timestamp. + let mut file_to_check = + if let Ok(mut wrapper_file) = File::options().read(true).write(true).open(file_to_update) { + let mut existing_file_content = Vec::with_capacity(fresh_content.len()); + wrapper_file + .read_to_end(existing_file_content.as_mut()) + .unwrap(); + + let mut existing_wrapper_hasher = hash_map::DefaultHasher::new(); + existing_wrapper_hasher.write(existing_file_content.as_ref()); + + let mut wrapper_h_hasher = hash_map::DefaultHasher::new(); + wrapper_h_hasher.write(fresh_content); + + // Check if wrappers are the same + if existing_wrapper_hasher.finish() == wrapper_h_hasher.finish() { + must_rewrite_file = false; + } + + // Reset file cursor if it's going to be rewritten + if must_rewrite_file { + wrapper_file.set_len(0).expect("Could not set file len"); + wrapper_file + .seek(SeekFrom::Start(0)) + .expect("Could not seek file to beginning"); + } + + wrapper_file + } else { + File::create(file_to_update) + .unwrap_or_else(|_| panic!("Could not create {}", file_to_update.display())) + }; + + if must_rewrite_file { + file_to_check + .write_all(fresh_content) + .unwrap_or_else(|_| panic!("Unable to write in {}", file_to_update.display())); + } +} diff --git a/libafl_qemu/libafl_qemu_sys/Cargo.toml b/libafl_qemu/libafl_qemu_sys/Cargo.toml index ad812715a0..b947303b8f 100644 --- a/libafl_qemu/libafl_qemu_sys/Cargo.toml +++ b/libafl_qemu/libafl_qemu_sys/Cargo.toml @@ -38,6 +38,8 @@ shared = [ "libafl_qemu_build/shared" ] clippy = [ "libafl_qemu_build/clippy" ] # special feature for clippy, don't use in normal projects +paranoid_debug = ["libafl_qemu_build/paranoid_debug"] # Will perform as many checks as possible. The target will be greatly slowed down. + [dependencies] paste = "1" num_enum = "0.7" @@ -49,3 +51,4 @@ pyo3 = { version = "0.18", optional = true } [build-dependencies] libafl_qemu_build = { path = "../libafl_qemu_build", version = "0.12.0" } pyo3-build-config = { version = "0.18", optional = true } +rustversion = "1.0" diff --git a/libafl_qemu/libafl_qemu_sys/build_linux.rs b/libafl_qemu/libafl_qemu_sys/build_linux.rs index 449e606dff..e59480c6b1 100644 --- a/libafl_qemu/libafl_qemu_sys/build_linux.rs +++ b/libafl_qemu/libafl_qemu_sys/build_linux.rs @@ -2,6 +2,11 @@ use std::{env, fs::copy, path::PathBuf}; use libafl_qemu_build::build_with_bindings; +#[rustversion::nightly] +use std::fs; +#[rustversion::nightly] +use libafl_qemu_build::store_generated_content_if_different; + #[macro_export] macro_rules! assert_unique_feature { () => {}; @@ -14,6 +19,18 @@ macro_rules! assert_unique_feature { } } +#[rustversion::nightly] +fn maybe_generate_stub_bindings(cpu_target: &str, emulation_mode: &str, stub_bindings_file: &PathBuf, bindings_file: &PathBuf) { + if cpu_target == "x86_64" && emulation_mode == "usermode" { + store_generated_content_if_different(stub_bindings_file, fs::read(bindings_file).expect("Could not read generated bindings file").as_slice()); + } +} + +#[rustversion::not(nightly)] +fn maybe_generate_stub_bindings(_cpu_target: &str, _emulation_mode: &str, _stub_bindings_file: &PathBuf, _bindings_file: &PathBuf) { + // Do nothing +} + pub fn build() { // Make sure that exactly one qemu mode is set assert_unique_feature!("usermode", "systemmode"); @@ -73,10 +90,14 @@ pub fn build() { let out_dir = env::var("OUT_DIR").unwrap(); let out_dir = PathBuf::from(out_dir); let bindings_file = out_dir.join("bindings.rs"); + + let src_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let src_dir = PathBuf::from(src_dir); + let stub_bindings_file = src_dir.join("src/x86_64_stub_bindings.rs"); - if std::env::var("DOCS_RS").is_ok() || cfg!(feature = "clippy") { + if env::var("DOCS_RS").is_ok() || cfg!(feature = "clippy") { // Only build when we're not generating docs and not in clippy - copy("src/x86_64_stub_bindings.rs", bindings_file).expect("Failed to copy the bindings stub"); + copy(stub_bindings_file, bindings_file).expect("Failed to copy the bindings stub"); return; } @@ -87,4 +108,9 @@ pub fn build() { jobs, &bindings_file, ); -} + + println!("cargo:rerun-if-changed={}", stub_bindings_file.display()); + + // If the bindings are built and differ from the current stub, replace it with the freshly generated bindings + maybe_generate_stub_bindings(&cpu_target, &emulation_mode, &stub_bindings_file, &bindings_file); +} \ No newline at end of file diff --git a/libafl_qemu/libafl_qemu_sys/src/lib.rs b/libafl_qemu/libafl_qemu_sys/src/lib.rs index e8ce35b4aa..63722c5c33 100644 --- a/libafl_qemu/libafl_qemu_sys/src/lib.rs +++ b/libafl_qemu/libafl_qemu_sys/src/lib.rs @@ -21,6 +21,7 @@ mod bindings { pub use bindings::*; #[cfg(any(feature = "clippy", not(target_os = "linux")))] +#[rustfmt::skip] mod x86_64_stub_bindings; #[cfg(emulation_mode = "usermode")] diff --git a/libafl_qemu/libafl_qemu_sys/src/x86_64_stub_bindings.rs b/libafl_qemu/libafl_qemu_sys/src/x86_64_stub_bindings.rs index 02642508f5..be87633126 100644 --- a/libafl_qemu/libafl_qemu_sys/src/x86_64_stub_bindings.rs +++ b/libafl_qemu/libafl_qemu_sys/src/x86_64_stub_bindings.rs @@ -101,7 +101,7 @@ impl __IncompleteArrayField { ::std::slice::from_raw_parts(self.as_ptr(), len) } #[inline] - pub unsafe fn as_slice_mut(&mut self, len: usize) -> &mut [T] { + pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] { ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len) } } @@ -1293,6 +1293,11 @@ pub struct RAMBlock { } #[repr(C)] #[derive(Debug, Copy, Clone)] +pub struct TCGCPUOps { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] pub struct Visitor { _unused: [u8; 0], } @@ -1589,7 +1594,7 @@ fn bindgen_test_layout_QemuLockCnt() { #[derive(Debug, Default, Copy, Clone)] pub struct MemTxAttrs { pub _bitfield_align_1: [u16; 0], - pub _bitfield_1: __BindgenBitfieldUnit<[u8; 4usize]>, + pub _bitfield_1: __BindgenBitfieldUnit<[u8; 3usize]>, } #[test] fn bindgen_test_layout_MemTxAttrs() { @@ -1672,50 +1677,6 @@ impl MemTxAttrs { } } #[inline] - pub fn byte_swap(&self) -> ::std::os::raw::c_uint { - unsafe { ::std::mem::transmute(self._bitfield_1.get(22usize, 1u8) as u32) } - } - #[inline] - pub fn set_byte_swap(&mut self, val: ::std::os::raw::c_uint) { - unsafe { - let val: u32 = ::std::mem::transmute(val); - self._bitfield_1.set(22usize, 1u8, val as u64) - } - } - #[inline] - pub fn target_tlb_bit0(&self) -> ::std::os::raw::c_uint { - unsafe { ::std::mem::transmute(self._bitfield_1.get(23usize, 1u8) as u32) } - } - #[inline] - pub fn set_target_tlb_bit0(&mut self, val: ::std::os::raw::c_uint) { - unsafe { - let val: u32 = ::std::mem::transmute(val); - self._bitfield_1.set(23usize, 1u8, val as u64) - } - } - #[inline] - pub fn target_tlb_bit1(&self) -> ::std::os::raw::c_uint { - unsafe { ::std::mem::transmute(self._bitfield_1.get(24usize, 1u8) as u32) } - } - #[inline] - pub fn set_target_tlb_bit1(&mut self, val: ::std::os::raw::c_uint) { - unsafe { - let val: u32 = ::std::mem::transmute(val); - self._bitfield_1.set(24usize, 1u8, val as u64) - } - } - #[inline] - pub fn target_tlb_bit2(&self) -> ::std::os::raw::c_uint { - unsafe { ::std::mem::transmute(self._bitfield_1.get(25usize, 1u8) as u32) } - } - #[inline] - pub fn set_target_tlb_bit2(&mut self, val: ::std::os::raw::c_uint) { - unsafe { - let val: u32 = ::std::mem::transmute(val); - self._bitfield_1.set(25usize, 1u8, val as u64) - } - } - #[inline] pub fn new_bitfield_1( unspecified: ::std::os::raw::c_uint, secure: ::std::os::raw::c_uint, @@ -1723,12 +1684,8 @@ impl MemTxAttrs { user: ::std::os::raw::c_uint, memory: ::std::os::raw::c_uint, requester_id: ::std::os::raw::c_uint, - byte_swap: ::std::os::raw::c_uint, - target_tlb_bit0: ::std::os::raw::c_uint, - target_tlb_bit1: ::std::os::raw::c_uint, - target_tlb_bit2: ::std::os::raw::c_uint, - ) -> __BindgenBitfieldUnit<[u8; 4usize]> { - let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 4usize]> = Default::default(); + ) -> __BindgenBitfieldUnit<[u8; 3usize]> { + let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 3usize]> = Default::default(); __bindgen_bitfield_unit.set(0usize, 1u8, { let unspecified: u32 = unsafe { ::std::mem::transmute(unspecified) }; unspecified as u64 @@ -1753,22 +1710,6 @@ impl MemTxAttrs { let requester_id: u32 = unsafe { ::std::mem::transmute(requester_id) }; requester_id as u64 }); - __bindgen_bitfield_unit.set(22usize, 1u8, { - let byte_swap: u32 = unsafe { ::std::mem::transmute(byte_swap) }; - byte_swap as u64 - }); - __bindgen_bitfield_unit.set(23usize, 1u8, { - let target_tlb_bit0: u32 = unsafe { ::std::mem::transmute(target_tlb_bit0) }; - target_tlb_bit0 as u64 - }); - __bindgen_bitfield_unit.set(24usize, 1u8, { - let target_tlb_bit1: u32 = unsafe { ::std::mem::transmute(target_tlb_bit1) }; - target_tlb_bit1 as u64 - }); - __bindgen_bitfield_unit.set(25usize, 1u8, { - let target_tlb_bit2: u32 = unsafe { ::std::mem::transmute(target_tlb_bit2) }; - target_tlb_bit2 as u64 - }); __bindgen_bitfield_unit } } @@ -2399,6 +2340,7 @@ pub type DeviceReset = ::std::option::Option(), - 208usize, + 216usize, concat!("Size of: ", stringify!(disassemble_info)) ); assert_eq!( @@ -4312,8 +4255,18 @@ fn bindgen_test_layout_disassemble_info() { ) ); assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).target_info) as usize - ptr as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).show_opcodes) as usize - ptr as usize }, 184usize, + concat!( + "Offset of field: ", + stringify!(disassemble_info), + "::", + stringify!(show_opcodes) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).target_info) as usize - ptr as usize }, + 192usize, concat!( "Offset of field: ", stringify!(disassemble_info), @@ -4323,7 +4276,7 @@ fn bindgen_test_layout_disassemble_info() { ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).cap_arch) as usize - ptr as usize }, - 192usize, + 200usize, concat!( "Offset of field: ", stringify!(disassemble_info), @@ -4333,7 +4286,7 @@ fn bindgen_test_layout_disassemble_info() { ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).cap_mode) as usize - ptr as usize }, - 196usize, + 204usize, concat!( "Offset of field: ", stringify!(disassemble_info), @@ -4343,7 +4296,7 @@ fn bindgen_test_layout_disassemble_info() { ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).cap_insn_unit) as usize - ptr as usize }, - 200usize, + 208usize, concat!( "Offset of field: ", stringify!(disassemble_info), @@ -4353,7 +4306,7 @@ fn bindgen_test_layout_disassemble_info() { ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).cap_insn_split) as usize - ptr as usize }, - 204usize, + 212usize, concat!( "Offset of field: ", stringify!(disassemble_info), @@ -4371,18 +4324,9 @@ impl Default for disassemble_info { } } } +pub type hwaddr = u64; #[doc = " vaddr:\n Type wide enough to contain any #target_ulong virtual address."] pub type vaddr = u64; -extern "C" { - pub fn cpu_memory_rw_debug( - cpu: *mut CPUState, - addr: vaddr, - ptr: *mut ::std::os::raw::c_void, - len: usize, - is_write: bool, - ) -> ::std::os::raw::c_int; -} -pub type hwaddr = u64; #[repr(C)] #[derive(Copy, Clone)] pub union CPUTLBEntry { @@ -4585,15 +4529,10 @@ impl ::std::ops::BitAndAssign for ShutdownCause { pub struct ShutdownCause(pub ::std::os::raw::c_uint); #[repr(C)] #[derive(Debug, Copy, Clone)] -pub struct TCGCPUOps { - _unused: [u8; 0], -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] pub struct SysemuCPUOps { _unused: [u8; 0], } -#[doc = " CPUClass:\n @class_by_name: Callback to map -cpu command line model name to an\n instantiatable CPU type.\n @parse_features: Callback to parse command line arguments.\n @reset_dump_flags: #CPUDumpFlags to use for reset logging.\n @has_work: Callback for checking if there is work to do.\n @memory_rw_debug: Callback for GDB memory access.\n @dump_state: Callback for dumping state.\n @query_cpu_fast:\n Fill in target specific information for the \"query-cpus-fast\"\n QAPI call.\n @get_arch_id: Callback for getting architecture-dependent CPU ID.\n @set_pc: Callback for setting the Program Counter register. This\n should have the semantics used by the target architecture when\n setting the PC from a source such as an ELF file entry point;\n for example on Arm it will also set the Thumb mode bit based\n on the least significant bit of the new PC value.\n If the target behaviour here is anything other than \"set\n the PC register to the value passed in\" then the target must\n also implement the synchronize_from_tb hook.\n @get_pc: Callback for getting the Program Counter register.\n As above, with the semantics of the target architecture.\n @gdb_read_register: Callback for letting GDB read a register.\n @gdb_write_register: Callback for letting GDB write a register.\n @gdb_adjust_breakpoint: Callback for adjusting the address of a\n breakpoint. Used by AVR to handle a gdb mis-feature with\n its Harvard architecture split code and data.\n @gdb_num_core_regs: Number of core registers accessible to GDB.\n @gdb_core_xml_file: File name for core registers GDB XML description.\n @gdb_stop_before_watchpoint: Indicates whether GDB expects the CPU to stop\n before the insn which triggers a watchpoint rather than after it.\n @gdb_arch_name: Optional callback that returns the architecture name known\n to GDB. The caller must free the returned string with g_free.\n @gdb_get_dynamic_xml: Callback to return dynamically generated XML for the\n gdb stub. Returns a pointer to the XML contents for the specified XML file\n or NULL if the CPU doesn't have a dynamically generated content for it.\n @disas_set_info: Setup architecture specific components of disassembly info\n @adjust_watchpoint_address: Perform a target-specific adjustment to an\n address before attempting to match it against watchpoints.\n @deprecation_note: If this CPUClass is deprecated, this field provides\n related information.\n\n Represents a CPU family or model."] +#[doc = " CPUClass:\n @class_by_name: Callback to map -cpu command line model name to an\n instantiatable CPU type.\n @parse_features: Callback to parse command line arguments.\n @reset_dump_flags: #CPUDumpFlags to use for reset logging.\n @has_work: Callback for checking if there is work to do.\n @mmu_index: Callback for choosing softmmu mmu index;\n may be used internally by memory_rw_debug without TCG.\n @memory_rw_debug: Callback for GDB memory access.\n @dump_state: Callback for dumping state.\n @query_cpu_fast:\n Fill in target specific information for the \"query-cpus-fast\"\n QAPI call.\n @get_arch_id: Callback for getting architecture-dependent CPU ID.\n @set_pc: Callback for setting the Program Counter register. This\n should have the semantics used by the target architecture when\n setting the PC from a source such as an ELF file entry point;\n for example on Arm it will also set the Thumb mode bit based\n on the least significant bit of the new PC value.\n If the target behaviour here is anything other than \"set\n the PC register to the value passed in\" then the target must\n also implement the synchronize_from_tb hook.\n @get_pc: Callback for getting the Program Counter register.\n As above, with the semantics of the target architecture.\n @gdb_read_register: Callback for letting GDB read a register.\n @gdb_write_register: Callback for letting GDB write a register.\n @gdb_adjust_breakpoint: Callback for adjusting the address of a\n breakpoint. Used by AVR to handle a gdb mis-feature with\n its Harvard architecture split code and data.\n @gdb_num_core_regs: Number of core registers accessible to GDB or 0 to infer\n from @gdb_core_xml_file.\n @gdb_core_xml_file: File name for core registers GDB XML description.\n @gdb_stop_before_watchpoint: Indicates whether GDB expects the CPU to stop\n before the insn which triggers a watchpoint rather than after it.\n @gdb_arch_name: Optional callback that returns the architecture name known\n to GDB. The caller must free the returned string with g_free.\n @disas_set_info: Setup architecture specific components of disassembly info\n @adjust_watchpoint_address: Perform a target-specific adjustment to an\n address before attempting to match it against watchpoints.\n @deprecation_note: If this CPUClass is deprecated, this field provides\n related information.\n\n Represents a CPU family or model."] #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct CPUClass { @@ -4609,6 +4548,9 @@ pub struct CPUClass { ), >, pub has_work: ::std::option::Option bool>, + pub mmu_index: ::std::option::Option< + unsafe extern "C" fn(cpu: *mut CPUState, ifetch: bool) -> ::std::os::raw::c_int, + >, pub memory_rw_debug: ::std::option::Option< unsafe extern "C" fn( cpu: *mut CPUState, @@ -4645,12 +4587,6 @@ pub struct CPUClass { pub gdb_core_xml_file: *const ::std::os::raw::c_char, pub gdb_arch_name: ::std::option::Option *const gchar>, - pub gdb_get_dynamic_xml: ::std::option::Option< - unsafe extern "C" fn( - cpu: *mut CPUState, - xmlname: *const ::std::os::raw::c_char, - ) -> *const ::std::os::raw::c_char, - >, pub disas_set_info: ::std::option::Option< unsafe extern "C" fn(cpu: *mut CPUState, info: *mut disassemble_info), >, @@ -4720,8 +4656,18 @@ fn bindgen_test_layout_CPUClass() { ) ); assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).memory_rw_debug) as usize - ptr as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).mmu_index) as usize - ptr as usize }, 200usize, + concat!( + "Offset of field: ", + stringify!(CPUClass), + "::", + stringify!(mmu_index) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).memory_rw_debug) as usize - ptr as usize }, + 208usize, concat!( "Offset of field: ", stringify!(CPUClass), @@ -4731,7 +4677,7 @@ fn bindgen_test_layout_CPUClass() { ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).dump_state) as usize - ptr as usize }, - 208usize, + 216usize, concat!( "Offset of field: ", stringify!(CPUClass), @@ -4741,7 +4687,7 @@ fn bindgen_test_layout_CPUClass() { ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).query_cpu_fast) as usize - ptr as usize }, - 216usize, + 224usize, concat!( "Offset of field: ", stringify!(CPUClass), @@ -4751,7 +4697,7 @@ fn bindgen_test_layout_CPUClass() { ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).get_arch_id) as usize - ptr as usize }, - 224usize, + 232usize, concat!( "Offset of field: ", stringify!(CPUClass), @@ -4761,7 +4707,7 @@ fn bindgen_test_layout_CPUClass() { ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).set_pc) as usize - ptr as usize }, - 232usize, + 240usize, concat!( "Offset of field: ", stringify!(CPUClass), @@ -4771,7 +4717,7 @@ fn bindgen_test_layout_CPUClass() { ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).get_pc) as usize - ptr as usize }, - 240usize, + 248usize, concat!( "Offset of field: ", stringify!(CPUClass), @@ -4781,7 +4727,7 @@ fn bindgen_test_layout_CPUClass() { ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).gdb_read_register) as usize - ptr as usize }, - 248usize, + 256usize, concat!( "Offset of field: ", stringify!(CPUClass), @@ -4791,7 +4737,7 @@ fn bindgen_test_layout_CPUClass() { ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).gdb_write_register) as usize - ptr as usize }, - 256usize, + 264usize, concat!( "Offset of field: ", stringify!(CPUClass), @@ -4801,7 +4747,7 @@ fn bindgen_test_layout_CPUClass() { ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).gdb_adjust_breakpoint) as usize - ptr as usize }, - 264usize, + 272usize, concat!( "Offset of field: ", stringify!(CPUClass), @@ -4811,7 +4757,7 @@ fn bindgen_test_layout_CPUClass() { ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).gdb_core_xml_file) as usize - ptr as usize }, - 272usize, + 280usize, concat!( "Offset of field: ", stringify!(CPUClass), @@ -4821,22 +4767,12 @@ fn bindgen_test_layout_CPUClass() { ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).gdb_arch_name) as usize - ptr as usize }, - 280usize, - concat!( - "Offset of field: ", - stringify!(CPUClass), - "::", - stringify!(gdb_arch_name) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).gdb_get_dynamic_xml) as usize - ptr as usize }, 288usize, concat!( "Offset of field: ", stringify!(CPUClass), "::", - stringify!(gdb_get_dynamic_xml) + stringify!(gdb_arch_name) ) ); assert_eq!( @@ -4947,6 +4883,7 @@ pub struct CPUTLBEntryFull { pub attrs: MemTxAttrs, pub prot: u8, pub lg_page_size: u8, + pub tlb_fill_flags: u8, pub slow_flags: [u8; 3usize], pub extra: CPUTLBEntryFull__bindgen_ty_1, } @@ -5119,8 +5056,18 @@ fn bindgen_test_layout_CPUTLBEntryFull() { ) ); assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).slow_flags) as usize - ptr as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).tlb_fill_flags) as usize - ptr as usize }, 22usize, + concat!( + "Offset of field: ", + stringify!(CPUTLBEntryFull), + "::", + stringify!(tlb_fill_flags) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).slow_flags) as usize - ptr as usize }, + 23usize, concat!( "Offset of field: ", stringify!(CPUTLBEntryFull), @@ -5130,7 +5077,7 @@ fn bindgen_test_layout_CPUTLBEntryFull() { ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).extra) as usize - ptr as usize }, - 25usize, + 26usize, concat!( "Offset of field: ", stringify!(CPUTLBEntryFull), @@ -5892,7 +5839,7 @@ pub struct kvm_run { pub struct qemu_work_item { _unused: [u8; 0], } -#[doc = " CPUState:\n @cpu_index: CPU index (informative).\n @cluster_index: Identifies which cluster this CPU is in.\n For boards which don't define clusters or for \"loose\" CPUs not assigned\n to a cluster this will be UNASSIGNED_CLUSTER_INDEX; otherwise it will\n be the same as the cluster-id property of the CPU object's TYPE_CPU_CLUSTER\n QOM parent.\n Under TCG this value is propagated to @tcg_cflags.\n See TranslationBlock::TCG CF_CLUSTER_MASK.\n @tcg_cflags: Pre-computed cflags for this cpu.\n @nr_cores: Number of cores within this CPU package.\n @nr_threads: Number of threads within this CPU core.\n @running: #true if CPU is currently running (lockless).\n @has_waiter: #true if a CPU is currently waiting for the cpu_exec_end;\n valid under cpu_list_lock.\n @created: Indicates whether the CPU thread has been successfully created.\n @interrupt_request: Indicates a pending interrupt request.\n @halted: Nonzero if the CPU is in suspended state.\n @stop: Indicates a pending stop request.\n @stopped: Indicates the CPU has been artificially stopped.\n @unplug: Indicates a pending CPU unplug request.\n @crash_occurred: Indicates the OS reported a crash (panic) for this CPU\n @singlestep_enabled: Flags for single-stepping.\n @icount_extra: Instructions until next timer event.\n @neg.can_do_io: True if memory-mapped IO is allowed.\n @cpu_ases: Pointer to array of CPUAddressSpaces (which define the\n AddressSpaces this CPU has)\n @num_ases: number of CPUAddressSpaces in @cpu_ases\n @as: Pointer to the first AddressSpace, for the convenience of targets which\n only have a single AddressSpace\n @gdb_regs: Additional GDB registers.\n @gdb_num_regs: Number of total registers accessible to GDB.\n @gdb_num_g_regs: Number of registers in GDB 'g' packets.\n @next_cpu: Next CPU sharing TB cache.\n @opaque: User data.\n @mem_io_pc: Host Program Counter at which the memory was accessed.\n @accel: Pointer to accelerator specific state.\n @kvm_fd: vCPU file descriptor for KVM.\n @work_mutex: Lock to prevent multiple access to @work_list.\n @work_list: List of pending asynchronous work.\n @trace_dstate_delayed: Delayed changes to trace_dstate (includes all changes\n to @trace_dstate).\n @trace_dstate: Dynamic tracing state of events for this vCPU (bitmask).\n @plugin_mask: Plugin event bitmap. Modified only via async work.\n @ignore_memory_transaction_failures: Cached copy of the MachineState\n flag of the same name: allows the board to suppress calling of the\n CPU do_transaction_failed hook function.\n @kvm_dirty_gfns: Points to the KVM dirty ring for this CPU when KVM dirty\n ring is enabled.\n @kvm_fetch_index: Keeps the index that we last fetched from the per-vCPU\n dirty ring structure.\n\n State of one CPU core or thread.\n\n Align, in order to match possible alignment required by CPUArchState,\n and eliminate a hole between CPUState and CPUArchState within ArchCPU."] +#[doc = " CPUState:\n @cpu_index: CPU index (informative).\n @cluster_index: Identifies which cluster this CPU is in.\n For boards which don't define clusters or for \"loose\" CPUs not assigned\n to a cluster this will be UNASSIGNED_CLUSTER_INDEX; otherwise it will\n be the same as the cluster-id property of the CPU object's TYPE_CPU_CLUSTER\n QOM parent.\n Under TCG this value is propagated to @tcg_cflags.\n See TranslationBlock::TCG CF_CLUSTER_MASK.\n @tcg_cflags: Pre-computed cflags for this cpu.\n @nr_cores: Number of cores within this CPU package.\n @nr_threads: Number of threads within this CPU core.\n @running: #true if CPU is currently running (lockless).\n @has_waiter: #true if a CPU is currently waiting for the cpu_exec_end;\n valid under cpu_list_lock.\n @created: Indicates whether the CPU thread has been successfully created.\n @interrupt_request: Indicates a pending interrupt request.\n @halted: Nonzero if the CPU is in suspended state.\n @stop: Indicates a pending stop request.\n @stopped: Indicates the CPU has been artificially stopped.\n @unplug: Indicates a pending CPU unplug request.\n @crash_occurred: Indicates the OS reported a crash (panic) for this CPU\n @singlestep_enabled: Flags for single-stepping.\n @icount_extra: Instructions until next timer event.\n @neg.can_do_io: True if memory-mapped IO is allowed.\n @cpu_ases: Pointer to array of CPUAddressSpaces (which define the\n AddressSpaces this CPU has)\n @num_ases: number of CPUAddressSpaces in @cpu_ases\n @as: Pointer to the first AddressSpace, for the convenience of targets which\n only have a single AddressSpace\n @gdb_regs: Additional GDB registers.\n @gdb_num_regs: Number of total registers accessible to GDB.\n @gdb_num_g_regs: Number of registers in GDB 'g' packets.\n @node: QTAILQ of CPUs sharing TB cache.\n @opaque: User data.\n @mem_io_pc: Host Program Counter at which the memory was accessed.\n @accel: Pointer to accelerator specific state.\n @kvm_fd: vCPU file descriptor for KVM.\n @work_mutex: Lock to prevent multiple access to @work_list.\n @work_list: List of pending asynchronous work.\n @plugin_mem_cbs: active plugin memory callbacks\n @plugin_state: per-CPU plugin state\n @ignore_memory_transaction_failures: Cached copy of the MachineState\n flag of the same name: allows the board to suppress calling of the\n CPU do_transaction_failed hook function.\n @kvm_dirty_gfns: Points to the KVM dirty ring for this CPU when KVM dirty\n ring is enabled.\n @kvm_fetch_index: Keeps the index that we last fetched from the per-vCPU\n dirty ring structure.\n\n State of one CPU core or thread.\n\n Align, in order to match possible alignment required by CPUArchState,\n and eliminate a hole between CPUState and CPUArchState within ArchCPU."] #[repr(C)] #[repr(align(16))] pub struct CPUState { @@ -5945,8 +5892,8 @@ pub struct CPUState { pub dirty_pages: u64, pub kvm_vcpu_stats_fd: ::std::os::raw::c_int, pub in_ioctl_lock: QemuLockCnt, - pub plugin_mask: [::std::os::raw::c_ulong; 1usize], pub plugin_mem_cbs: *mut GArray, + pub plugin_state: *mut CPUPluginState, pub cpu_index: ::std::os::raw::c_int, pub cluster_index: ::std::os::raw::c_int, pub tcg_cflags: u32, @@ -6687,23 +6634,23 @@ fn bindgen_test_layout_CPUState() { ) ); assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).plugin_mask) as usize - ptr as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).plugin_mem_cbs) as usize - ptr as usize }, 696usize, concat!( "Offset of field: ", stringify!(CPUState), "::", - stringify!(plugin_mask) + stringify!(plugin_mem_cbs) ) ); assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).plugin_mem_cbs) as usize - ptr as usize }, + unsafe { ::std::ptr::addr_of!((*ptr).plugin_state) as usize - ptr as usize }, 704usize, concat!( "Offset of field: ", stringify!(CPUState), "::", - stringify!(plugin_mem_cbs) + stringify!(plugin_state) ) ); assert_eq!( @@ -6860,7 +6807,7 @@ impl Default for CPUState { } impl ::std::fmt::Debug for CPUState { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - write ! (f , "CPUState {{ parent_obj: {:?}, cc: {:?}, nr_cores: {:?}, nr_threads: {:?}, thread: {:?}, thread_id: {:?}, running: {:?}, has_waiter: {:?}, halt_cond: {:?}, thread_kicked: {:?}, created: {:?}, stop: {:?}, stopped: {:?}, start_powered_off: {:?}, unplug: {:?}, crash_occurred: {:?}, exit_request: {:?}, exclusive_context_count: {:?}, singlestep_enabled: {:?}, jmp_env: {:?}, work_mutex: {:?}, work_list: {:?}, cpu_ases: {:?}, num_ases: {:?}, as: {:?}, memory: {:?}, tb_jmp_cache: {:?}, gdb_regs: {:?}, gdb_num_regs: {:?}, gdb_num_g_regs: {:?}, node: {:?}, breakpoints: {:?}, watchpoints: {:?}, watchpoint_hit: {:?}, opaque: {:?}, kvm_fd: {:?}, kvm_state: {:?}, kvm_run: {:?}, kvm_dirty_gfns: {:?}, kvm_vcpu_stats_fd: {:?}, in_ioctl_lock: {:?}, plugin_mask: {:?}, plugin_mem_cbs: {:?}, cpu_index: {:?}, cluster_index: {:?}, accel: {:?}, vcpu_dirty: {:?}, throttle_thread_scheduled: {:?}, ignore_memory_transaction_failures: {:?}, prctl_unalign_sigbus: {:?}, iommu_notifiers: {:?}, neg_align: {:?}, neg: {:?} }}" , self . parent_obj , self . cc , self . nr_cores , self . nr_threads , self . thread , self . thread_id , self . running , self . has_waiter , self . halt_cond , self . thread_kicked , self . created , self . stop , self . stopped , self . start_powered_off , self . unplug , self . crash_occurred , self . exit_request , self . exclusive_context_count , self . singlestep_enabled , self . jmp_env , self . work_mutex , self . work_list , self . cpu_ases , self . num_ases , self . as_ , self . memory , self . tb_jmp_cache , self . gdb_regs , self . gdb_num_regs , self . gdb_num_g_regs , self . node , self . breakpoints , self . watchpoints , self . watchpoint_hit , self . opaque , self . kvm_fd , self . kvm_state , self . kvm_run , self . kvm_dirty_gfns , self . kvm_vcpu_stats_fd , self . in_ioctl_lock , self . plugin_mask , self . plugin_mem_cbs , self . cpu_index , self . cluster_index , self . accel , self . vcpu_dirty , self . throttle_thread_scheduled , self . ignore_memory_transaction_failures , self . prctl_unalign_sigbus , self . iommu_notifiers , self . neg_align , self . neg) + write ! (f , "CPUState {{ parent_obj: {:?}, cc: {:?}, nr_cores: {:?}, nr_threads: {:?}, thread: {:?}, thread_id: {:?}, running: {:?}, has_waiter: {:?}, halt_cond: {:?}, thread_kicked: {:?}, created: {:?}, stop: {:?}, stopped: {:?}, start_powered_off: {:?}, unplug: {:?}, crash_occurred: {:?}, exit_request: {:?}, exclusive_context_count: {:?}, singlestep_enabled: {:?}, jmp_env: {:?}, work_mutex: {:?}, work_list: {:?}, cpu_ases: {:?}, num_ases: {:?}, as: {:?}, memory: {:?}, tb_jmp_cache: {:?}, gdb_regs: {:?}, gdb_num_regs: {:?}, gdb_num_g_regs: {:?}, node: {:?}, breakpoints: {:?}, watchpoints: {:?}, watchpoint_hit: {:?}, opaque: {:?}, kvm_fd: {:?}, kvm_state: {:?}, kvm_run: {:?}, kvm_dirty_gfns: {:?}, kvm_vcpu_stats_fd: {:?}, in_ioctl_lock: {:?}, plugin_mem_cbs: {:?}, plugin_state: {:?}, cpu_index: {:?}, cluster_index: {:?}, accel: {:?}, vcpu_dirty: {:?}, throttle_thread_scheduled: {:?}, ignore_memory_transaction_failures: {:?}, prctl_unalign_sigbus: {:?}, iommu_notifiers: {:?}, neg_align: {:?}, neg: {:?} }}" , self . parent_obj , self . cc , self . nr_cores , self . nr_threads , self . thread , self . thread_id , self . running , self . has_waiter , self . halt_cond , self . thread_kicked , self . created , self . stop , self . stopped , self . start_powered_off , self . unplug , self . crash_occurred , self . exit_request , self . exclusive_context_count , self . singlestep_enabled , self . jmp_env , self . work_mutex , self . work_list , self . cpu_ases , self . num_ases , self . as_ , self . memory , self . tb_jmp_cache , self . gdb_regs , self . gdb_num_regs , self . gdb_num_g_regs , self . node , self . breakpoints , self . watchpoints , self . watchpoint_hit , self . opaque , self . kvm_fd , self . kvm_state , self . kvm_run , self . kvm_dirty_gfns , self . kvm_vcpu_stats_fd , self . in_ioctl_lock , self . plugin_mem_cbs , self . plugin_state , self . cpu_index , self . cluster_index , self . accel , self . vcpu_dirty , self . throttle_thread_scheduled , self . ignore_memory_transaction_failures , self . prctl_unalign_sigbus , self . iommu_notifiers , self . neg_align , self . neg) } } extern "C" { @@ -11485,108 +11432,15 @@ impl ::std::fmt::Debug for ArchCPU { write ! (f , "ArchCPU {{ parent_obj: {:?}, env: {:?}, vmsentry: {:?}, hyperv_vendor: {:?}, hyperv_synic_kvm_only: {:?}, hyperv_passthrough: {:?}, hyperv_no_nonarch_cs: {:?}, hyperv_vendor_id: {:?}, hyperv_interface_id: {:?}, hyperv_limits: {:?}, hyperv_enforce_cpuid: {:?}, check_cpuid: {:?}, enforce_cpuid: {:?}, force_features: {:?}, expose_kvm: {:?}, expose_tcg: {:?}, migratable: {:?}, migrate_smi_count: {:?}, max_features: {:?}, vmware_cpuid_freq: {:?}, cache_info_passthrough: {:?}, mwait: {:?}, filtered_features: {:?}, enable_pmu: {:?}, enable_lmce: {:?}, enable_l3_cache: {:?}, legacy_cache: {:?}, enable_cpuid_0xb: {:?}, full_cpuid_auto_level: {:?}, vendor_cpuid_only: {:?}, intel_pt_auto_level: {:?}, fill_mtrr_mask: {:?}, host_phys_bits: {:?}, kvm_no_smi_migration: {:?}, kvm_pv_enforce_cpuid: {:?}, apic_state: {:?}, cpu_as_root: {:?}, cpu_as_mem: {:?}, smram: {:?}, machine_done: {:?}, kvm_msr_buf: {:?}, xen_vapic: {:?} }}" , self . parent_obj , self . env , self . vmsentry , self . hyperv_vendor , self . hyperv_synic_kvm_only , self . hyperv_passthrough , self . hyperv_no_nonarch_cs , self . hyperv_vendor_id , self . hyperv_interface_id , self . hyperv_limits , self . hyperv_enforce_cpuid , self . check_cpuid , self . enforce_cpuid , self . force_features , self . expose_kvm , self . expose_tcg , self . migratable , self . migrate_smi_count , self . max_features , self . vmware_cpuid_freq , self . cache_info_passthrough , self . mwait , self . filtered_features , self . enable_pmu , self . enable_lmce , self . enable_l3_cache , self . legacy_cache , self . enable_cpuid_0xb , self . full_cpuid_auto_level , self . vendor_cpuid_only , self . intel_pt_auto_level , self . fill_mtrr_mask , self . host_phys_bits , self . kvm_no_smi_migration , self . kvm_pv_enforce_cpuid , self . apic_state , self . cpu_as_root , self . cpu_as_mem , self . smram , self . machine_done , self . kvm_msr_buf , self . xen_vapic) } } -pub type abi_ulong = target_ulong; -pub type abi_long = target_long; extern "C" { - #[doc = " page_check_range\n @start: first byte of range\n @len: length of range\n @flags: flags required for each page\n\n Return true if every page in [@start, @start+@len) has @flags set.\n Return false if any page is unmapped. Thus testing flags == 0 is\n equivalent to testing for flags == PAGE_VALID."] - pub fn page_check_range( - start: target_ulong, - last: target_ulong, - flags: ::std::os::raw::c_int, - ) -> bool; + pub fn cpu_memory_rw_debug( + cpu: *mut CPUState, + addr: vaddr, + ptr: *mut ::std::os::raw::c_void, + len: usize, + is_write: bool, + ) -> ::std::os::raw::c_int; } -pub const MemOp_MO_8: MemOp = MemOp(0); -pub const MemOp_MO_16: MemOp = MemOp(1); -pub const MemOp_MO_32: MemOp = MemOp(2); -pub const MemOp_MO_64: MemOp = MemOp(3); -pub const MemOp_MO_128: MemOp = MemOp(4); -pub const MemOp_MO_256: MemOp = MemOp(5); -pub const MemOp_MO_512: MemOp = MemOp(6); -pub const MemOp_MO_1024: MemOp = MemOp(7); -pub const MemOp_MO_SIZE: MemOp = MemOp(7); -pub const MemOp_MO_SIGN: MemOp = MemOp(8); -pub const MemOp_MO_BSWAP: MemOp = MemOp(16); -pub const MemOp_MO_LE: MemOp = MemOp(0); -pub const MemOp_MO_BE: MemOp = MemOp(16); -pub const MemOp_MO_TE: MemOp = MemOp(0); -pub const MemOp_MO_ASHIFT: MemOp = MemOp(5); -pub const MemOp_MO_AMASK: MemOp = MemOp(224); -pub const MemOp_MO_UNALN: MemOp = MemOp(0); -pub const MemOp_MO_ALIGN_2: MemOp = MemOp(32); -pub const MemOp_MO_ALIGN_4: MemOp = MemOp(64); -pub const MemOp_MO_ALIGN_8: MemOp = MemOp(96); -pub const MemOp_MO_ALIGN_16: MemOp = MemOp(128); -pub const MemOp_MO_ALIGN_32: MemOp = MemOp(160); -pub const MemOp_MO_ALIGN_64: MemOp = MemOp(192); -pub const MemOp_MO_ALIGN: MemOp = MemOp(224); -pub const MemOp_MO_ATOM_SHIFT: MemOp = MemOp(8); -pub const MemOp_MO_ATOM_IFALIGN: MemOp = MemOp(0); -pub const MemOp_MO_ATOM_IFALIGN_PAIR: MemOp = MemOp(256); -pub const MemOp_MO_ATOM_WITHIN16: MemOp = MemOp(512); -pub const MemOp_MO_ATOM_WITHIN16_PAIR: MemOp = MemOp(768); -pub const MemOp_MO_ATOM_SUBALIGN: MemOp = MemOp(1024); -pub const MemOp_MO_ATOM_NONE: MemOp = MemOp(1280); -pub const MemOp_MO_ATOM_MASK: MemOp = MemOp(1792); -pub const MemOp_MO_UB: MemOp = MemOp(0); -pub const MemOp_MO_UW: MemOp = MemOp(1); -pub const MemOp_MO_UL: MemOp = MemOp(2); -pub const MemOp_MO_UQ: MemOp = MemOp(3); -pub const MemOp_MO_UO: MemOp = MemOp(4); -pub const MemOp_MO_SB: MemOp = MemOp(8); -pub const MemOp_MO_SW: MemOp = MemOp(9); -pub const MemOp_MO_SL: MemOp = MemOp(10); -pub const MemOp_MO_SQ: MemOp = MemOp(11); -pub const MemOp_MO_SO: MemOp = MemOp(12); -pub const MemOp_MO_LEUW: MemOp = MemOp(1); -pub const MemOp_MO_LEUL: MemOp = MemOp(2); -pub const MemOp_MO_LEUQ: MemOp = MemOp(3); -pub const MemOp_MO_LESW: MemOp = MemOp(9); -pub const MemOp_MO_LESL: MemOp = MemOp(10); -pub const MemOp_MO_LESQ: MemOp = MemOp(11); -pub const MemOp_MO_BEUW: MemOp = MemOp(17); -pub const MemOp_MO_BEUL: MemOp = MemOp(18); -pub const MemOp_MO_BEUQ: MemOp = MemOp(19); -pub const MemOp_MO_BESW: MemOp = MemOp(25); -pub const MemOp_MO_BESL: MemOp = MemOp(26); -pub const MemOp_MO_BESQ: MemOp = MemOp(27); -pub const MemOp_MO_TEUW: MemOp = MemOp(1); -pub const MemOp_MO_TEUL: MemOp = MemOp(2); -pub const MemOp_MO_TEUQ: MemOp = MemOp(3); -pub const MemOp_MO_TEUO: MemOp = MemOp(4); -pub const MemOp_MO_TESW: MemOp = MemOp(9); -pub const MemOp_MO_TESL: MemOp = MemOp(10); -pub const MemOp_MO_TESQ: MemOp = MemOp(11); -pub const MemOp_MO_SSIZE: MemOp = MemOp(15); -impl ::std::ops::BitOr for MemOp { - type Output = Self; - #[inline] - fn bitor(self, other: Self) -> Self { - MemOp(self.0 | other.0) - } -} -impl ::std::ops::BitOrAssign for MemOp { - #[inline] - fn bitor_assign(&mut self, rhs: MemOp) { - self.0 |= rhs.0; - } -} -impl ::std::ops::BitAnd for MemOp { - type Output = Self; - #[inline] - fn bitand(self, other: Self) -> Self { - MemOp(self.0 & other.0) - } -} -impl ::std::ops::BitAndAssign for MemOp { - #[inline] - fn bitand_assign(&mut self, rhs: MemOp) { - self.0 &= rhs.0; - } -} -#[repr(transparent)] -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub struct MemOp(pub ::std::os::raw::c_uint); -pub type MemOpIdx = u32; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct RBNode { @@ -11810,6 +11664,112 @@ impl Default for IntervalTreeNode { } } pub type IntervalTreeRoot = RBRootLeftCached; +pub type abi_ulong = target_ulong; +pub type abi_long = target_long; +extern "C" { + #[doc = " --- Begin LibAFL code ---"] + pub fn pageflags_get_root() -> *mut IntervalTreeRoot; +} +extern "C" { + #[doc = " page_check_range\n @start: first byte of range\n @len: length of range\n @flags: flags required for each page\n\n Return true if every page in [@start, @start+@len) has @flags set.\n Return false if any page is unmapped. Thus testing flags == 0 is\n equivalent to testing for flags == PAGE_VALID."] + pub fn page_check_range( + start: target_ulong, + last: target_ulong, + flags: ::std::os::raw::c_int, + ) -> bool; +} +pub const MemOp_MO_8: MemOp = MemOp(0); +pub const MemOp_MO_16: MemOp = MemOp(1); +pub const MemOp_MO_32: MemOp = MemOp(2); +pub const MemOp_MO_64: MemOp = MemOp(3); +pub const MemOp_MO_128: MemOp = MemOp(4); +pub const MemOp_MO_256: MemOp = MemOp(5); +pub const MemOp_MO_512: MemOp = MemOp(6); +pub const MemOp_MO_1024: MemOp = MemOp(7); +pub const MemOp_MO_SIZE: MemOp = MemOp(7); +pub const MemOp_MO_SIGN: MemOp = MemOp(8); +pub const MemOp_MO_BSWAP: MemOp = MemOp(16); +pub const MemOp_MO_LE: MemOp = MemOp(0); +pub const MemOp_MO_BE: MemOp = MemOp(16); +pub const MemOp_MO_TE: MemOp = MemOp(0); +pub const MemOp_MO_ASHIFT: MemOp = MemOp(5); +pub const MemOp_MO_AMASK: MemOp = MemOp(224); +pub const MemOp_MO_UNALN: MemOp = MemOp(0); +pub const MemOp_MO_ALIGN_2: MemOp = MemOp(32); +pub const MemOp_MO_ALIGN_4: MemOp = MemOp(64); +pub const MemOp_MO_ALIGN_8: MemOp = MemOp(96); +pub const MemOp_MO_ALIGN_16: MemOp = MemOp(128); +pub const MemOp_MO_ALIGN_32: MemOp = MemOp(160); +pub const MemOp_MO_ALIGN_64: MemOp = MemOp(192); +pub const MemOp_MO_ALIGN: MemOp = MemOp(224); +pub const MemOp_MO_ATOM_SHIFT: MemOp = MemOp(8); +pub const MemOp_MO_ATOM_IFALIGN: MemOp = MemOp(0); +pub const MemOp_MO_ATOM_IFALIGN_PAIR: MemOp = MemOp(256); +pub const MemOp_MO_ATOM_WITHIN16: MemOp = MemOp(512); +pub const MemOp_MO_ATOM_WITHIN16_PAIR: MemOp = MemOp(768); +pub const MemOp_MO_ATOM_SUBALIGN: MemOp = MemOp(1024); +pub const MemOp_MO_ATOM_NONE: MemOp = MemOp(1280); +pub const MemOp_MO_ATOM_MASK: MemOp = MemOp(1792); +pub const MemOp_MO_UB: MemOp = MemOp(0); +pub const MemOp_MO_UW: MemOp = MemOp(1); +pub const MemOp_MO_UL: MemOp = MemOp(2); +pub const MemOp_MO_UQ: MemOp = MemOp(3); +pub const MemOp_MO_UO: MemOp = MemOp(4); +pub const MemOp_MO_SB: MemOp = MemOp(8); +pub const MemOp_MO_SW: MemOp = MemOp(9); +pub const MemOp_MO_SL: MemOp = MemOp(10); +pub const MemOp_MO_SQ: MemOp = MemOp(11); +pub const MemOp_MO_SO: MemOp = MemOp(12); +pub const MemOp_MO_LEUW: MemOp = MemOp(1); +pub const MemOp_MO_LEUL: MemOp = MemOp(2); +pub const MemOp_MO_LEUQ: MemOp = MemOp(3); +pub const MemOp_MO_LESW: MemOp = MemOp(9); +pub const MemOp_MO_LESL: MemOp = MemOp(10); +pub const MemOp_MO_LESQ: MemOp = MemOp(11); +pub const MemOp_MO_BEUW: MemOp = MemOp(17); +pub const MemOp_MO_BEUL: MemOp = MemOp(18); +pub const MemOp_MO_BEUQ: MemOp = MemOp(19); +pub const MemOp_MO_BESW: MemOp = MemOp(25); +pub const MemOp_MO_BESL: MemOp = MemOp(26); +pub const MemOp_MO_BESQ: MemOp = MemOp(27); +pub const MemOp_MO_TEUW: MemOp = MemOp(1); +pub const MemOp_MO_TEUL: MemOp = MemOp(2); +pub const MemOp_MO_TEUQ: MemOp = MemOp(3); +pub const MemOp_MO_TEUO: MemOp = MemOp(4); +pub const MemOp_MO_TESW: MemOp = MemOp(9); +pub const MemOp_MO_TESL: MemOp = MemOp(10); +pub const MemOp_MO_TESQ: MemOp = MemOp(11); +pub const MemOp_MO_SSIZE: MemOp = MemOp(15); +impl ::std::ops::BitOr for MemOp { + type Output = Self; + #[inline] + fn bitor(self, other: Self) -> Self { + MemOp(self.0 | other.0) + } +} +impl ::std::ops::BitOrAssign for MemOp { + #[inline] + fn bitor_assign(&mut self, rhs: MemOp) { + self.0 |= rhs.0; + } +} +impl ::std::ops::BitAnd for MemOp { + type Output = Self; + #[inline] + fn bitand(self, other: Self) -> Self { + MemOp(self.0 & other.0) + } +} +impl ::std::ops::BitAndAssign for MemOp { + #[inline] + fn bitand_assign(&mut self, rhs: MemOp) { + self.0 &= rhs.0; + } +} +#[repr(transparent)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct MemOp(pub ::std::os::raw::c_uint); +pub type MemOpIdx = u32; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct tb_tc { @@ -12416,6 +12376,7 @@ pub struct libafl_mapinfo { pub path: *const ::std::os::raw::c_char, pub flags: ::std::os::raw::c_int, pub is_priv: ::std::os::raw::c_int, + pub is_valid: bool, } #[test] fn bindgen_test_layout_libafl_mapinfo() { @@ -12423,7 +12384,7 @@ fn bindgen_test_layout_libafl_mapinfo() { let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), - 40usize, + 48usize, concat!("Size of: ", stringify!(libafl_mapinfo)) ); assert_eq!( @@ -12491,6 +12452,16 @@ fn bindgen_test_layout_libafl_mapinfo() { stringify!(is_priv) ) ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).is_valid) as usize - ptr as usize }, + 40usize, + concat!( + "Offset of field: ", + stringify!(libafl_mapinfo), + "::", + stringify!(is_valid) + ) + ); } impl Default for libafl_mapinfo { fn default() -> Self { @@ -12506,7 +12477,8 @@ extern "C" { } extern "C" { pub fn libafl_maps_next( - node: *mut IntervalTreeNode, + pageflags_maps_node: *mut IntervalTreeNode, + proc_maps_node: *mut IntervalTreeRoot, ret: *mut libafl_mapinfo, ) -> *mut IntervalTreeNode; } @@ -12629,6 +12601,37 @@ extern "C" { #[doc = " qemu_plugin_hwaddr_phys_addr() - query physical address for memory operation\n @haddr: address handle from qemu_plugin_get_hwaddr()\n\n Returns the physical address associated with the memory operation\n\n Note that the returned physical address may not be unique if you are dealing\n with multiple address spaces."] pub fn qemu_plugin_hwaddr_phys_addr(haddr: *const qemu_plugin_hwaddr) -> u64; } +#[doc = " struct CPUPluginState - per-CPU state for plugins\n @event_mask: plugin event bitmap. Modified only via async work."] +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct CPUPluginState { + pub event_mask: [::std::os::raw::c_ulong; 1usize], +} +#[test] +fn bindgen_test_layout_CPUPluginState() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 8usize, + concat!("Size of: ", stringify!(CPUPluginState)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(CPUPluginState)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).event_mask) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(CPUPluginState), + "::", + stringify!(event_mask) + ) + ); +} pub const TCGReg_TCG_REG_EAX: TCGReg = TCGReg(0); pub const TCGReg_TCG_REG_ECX: TCGReg = TCGReg(1); pub const TCGReg_TCG_REG_EDX: TCGReg = TCGReg(2); @@ -13019,13 +13022,13 @@ impl TCGTemp { } #[inline] pub fn temp_subindex(&self) -> ::std::os::raw::c_uint { - unsafe { ::std::mem::transmute(self._bitfield_1.get(40usize, 1u8) as u32) } + unsafe { ::std::mem::transmute(self._bitfield_1.get(40usize, 2u8) as u32) } } #[inline] pub fn set_temp_subindex(&mut self, val: ::std::os::raw::c_uint) { unsafe { let val: u32 = ::std::mem::transmute(val); - self._bitfield_1.set(40usize, 1u8, val as u64) + self._bitfield_1.set(40usize, 2u8, val as u64) } } #[inline] @@ -13083,7 +13086,7 @@ impl TCGTemp { let temp_allocated: u32 = unsafe { ::std::mem::transmute(temp_allocated) }; temp_allocated as u64 }); - __bindgen_bitfield_unit.set(40usize, 1u8, { + __bindgen_bitfield_unit.set(40usize, 2u8, { let temp_subindex: u32 = unsafe { ::std::mem::transmute(temp_subindex) }; temp_subindex as u64 }); @@ -13624,7 +13627,7 @@ impl Default for libafl_hook { extern "C" { pub fn libafl_qemu_set_hook( pc: target_ulong, - callback: ::std::option::Option, + callback: ::std::option::Option< extern "C" fn(data: u64, pc: target_ulong)>, data: u64, invalidate: ::std::os::raw::c_int, ) -> usize; @@ -13647,7 +13650,7 @@ extern "C" { extern "C" { pub fn libafl_add_backdoor_hook( exec: ::std::option::Option< - extern "C" fn(data: u64, cpu: *mut CPUArchState, pc: target_ulong), + extern "C" fn(data: u64, cpu: *mut CPUArchState, pc: target_ulong), >, data: u64, ) -> usize; @@ -13661,9 +13664,9 @@ extern "C" { extern "C" { pub fn libafl_add_edge_hook( gen: ::std::option::Option< - extern "C" fn(data: u64, src: target_ulong, dst: target_ulong) -> u64, + extern "C" fn(data: u64, src: target_ulong, dst: target_ulong) -> u64, >, - exec: ::std::option::Option, + exec: ::std::option::Option< extern "C" fn(data: u64, id: u64)>, data: u64, ) -> usize; } @@ -13681,11 +13684,11 @@ extern "C" { } extern "C" { pub fn libafl_add_block_hook( - gen: ::std::option::Option u64>, + gen: ::std::option::Option< extern "C" fn(data: u64, pc: target_ulong) -> u64>, post_gen: ::std::option::Option< - extern "C" fn(data: u64, pc: target_ulong, block_length: target_ulong), + extern "C" fn(data: u64, pc: target_ulong, block_length: target_ulong), >, - exec: ::std::option::Option, + exec: ::std::option::Option< extern "C" fn(data: u64, id: u64)>, data: u64, ) -> usize; } @@ -13711,12 +13714,12 @@ extern "C" { oi: MemOpIdx, ) -> u64, >, - exec1: ::std::option::Option, - exec2: ::std::option::Option, - exec4: ::std::option::Option, - exec8: ::std::option::Option, + exec1: ::std::option::Option< extern "C" fn(data: u64, id: u64, addr: target_ulong)>, + exec2: ::std::option::Option< extern "C" fn(data: u64, id: u64, addr: target_ulong)>, + exec4: ::std::option::Option< extern "C" fn(data: u64, id: u64, addr: target_ulong)>, + exec8: ::std::option::Option< extern "C" fn(data: u64, id: u64, addr: target_ulong)>, execN: ::std::option::Option< - extern "C" fn(data: u64, id: u64, addr: target_ulong, size: usize), + extern "C" fn(data: u64, id: u64, addr: target_ulong, size: usize), >, data: u64, ) -> usize; @@ -13731,12 +13734,12 @@ extern "C" { oi: MemOpIdx, ) -> u64, >, - exec1: ::std::option::Option, - exec2: ::std::option::Option, - exec4: ::std::option::Option, - exec8: ::std::option::Option, + exec1: ::std::option::Option< extern "C" fn(data: u64, id: u64, addr: target_ulong)>, + exec2: ::std::option::Option< extern "C" fn(data: u64, id: u64, addr: target_ulong)>, + exec4: ::std::option::Option< extern "C" fn(data: u64, id: u64, addr: target_ulong)>, + exec8: ::std::option::Option< extern "C" fn(data: u64, id: u64, addr: target_ulong)>, execN: ::std::option::Option< - extern "C" fn(data: u64, id: u64, addr: target_ulong, size: usize), + extern "C" fn(data: u64, id: u64, addr: target_ulong, size: usize), >, data: u64, ) -> usize; @@ -13761,11 +13764,13 @@ extern "C" { } extern "C" { pub fn libafl_add_cmp_hook( - gen: ::std::option::Option u64>, - exec1: ::std::option::Option, - exec2: ::std::option::Option, - exec4: ::std::option::Option, - exec8: ::std::option::Option, + gen: ::std::option::Option< + extern "C" fn(data: u64, pc: target_ulong, size: usize) -> u64, + >, + exec1: ::std::option::Option< extern "C" fn(data: u64, id: u64, v0: u8, v1: u8)>, + exec2: ::std::option::Option< extern "C" fn(data: u64, id: u64, v0: u16, v1: u16)>, + exec4: ::std::option::Option< extern "C" fn(data: u64, id: u64, v0: u32, v1: u32)>, + exec8: ::std::option::Option< extern "C" fn(data: u64, id: u64, v0: u64, v1: u64)>, data: u64, ) -> usize; } @@ -13863,7 +13868,7 @@ extern "C" { } extern "C" { pub fn libafl_add_new_thread_hook( - callback: ::std::option::Option bool>, + callback: ::std::option::Option< extern "C" fn(data: u64, tid: u32) -> bool>, data: u64, ) -> usize; } diff --git a/libafl_qemu/src/command.rs b/libafl_qemu/src/command.rs index a064b93a6f..dbd449f46c 100644 --- a/libafl_qemu/src/command.rs +++ b/libafl_qemu/src/command.rs @@ -16,9 +16,9 @@ use num_enum::TryFromPrimitive; use crate::QemuInstrumentationPagingFilter; use crate::{ executor::QemuExecutorState, sync_exit::SyncBackdoorError, EmuExitHandler, Emulator, - GuestAddrKind, GuestReg, HandlerError, HasInstrumentationFilter, InnerHandlerResult, IsFilter, - IsSnapshotManager, Qemu, QemuHelperTuple, QemuInstrumentationAddressRangeFilter, Regs, - StdEmuExitHandler, StdInstrumentationFilter, CPU, + GuestAddrKind, GuestReg, HandlerError, HasInstrumentationFilter, InnerHandlerResult, + InputLocation, IsFilter, IsSnapshotManager, Qemu, QemuHelperTuple, + QemuInstrumentationAddressRangeFilter, Regs, StdEmuExitHandler, StdInstrumentationFilter, CPU, }; pub const VERSION: u64 = bindings::LIBAFL_QEMU_HDR_VERSION_NUMBER as u64; @@ -321,6 +321,7 @@ where #[derive(Debug, Clone)] pub struct InputCommand { location: EmulatorMemoryChunk, + cpu: CPU, } impl IsCommand> for InputCommand @@ -346,7 +347,7 @@ where let ret_value = self.location.write(qemu, input.target_bytes().as_slice()); if let Some(reg) = ret_reg { - qemu.write_reg(reg, ret_value).unwrap(); + self.cpu.write_reg(reg, ret_value).unwrap(); } Ok(InnerHandlerResult::Continue) @@ -385,7 +386,11 @@ where .map_err(|_| HandlerError::MultipleSnapshotDefinition)?; emu_exit_handler - .set_input_location(self.input_location.clone(), ret_reg) + .set_input_location(InputLocation::new( + self.input_location.clone(), + qemu.current_cpu().unwrap(), + ret_reg, + )) .unwrap(); let ret_value = self @@ -603,8 +608,8 @@ impl EndCommand { impl InputCommand { #[must_use] - pub fn new(location: EmulatorMemoryChunk) -> Self { - Self { location } + pub fn new(location: EmulatorMemoryChunk, cpu: CPU) -> Self { + Self { location, cpu } } } diff --git a/libafl_qemu/src/emu/mod.rs b/libafl_qemu/src/emu/mod.rs index ac5fcbc0cc..fd017851a7 100644 --- a/libafl_qemu/src/emu/mod.rs +++ b/libafl_qemu/src/emu/mod.rs @@ -173,6 +173,24 @@ where } } +#[derive(Debug, Clone)] +pub struct InputLocation { + mem_chunk: EmulatorMemoryChunk, + cpu: CPU, + ret_register: Option, +} + +impl InputLocation { + #[must_use] + pub fn new(mem_chunk: EmulatorMemoryChunk, cpu: CPU, ret_register: Option) -> Self { + Self { + mem_chunk, + cpu, + ret_register, + } + } +} + /// Synchronous Exit handler maintaining only one snapshot. #[derive(Debug, Clone)] pub struct StdEmuExitHandler @@ -181,7 +199,7 @@ where { snapshot_manager: RefCell, snapshot_id: OnceCell, - input_location: OnceCell<(EmulatorMemoryChunk, Option)>, + input_location: OnceCell, } impl StdEmuExitHandler @@ -196,12 +214,8 @@ where } } - pub fn set_input_location( - &self, - input_location: EmulatorMemoryChunk, - ret_reg: Option, - ) -> Result<(), (EmulatorMemoryChunk, Option)> { - self.input_location.set((input_location, ret_reg)) + pub fn set_input_location(&self, input_location: InputLocation) -> Result<(), InputLocation> { + self.input_location.set(input_location) } pub fn set_snapshot_id(&self, snapshot_id: SnapshotId) -> Result<(), SnapshotId> { @@ -236,10 +250,11 @@ where ) { let exit_handler = emu.state().exit_handler.borrow(); - if let Some((input_location, ret_register)) = exit_handler.input_location.get() { - let input_command = InputCommand::new(input_location.clone()); + if let Some(input_location) = exit_handler.input_location.get() { + let input_command = + InputCommand::new(input_location.mem_chunk.clone(), input_location.cpu.clone()); input_command - .run(emu, qemu_executor_state, input, *ret_register) + .run(emu, qemu_executor_state, input, input_location.ret_register) .unwrap(); } } diff --git a/libafl_qemu/src/emu/usermode.rs b/libafl_qemu/src/emu/usermode.rs index 8cf18bb927..427192e9ed 100644 --- a/libafl_qemu/src/emu/usermode.rs +++ b/libafl_qemu/src/emu/usermode.rs @@ -4,8 +4,8 @@ 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, IntervalTreeNode, - IntervalTreeRoot, MapInfo, MmapPerms, VerifyAccess, + mmap_next_start, pageflags_get_root, 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: *mut IntervalTreeRoot, - maps_node: *mut IntervalTreeNode, + self_maps_root: *mut IntervalTreeRoot, + pageflags_node: *mut IntervalTreeNode, } // Consider a private new only for Emulator @@ -36,11 +36,12 @@ impl GuestMaps { #[must_use] pub(crate) fn new() -> Self { unsafe { - let root = read_self_maps(); - let first = libafl_maps_first(root); + let pageflags_root = pageflags_get_root(); + let self_maps_root = read_self_maps(); + let pageflags_first = libafl_maps_first(pageflags_root); Self { - maps_root: root, - maps_node: first, + self_maps_root, + pageflags_node: pageflags_first, } } } @@ -54,12 +55,15 @@ impl Iterator for GuestMaps { unsafe { let mut ret = MaybeUninit::uninit(); - self.maps_node = libafl_maps_next(self.maps_node, ret.as_mut_ptr()); + self.pageflags_node = + libafl_maps_next(self.pageflags_node, self.self_maps_root, ret.as_mut_ptr()); - if self.maps_node.is_null() { - None + let ret = ret.assume_init(); + + if ret.is_valid { + Some(ret.into()) } else { - Some(ret.assume_init().into()) + None } } } @@ -79,7 +83,7 @@ impl GuestMaps { impl Drop for GuestMaps { fn drop(&mut self) { unsafe { - free_self_maps(self.maps_root); + free_self_maps(self.self_maps_root); } } } diff --git a/libafl_qemu/src/helpers/asan.rs b/libafl_qemu/src/helpers/asan.rs index 8faa39c854..c738e9bf53 100644 --- a/libafl_qemu/src/helpers/asan.rs +++ b/libafl_qemu/src/helpers/asan.rs @@ -461,7 +461,7 @@ impl AsanGiovese { pub fn report_or_crash(&mut self, qemu: Qemu, pc: GuestAddr, error: AsanError) { if let Some(mut cb) = self.error_callback.take() { - (cb)(self, qemu, pc, error); + cb(self, qemu, pc, error); self.error_callback = Some(cb); } else { std::process::abort(); @@ -470,7 +470,7 @@ impl AsanGiovese { pub fn report(&mut self, qemu: Qemu, pc: GuestAddr, error: AsanError) { if let Some(mut cb) = self.error_callback.take() { - (cb)(self, qemu, pc, error); + cb(self, qemu, pc, error); self.error_callback = Some(cb); } } diff --git a/libafl_qemu/src/helpers/snapshot.rs b/libafl_qemu/src/helpers/snapshot.rs index 1088adc310..0eb544fb78 100644 --- a/libafl_qemu/src/helpers/snapshot.rs +++ b/libafl_qemu/src/helpers/snapshot.rs @@ -1,6 +1,7 @@ use std::{ cell::UnsafeCell, collections::{HashMap, HashSet}, + mem::MaybeUninit, sync::Mutex, }; @@ -37,7 +38,7 @@ pub const SNAPSHOT_PAGE_MASK: GuestAddr = !(SNAPSHOT_PAGE_SIZE as GuestAddr - 1) pub type StopExecutionCallback = Box; -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct SnapshotPageInfo { pub addr: GuestAddr, pub perms: MmapPerms, @@ -138,6 +139,7 @@ impl QemuSnapshotHelper { #[allow(clippy::uninit_assumed_init)] pub fn snapshot(&mut self, qemu: Qemu) { + log::info!("Start snapshot"); self.brk = qemu.get_brk(); self.mmap_start = qemu.get_mmap_start(); self.pages.clear(); @@ -172,6 +174,7 @@ impl QemuSnapshotHelper { } self.empty = false; *self.new_maps.lock().unwrap() = self.maps.clone(); + log::info!("End snapshot"); } pub fn page_access(&mut self, page: GuestAddr) { @@ -200,7 +203,7 @@ impl QemuSnapshotHelper { pub fn access(&mut self, addr: GuestAddr, size: usize) { // ASSUMPTION: the access can only cross 2 pages - debug_assert!(size > 0); + debug_assert!(size > 0 && size < SNAPSHOT_PAGE_SIZE); let page = addr & SNAPSHOT_PAGE_MASK; self.page_access(page); let second_page = (addr + size as GuestAddr - 1) & SNAPSHOT_PAGE_MASK; @@ -209,10 +212,122 @@ impl QemuSnapshotHelper { } } + pub fn check_snapshot(&self, qemu: Qemu) { + let mut saved_pages_list = self.pages.clone(); + + log::info!("Checking snapshot correctness"); + + let mut perm_errors: Vec<(GuestAddr, MmapPerms, MmapPerms)> = Vec::new(); + let mut content_mismatch = false; + + for map in qemu.mappings() { + let mut addr = map.start(); + // assert_eq!(addr & SNAPSHOT_PAGE_MASK, 0); + while addr < map.end() { + if let Some(saved_page) = saved_pages_list.remove(&addr) { + if saved_page.perms.readable() { + let mut current_page_content: MaybeUninit<[u8; SNAPSHOT_PAGE_SIZE]> = + MaybeUninit::uninit(); + + if saved_page.perms != map.flags() { + perm_errors.push((addr, saved_page.perms, map.flags())); + log::warn!( + "\t0x{:x}: Flags do not match: saved is {:?} and current is {:?}", + addr, + saved_page.perms, + map.flags() + ); + } + + unsafe { + qemu.read_mem( + addr, + current_page_content.as_mut_ptr().as_mut().unwrap(), + ); + } + + let current_page_content: &mut [u8; SNAPSHOT_PAGE_SIZE] = + unsafe { &mut current_page_content.assume_init() }; + + if saved_page.data.as_ref().unwrap().as_ref() + != current_page_content.as_ref() + { + let mut offsets = Vec::new(); + for (i, (saved_page_byte, current_page_byte)) in saved_page + .data + .unwrap() + .iter() + .zip(current_page_content.iter()) + .enumerate() + { + if saved_page_byte != current_page_byte { + offsets.push(i); + } + } + log::warn!( + "Faulty restore at {}", + offsets.iter().fold(String::new(), |acc, offset| format!( + "{}, 0x{:x}", + acc, + addr + *offset as GuestAddr + )) + ); + content_mismatch = true; + } + } + } else { + log::warn!("\tpage not found @addr 0x{:x}", addr); + } + + addr += SNAPSHOT_PAGE_SIZE as GuestAddr; + } + } + + assert!(saved_pages_list.is_empty()); + + if !perm_errors.is_empty() { + let mut perm_error_ranges: Vec<(GuestAddr, GuestAddr, MmapPerms, MmapPerms)> = + Vec::new(); + + for error in perm_errors { + if let Some(last_range) = perm_error_ranges.last_mut() { + if last_range.1 + SNAPSHOT_PAGE_SIZE as GuestAddr == error.0 as GuestAddr + && error.1 == last_range.2 + && error.2 == last_range.3 + { + last_range.1 += SNAPSHOT_PAGE_SIZE as GuestAddr; + } else { + perm_error_ranges.push((error.0, error.0, error.1, error.2)); + } + } else { + perm_error_ranges.push((error.0, error.0, error.1, error.2)); + } + } + + for error_range in perm_error_ranges { + log::error!( + "0x{:x} -> 0x{:x}: saved is {:?} but current is {:?}", + error_range.0, + error_range.1, + error_range.2, + error_range.3 + ); + } + + content_mismatch = true; + } + + assert!(!content_mismatch, "Error found, stopping..."); + + log::info!("Snapshot check OK"); + } + pub fn reset(&mut self, qemu: Qemu) { { let new_maps = self.new_maps.get_mut().unwrap(); + log::info!("Start restore"); + for acc in &mut self.accesses { unsafe { &mut (*acc.get()) }.dirty.retain(|page| { if let Some(info) = self.pages.get_mut(page) { @@ -297,6 +412,11 @@ impl QemuSnapshotHelper { qemu.set_brk(self.brk); qemu.set_mmap_start(self.mmap_start); + + #[cfg(feature = "paranoid_debug")] + self.check_snapshot(qemu); + + log::info!("End restore"); } pub fn is_unmap_allowed(&mut self, start: GuestAddr, mut size: usize) -> bool { @@ -334,7 +454,7 @@ impl QemuSnapshotHelper { if self.mmap_limit != 0 && total_size > self.mmap_limit { let mut cb = self.stop_execution.take().unwrap(); let qemu = Qemu::get().unwrap(); - (cb)(self, &qemu); + cb(self, &qemu); self.stop_execution = Some(cb); } } @@ -464,8 +584,8 @@ impl QemuSnapshotHelper { )); } - for (i, ..) in found { - new_maps.tree.delete(i); + for (interval, ..) in found { + new_maps.tree.delete(interval); } } @@ -671,7 +791,7 @@ where } else if sys_const == SYS_mprotect { if let Ok(prot) = MmapPerms::try_from(a2 as i32) { let h = hooks.match_helper_mut::().unwrap(); - h.add_mapped(a0, a1 as usize, Some(prot)); + h.change_mapped(a0, a1 as usize, Some(prot)); } } else if sys_const == SYS_munmap { let h = hooks.match_helper_mut::().unwrap(); diff --git a/libafl_qemu/src/sync_exit.rs b/libafl_qemu/src/sync_exit.rs index a5bab0fa15..7697232dba 100644 --- a/libafl_qemu/src/sync_exit.rs +++ b/libafl_qemu/src/sync_exit.rs @@ -112,11 +112,14 @@ where emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg2])?; SyncBackdoor { - command: Command::InputCommand(InputCommand::new(EmulatorMemoryChunk::virt( - virt_addr, - max_input_size, - emu.qemu().current_cpu().unwrap().clone(), - ))), + command: Command::InputCommand(InputCommand::new( + EmulatorMemoryChunk::virt( + virt_addr, + max_input_size, + emu.qemu().current_cpu().unwrap().clone(), + ), + emu.qemu().current_cpu().unwrap(), + )), arch_regs_map, } } @@ -127,11 +130,14 @@ where emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg2])?; SyncBackdoor { - command: Command::InputCommand(InputCommand::new(EmulatorMemoryChunk::phys( - phys_addr, - max_input_size, - Some(emu.qemu().current_cpu().unwrap().clone()), - ))), + command: Command::InputCommand(InputCommand::new( + EmulatorMemoryChunk::phys( + phys_addr, + max_input_size, + Some(emu.qemu().current_cpu().unwrap().clone()), + ), + emu.qemu().current_cpu().unwrap(), + )), arch_regs_map, } }