From b127f0579dcfa77d12d4fe748702017059b3e903 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Wed, 15 May 2024 01:42:59 +0200 Subject: [PATCH] Regenerate QEMU binding stubs only for newer versions of the nightly compiler (#2177) * Regenerate binding stubs only for newer versions of the nightly compiler. * fmt * clippy --- libafl_qemu/libafl_qemu_build/Cargo.toml | 1 + libafl_qemu/libafl_qemu_build/src/bindings.rs | 2 +- libafl_qemu/libafl_qemu_build/src/lib.rs | 117 +++++++++++++++--- .../src/x86_64_stub_bindings.rs | 1 + .../runtime/libafl_qemu_stub_bindings.rs | 1 + 5 files changed, 102 insertions(+), 20 deletions(-) diff --git a/libafl_qemu/libafl_qemu_build/Cargo.toml b/libafl_qemu/libafl_qemu_build/Cargo.toml index 9ce6e047ee..d773bc037f 100644 --- a/libafl_qemu/libafl_qemu_build/Cargo.toml +++ b/libafl_qemu/libafl_qemu_build/Cargo.toml @@ -37,3 +37,4 @@ pkg-config = "0.3.26" cc = "1.0" regex = "1" rustversion = "1.0" +rustc_version = "0.4" \ No newline at end of file diff --git a/libafl_qemu/libafl_qemu_build/src/bindings.rs b/libafl_qemu/libafl_qemu_build/src/bindings.rs index 1a55db6d1f..a07a914948 100644 --- a/libafl_qemu/libafl_qemu_build/src/bindings.rs +++ b/libafl_qemu/libafl_qemu_build/src/bindings.rs @@ -95,7 +95,7 @@ pub fn generate( ) -> Result { let wrapper_h = build_dir.join("wrapper.h"); - store_generated_content_if_different(&wrapper_h, WRAPPER_HEADER.as_bytes()); + store_generated_content_if_different(&wrapper_h, WRAPPER_HEADER.as_bytes(), None, None, false); let bindings = bindgen::Builder::default() .derive_debug(true) diff --git a/libafl_qemu/libafl_qemu_build/src/lib.rs b/libafl_qemu/libafl_qemu_build/src/lib.rs index 69f2f4c996..08479df936 100644 --- a/libafl_qemu/libafl_qemu_build/src/lib.rs +++ b/libafl_qemu/libafl_qemu_build/src/lib.rs @@ -6,12 +6,14 @@ use std::{ env, fs, fs::File, hash::Hasher, - io::{Read, Seek, SeekFrom, Write}, + io::{BufRead, BufReader, Read, Seek, SeekFrom, Write}, path::{Path, PathBuf}, process::Command, ptr::addr_of_mut, }; +use regex::Regex; +use rustc_version::Version; use which::which; mod bindings; @@ -243,26 +245,37 @@ fn include_path(build_dir: &Path, path: &str) -> String { } } -pub fn store_generated_content_if_different(file_to_update: &PathBuf, fresh_content: &[u8]) { +/// If `fresh_content` != `content_file_to_update` (the file is read directly if `content_file_to_update` is None), update the file. prefix is not considered for comparison. +/// If a prefix is given, it will be added as the first line of the file. +pub fn store_generated_content_if_different( + file_to_update: &PathBuf, + fresh_content: &[u8], + content_file_to_update: Option>, + first_line_prefix: Option<&str>, + force_regeneration: bool, +) { 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 existing_file_content = content_file_to_update.unwrap_or_else(|| { + let mut content = Vec::with_capacity(fresh_content.len()); + wrapper_file.read_to_end(content.as_mut()).unwrap(); + content + }); - let mut existing_wrapper_hasher = hash_map::DefaultHasher::new(); - existing_wrapper_hasher.write(existing_file_content.as_ref()); + if !force_regeneration { + 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); + 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; + // 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 @@ -280,6 +293,10 @@ pub fn store_generated_content_if_different(file_to_update: &PathBuf, fresh_cont }; if must_rewrite_file { + if let Some(prefix) = first_line_prefix { + writeln!(&file_to_check, "{prefix}").expect("Could not write prefix"); + } + file_to_check .write_all(fresh_content) .unwrap_or_else(|_| panic!("Unable to write in {}", file_to_update.display())); @@ -293,12 +310,74 @@ pub fn maybe_generate_stub_bindings( 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(), - ); + let current_rustc_version = + rustc_version::version().expect("Could not get current rustc version"); + let semver_re = Regex::new(r"/\* (.*) \*/").unwrap(); + + // We only try to store the stub if the current rustc version is strictly bigger than the one used to generate + // the versioned stub. + let (try_generate, force_regeneration, stub_content): (bool, bool, Option>) = + if let Ok(stub_file) = File::open(stub_bindings_file) { + let mut stub_rdr = BufReader::new(stub_file); + + let mut first_line = String::new(); + let mut stub_content = Vec::::new(); + assert!( + stub_rdr + .read_line(&mut first_line) + .expect("Could not read first line") + > 0, + "Error while reading first line." + ); + + if let Some((_, [version_str])) = semver_re + .captures_iter(&first_line) + .next() + .map(|caps| caps.extract()) + { + // The first line matches the regex + + if let Ok(version) = Version::parse(version_str) { + // The first line contains a version + + stub_rdr + .read_to_end(&mut stub_content) + .expect("could not read stub content"); + (current_rustc_version > version, false, Some(stub_content)) + } else { + stub_rdr.seek(SeekFrom::Start(0)).unwrap(); + stub_rdr + .read_to_end(&mut stub_content) + .expect("could not read stub content"); + + (true, true, Some(stub_content)) + } + } else { + stub_rdr.seek(SeekFrom::Start(0)).unwrap(); + stub_rdr + .read_to_end(&mut stub_content) + .expect("could not read stub content"); + + (true, true, Some(stub_content)) + } + } else { + // No stub file stored + (true, true, None) + }; + + let header = format!("/* {current_rustc_version} */"); + + if try_generate { + store_generated_content_if_different( + stub_bindings_file, + fs::read(bindings_file) + .expect("Could not read generated bindings file") + .as_slice(), + stub_content, + Some(header.as_str()), + force_regeneration, + ); + } } } 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 5faa259f4f..138fd3a954 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 @@ -1,3 +1,4 @@ +/* 1.80.0-nightly */ /* automatically generated by rust-bindgen 0.69.4 */ #[repr(C)] diff --git a/libafl_qemu/runtime/libafl_qemu_stub_bindings.rs b/libafl_qemu/runtime/libafl_qemu_stub_bindings.rs index 179a586e02..97b4a2d5c0 100644 --- a/libafl_qemu/runtime/libafl_qemu_stub_bindings.rs +++ b/libafl_qemu/runtime/libafl_qemu_stub_bindings.rs @@ -1,3 +1,4 @@ +/* 1.80.0-nightly */ /* automatically generated by rust-bindgen 0.69.4 */ pub const _STDINT_H: u32 = 1;