From 2f02c1b90ec26f569dc69ec8470d96643b06b46c Mon Sep 17 00:00:00 2001 From: "Corteggiani, Nassim" Date: Tue, 6 Aug 2024 21:32:58 +0200 Subject: [PATCH] enable differential feedback and execute RTL only if csr feedback is positive --- .../src/differential.rs | 2 +- .../src/differential_feedback.rs | 3 + fuzzers/cva6-vcs-processorfuzz/src/main.rs | 15 +- fuzzers/cva6-vcs-processorfuzz/src/spike.rs | 232 ++++++++++++++++++ libpresifuzz_observers/src/trace_observer.rs | 41 +++- 5 files changed, 279 insertions(+), 14 deletions(-) create mode 100644 fuzzers/cva6-vcs-processorfuzz/src/spike.rs diff --git a/fuzzers/cva6-vcs-processorfuzz/src/differential.rs b/fuzzers/cva6-vcs-processorfuzz/src/differential.rs index ab2643e..442fbfa 100644 --- a/fuzzers/cva6-vcs-processorfuzz/src/differential.rs +++ b/fuzzers/cva6-vcs-processorfuzz/src/differential.rs @@ -116,7 +116,7 @@ where .feedback_mut() .is_interesting(state, mgr, input, observers, &ret1); - if is_corpus.is_ok() && is_corpus.unwrap() == False { + if is_corpus.is_ok() && is_corpus.unwrap() == false { return Ok(ret1); } diff --git a/fuzzers/cva6-vcs-processorfuzz/src/differential_feedback.rs b/fuzzers/cva6-vcs-processorfuzz/src/differential_feedback.rs index 6ca4d92..f3dee0a 100644 --- a/fuzzers/cva6-vcs-processorfuzz/src/differential_feedback.rs +++ b/fuzzers/cva6-vcs-processorfuzz/src/differential_feedback.rs @@ -40,6 +40,7 @@ use std::{ #[derive(Serialize, Deserialize, Clone, Debug)] pub struct DifferentialFeedback { first_name: String, + second_name: String, name: String, counter: u32, } @@ -128,9 +129,11 @@ impl DifferentialFeedback { pub fn new_with_observer( name: &'static str, first_name: &'static str, + second_name: &'static str, ) -> Self { Self { first_name: first_name.to_string(), + second_name: second_name.to_string(), name: name.to_string(), counter: 0, } diff --git a/fuzzers/cva6-vcs-processorfuzz/src/main.rs b/fuzzers/cva6-vcs-processorfuzz/src/main.rs index 83b3931..b33c33a 100644 --- a/fuzzers/cva6-vcs-processorfuzz/src/main.rs +++ b/fuzzers/cva6-vcs-processorfuzz/src/main.rs @@ -68,9 +68,12 @@ use libpresifuzz_observers::verdi_xml_observer::VerdiXMLMapObserver; use libpresifuzz_observers::trace_observer::ExecTraceObserver; use libpresifuzz_observers::trace_observer::ProcessorFuzzExecTraceObserver; +use libpresifuzz_observers::trace_observer::CVA6ExecTraceObserver; pub mod simv; use crate::simv::SimvCommandConfigurator; +pub mod spike; +use crate::spike::SpikeCommandConfigurator; use libpresifuzz_mutators::riscv_isa::riscv_mutations; use libpresifuzz_mutators::scheduled::StdISAScheduledMutator; @@ -80,6 +83,8 @@ use libpresifuzz_ec::manager::*; use libpresifuzz_feedbacks::transferred::TransferredFeedback; use libpresifuzz_stages::sync::SyncFromDiskStage; +use libpresifuzz_feedbacks::csr_feedback::CSRFeedback; + mod differential; mod differential_feedback; //mod verdi_feedback; @@ -209,7 +214,7 @@ pub fn fuzz() { SimvCommandConfigurator::new_from_config_file("config.yml", workdir, &mut [], "", 1); std::env::set_current_dir(&workdir).expect("Unable to change into {dir}"); - let mut spike_trace_observer = ExecTraceObserver::::new("spike_exec_trace_observer", "./spike.log"); + let mut spike_trace_observer = ExecTraceObserver::::new("spike_exec_trace_observer", "./spike.log"); let mut cva6_trace_observer = ExecTraceObserver::::new("cva6_exec_trace_observer", "./cva6.log"); let mut objective = differential_feedback::DifferentialFeedback::new_with_observer( @@ -218,8 +223,7 @@ pub fn fuzz() { "differential_trace_feedback", ); - let processorfuzz_feedback = ProcessorFuzzFeedback::new("spike_exec_trace_observer"); - let feedback = processorfuzz_feedback; + let mut feedback = CSRFeedback::new_with_observer("spike_exec_trace_observer", Some(false)); // Instantiate State with feedback, objective, in/out corpus let mut state = StdState::new( @@ -241,11 +245,12 @@ pub fn fuzz() { // Finally, instantiate the fuzzer let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); + let spike = SpikeCommandConfigurator::new_from_config_file("config.yml", workdir); + let mut executor = differential::DiffExecutor::new( spike.into_executor(tuple_list!()), simv.into_executor(tuple_list!()), - tuple_list!(spike_trace_observer, rocket_trace_observer), - processorfuzz_feedback + tuple_list!(spike_trace_observer, cva6_trace_observer), ); let corpus_dir = PathBuf::from(corpus_dir.to_string()); diff --git a/fuzzers/cva6-vcs-processorfuzz/src/spike.rs b/fuzzers/cva6-vcs-processorfuzz/src/spike.rs new file mode 100644 index 0000000..e53cff3 --- /dev/null +++ b/fuzzers/cva6-vcs-processorfuzz/src/spike.rs @@ -0,0 +1,232 @@ +// SPDX-FileCopyrightText: 2022 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +#![cfg_attr( + test, + deny( + dead_code, + unreachable_code + ) +)] +#![allow(dead_code, unreachable_code)] + + +use libafl::{ + executors::command::CommandConfigurator, + inputs::{HasTargetBytes, Input}, + Error, +}; + +use libafl_bolts::AsSlice; +use libpresifuzz_riscv::dasm::RiscvInstructions; +use libpresifuzz_riscv::elf::ELF; + +use std::time::Duration; +use std::process::Stdio; +use std::assert; +use std::path::Path; +use std::process::{Child, Command}; +use std::env; +use std::os::unix::fs; +use std::io::ErrorKind; + +#[cfg(feature = "debug")] +use color_print::cprintln; + +extern crate yaml_rust; + +#[derive(Default, Debug)] +pub struct SpikeCommandConfigurator { + workdir : String, + args : String, + debug_file : String, + system_timeout_s : u64, + template_file : String, + testcase_name : String, + payload_address : u32, +} + +impl CommandConfigurator for SpikeCommandConfigurator { + + fn spawn_child(&mut self, input: &I) -> Result { + + let old_log = "testcase.elf_spike.log"; + if let Ok(_metadata) = std::fs::metadata(&old_log) { + let _ = std::fs::remove_file(&old_log); + } + + // Spike Command Executor prepares spike inputs and start spike with proper arguments + // 1. Generate testcase in expected format + // #[cfg(feature = "shared_memory_testcase")] + // self.generate_testcase_shm(input); + // #[cfg(feature = "file_testcase")] + self.generate_testcase_file(input); + + #[cfg(feature = "debug")] + cprintln!("[INFO] Running spike ..."); + + // 2. args string into vec + let forged_cmd_args = format!("\ + -l --log={workdir}/testcase.elf_spike.log \ + --log-commits \ + -d --debug-cmd={workdir}/{debug_file} \ + {args} \ + {workdir}/testcase.elf", workdir = self.workdir, debug_file = self.debug_file, args = self.args); + let args_vec: Vec<&str> = forged_cmd_args.split(' ').collect(); + let args_v = &args_vec[0 .. args_vec.len()]; + + #[cfg(feature = "debug")] + println!("Executing command: {:?}", forged_cmd_args); + #[cfg(feature = "debug")] + println!("Executing command: {:?}", args_v); + + // 3. spawn spike + if cfg!(feature = "debug") { + Ok(Command::new("spike") + .args(args_v) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .expect("failed to start process")) + } else { + Ok(Command::new("spike") + .args(args_v) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn() + .expect("failed to start process")) + } + + + } + + fn exec_timeout(&self) -> Duration { + Duration::from_secs(self.system_timeout_s.into()) + } +} + +impl SpikeCommandConfigurator { + + pub fn testcase_name(&self) -> &str { + self.testcase_name.as_str() + } + + pub fn set_testcase_name(&mut self, new_testcase_name: String) { + self.testcase_name = new_testcase_name; + } + + pub fn get_payload_address(&self) -> u32 { + self.payload_address + } + + pub fn new_from_config_file(config_filename: &'static str, workdir: &str) -> SpikeCommandConfigurator { + // parse yaml configuration file to extact: + // * spike arguments + // * spike executable name + // * spike version + // * spike ISA + + let yaml_fd = std::fs::File::open(config_filename).unwrap(); + let config: serde_yaml::Value = serde_yaml::from_reader(yaml_fd).unwrap(); + + let args = config["spike"]["args"] + .as_str() + .unwrap_or(""); + + let debug_file= config["spike"]["debug_file"] + .as_str() + .unwrap_or(""); + + let system_timeout_s = config["spike"]["system_timeout_s"] + .as_u64() + .unwrap_or(0); + + let template_file = config["spike"]["template_file"] + .as_str() + .unwrap_or("iram.elf"); + + #[cfg(feature = "debug")] + { + println!("spike.args = {}", args); + println!("spike.debug_file = {}", debug_file); + println!("spike.system_timeout_s = {}", system_timeout_s); + println!("spike.template_file = {}", template_file); + } + + let mut spike_home = env::current_dir().unwrap().as_os_str().to_str().unwrap().to_string(); + spike_home.push_str("/build"); + + let src_s = vec![ + format!("{}/{}", spike_home, template_file)]; + + let dst_s = vec![ + format!("{}/{}", workdir, template_file)]; + + for i in 0..src_s.len() { + #[cfg(feature = "debug")] + println!("Creating symlink from {src} to {dst}", src = &src_s[i], dst = &dst_s[i]); + + match fs::symlink(&src_s[i], &dst_s[i]) { + Err(e) if e.kind() == ErrorKind::AlreadyExists => { + println!("No need to copy {} because file already exists", &src_s[i]); + }, + Ok(_) => {}, + _ => { panic!("Fail to create symbolic link for simv workdir!");} + }; + } + + let tmp_template_file = format!("{}/{}", workdir, template_file); + println!("{}", tmp_template_file); + let payload_address = Self::find_payload_base_address(&tmp_template_file); + + return SpikeCommandConfigurator { + workdir : workdir.to_string(), + args : args.to_string(), + debug_file : debug_file.to_string(), + system_timeout_s : system_timeout_s, + template_file : template_file.to_string(), + testcase_name : "testcase".to_string(), + payload_address : payload_address, + }; + } + + fn generate_testcase_shm(&self, _input: &I) { + panic!("generate_testcase_shm is not yet available for Spike!"); + } + + fn find_payload_base_address(file_path: &str) -> u32 { + let (address, _offset, _size) = ELF::find_symbol(file_path, "payload").unwrap(); + address as u32 + } + + pub fn generate_testcase_file(&mut self, input: &I) { + assert!(Path::new(&self.template_file).exists()); + + let slice = input.target_bytes(); + let slice = slice.as_slice(); + let riscv_ins = RiscvInstructions::from_le(slice.to_vec()); + + let mut input_filename = String::from(&self.workdir); + input_filename.push_str("/"); + input_filename.push_str(&self.testcase_name); + input_filename.push_str(".elf"); + + let mut elf_template = ELF::new(&self.template_file).unwrap(); + + elf_template.update(&riscv_ins); + elf_template.write_elf(&input_filename).unwrap(); + } +} + + +#[cfg(feature = "std")] +#[cfg(test)] +mod tests { + + #[test] + fn test_spike_executor() { + } +} + diff --git a/libpresifuzz_observers/src/trace_observer.rs b/libpresifuzz_observers/src/trace_observer.rs index ed63931..80cf4d9 100644 --- a/libpresifuzz_observers/src/trace_observer.rs +++ b/libpresifuzz_observers/src/trace_observer.rs @@ -4,6 +4,7 @@ use libafl::{ executors::{ExitKind}, + observers::{DifferentialObserver, ObserversTuple}, observers::{Observer}, Error, inputs::{UsesInput}, @@ -13,7 +14,6 @@ use core::{fmt::Debug}; use serde::{Deserialize, Serialize}; use libafl_bolts::{HasLen, Named}; use std::fmt::{self, Display, Formatter}; -use std::str::FromStr; use std::num::ParseIntError; use std::fs::File; @@ -349,6 +349,31 @@ where } } +impl DifferentialObserver for ExecTraceObserver +where + OTA: ObserversTuple, + OTB: ObserversTuple, + S: UsesInput, + T: ExecTraceParser, +{ + fn pre_observe_first(&mut self, _observers: &mut OTA) -> Result<(), Error> { + Ok(()) + } + + fn post_observe_first(&mut self, _observers: &mut OTA) -> Result<(), Error> { + Ok(()) + } + + fn pre_observe_second(&mut self, _observers: &mut OTB) -> Result<(), Error> { + Ok(()) + } + + fn post_observe_second(&mut self, _observers: &mut OTB) -> Result<(), Error> { + Ok(()) + } +} + + #[derive(Copy, Clone, Serialize, Deserialize, Debug)] pub struct SpikeExecTraceObserver; @@ -404,6 +429,7 @@ impl ExecTraceParser for SpikeExecTraceObserver } } + #[derive(Copy, Clone, Serialize, Deserialize, Debug)] pub struct ProcessorFuzzExecTraceObserver; @@ -508,12 +534,12 @@ impl ExecTraceParser for ProcessorFuzzExecTraceObserver // } #[derive(Copy, Clone, Serialize, Deserialize, Debug)] -pub struct CVA6ExecTrace; +pub struct CVA6ExecTraceObserver; -impl ExecTraceParser for CVA6ExecTrace +impl ExecTraceParser for CVA6ExecTraceObserver { fn new() -> Self { - CVA6ExecTrace {} + CVA6ExecTraceObserver {} } fn parse(&self, workdir: &str) -> Result, Error> { @@ -540,12 +566,11 @@ impl ExecTraceParser for CVA6ExecTrace Ok(trace) } - // OpLog::RegOp(RegOp{op_type: OpType::Read, name: caps[6].to_string(), value: u64::from_str_radix(&caps[7], 16).unwrap()}) - // OpLog::RegOp(RegOp{op_type: OpType::Write, name: caps[2].to_string(), value: u64::from_str_radix(&caps[3], 16).unwrap()}), } -impl CVA6ExecTrace +impl CVA6ExecTraceObserver + { fn parse_hex_u64(&self, s: &str) -> Result { @@ -632,7 +657,7 @@ impl CVA6ExecTrace let exec_address = self.parse_hex_u64(parts[4].trim_end_matches(',')).ok()?; let cause = parts[6].to_string(); let tval = self.parse_hex_u64(parts[8]).ok()?; - let mut ops = vec![ + let ops = vec![ OpLog::RegOp(RegOp{op_type: OpType::Trap, name: cause, value: tval}) ];