embed coverage debugger scripts (#783)

This commit is contained in:
bmc-msft
2021-04-09 19:16:41 -04:00
committed by GitHub
parent a91b5aae89
commit ef8e200438
2 changed files with 64 additions and 26 deletions

View File

@ -188,7 +188,7 @@ impl CoverageProcessor {
pub async fn new(config: Arc<Config>) -> Result<Self> { pub async fn new(config: Arc<Config>) -> Result<Self> {
let heartbeat_client = config.common.init_heartbeat().await?; let heartbeat_client = config.common.init_heartbeat().await?;
let total = TotalCoverage::new(config.coverage.path.join(TOTAL_COVERAGE)); let total = TotalCoverage::new(config.coverage.path.join(TOTAL_COVERAGE));
let recorder = CoverageRecorder::new(config.clone()); let recorder = CoverageRecorder::new(config.clone()).await?;
let module_totals = BTreeMap::default(); let module_totals = BTreeMap::default();
Ok(Self { Ok(Self {

View File

@ -9,10 +9,8 @@ use std::{
}; };
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use onefuzz::{ use onefuzz::{fs::has_files, sha256::digest_file};
fs::{has_files, OwnedDir}, use tempfile::{tempdir, TempDir};
sha256::digest_file,
};
use tokio::{ use tokio::{
fs, fs,
process::{Child, Command}, process::{Child, Command},
@ -22,17 +20,71 @@ use crate::tasks::coverage::libfuzzer_coverage::Config;
pub struct CoverageRecorder { pub struct CoverageRecorder {
config: Arc<Config>, config: Arc<Config>,
script_dir: OwnedDir, script_path: PathBuf,
// keep _temp_dir such that Drop cleans up temporary files
_temp_dir: Option<TempDir>,
} }
const SYMBOL_EXTRACT_ERROR: &str = "Target appears to be missing sancov instrumentation. This error can also happen if symbols for the target are not available."; const SYMBOL_EXTRACT_ERROR: &str = "Target appears to be missing sancov instrumentation. This error can also happen if symbols for the target are not available.";
impl CoverageRecorder { impl CoverageRecorder {
pub fn new(config: Arc<Config>) -> Self { pub async fn new(config: Arc<Config>) -> Result<Self> {
let script_dir = let (script_path, _temp_dir) = match env::var("ONEFUZZ_TOOLS") {
OwnedDir::new(env::var("ONEFUZZ_TOOLS").unwrap_or_else(|_| "script".to_string())); Ok(tools_dir) => {
let script_path = PathBuf::from(tools_dir);
if cfg!(target_os = "linux") {
(
script_path
.join("linux")
.join("libfuzzer-coverage")
.join("coverage_cmd.py"),
None,
)
} else if cfg!(target_os = "windows") {
(
script_path
.join("win64")
.join("libfuzzer-coverage")
.join("DumpCounters.js"),
None,
)
} else {
bail!("coverage recorder not implemented for target os");
}
}
Err(_) => {
let temp_dir = tempdir()?;
let script_path = if cfg!(target_os = "linux") {
let script_path = temp_dir.path().join("coverage_cmd.py");
let content = include_bytes!(
"../../../../script/linux/libfuzzer-coverage/coverage_cmd.py"
);
fs::write(&script_path, content).await.with_context(|| {
format!("unable to write file: {}", script_path.display())
})?;
script_path
} else if cfg!(target_os = "windows") {
let script_path = temp_dir.path().join("DumpCounters.js");
let content = include_bytes!(
"../../../../script/win64/libfuzzer-coverage/DumpCounters.js"
);
fs::write(&script_path, content).await.with_context(|| {
format!("unable to write file: {}", script_path.display())
})?;
script_path
} else {
bail!("coverage recorder not implemented for target os");
};
Self { config, script_dir } (script_path, Some(temp_dir))
}
};
Ok(Self {
config,
script_path,
_temp_dir,
})
} }
/// Invoke a script to write coverage to a file. /// Invoke a script to write coverage to a file.
@ -103,19 +155,12 @@ impl CoverageRecorder {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
fn invoke_debugger_script(&self, test_input: &Path, output: &Path) -> Result<Child> { fn invoke_debugger_script(&self, test_input: &Path, output: &Path) -> Result<Child> {
let script_path = self
.script_dir
.path()
.join("linux")
.join("libfuzzer-coverage")
.join("coverage_cmd.py");
let mut cmd = Command::new("gdb"); let mut cmd = Command::new("gdb");
cmd.arg(&self.config.target_exe) cmd.arg(&self.config.target_exe)
.arg("-nh") .arg("-nh")
.arg("-batch") .arg("-batch")
.arg("-x") .arg("-x")
.arg(script_path) .arg(&self.script_path)
.arg("-ex") .arg("-ex")
.arg(format!( .arg(format!(
"coverage {} {} {}", "coverage {} {} {}",
@ -139,18 +184,11 @@ impl CoverageRecorder {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
fn invoke_debugger_script(&self, test_input: &Path, output: &Path) -> Result<Child> { fn invoke_debugger_script(&self, test_input: &Path, output: &Path) -> Result<Child> {
let script_path = self
.script_dir
.path()
.join("win64")
.join("libfuzzer-coverage")
.join("DumpCounters.js");
let should_disable_sympath = !self.config.target_env.contains_key("_NT_SYMBOL_PATH"); let should_disable_sympath = !self.config.target_env.contains_key("_NT_SYMBOL_PATH");
let cdb_cmd = format!( let cdb_cmd = format!(
".scriptload {}; !dumpcounters {:?}, {}; q", ".scriptload {}; !dumpcounters {:?}, {}; q",
script_path.to_string_lossy(), self.script_path.to_string_lossy(),
output.to_string_lossy(), output.to_string_lossy(),
should_disable_sympath, should_disable_sympath,
); );