diff --git a/docs/command-replacements.md b/docs/command-replacements.md index c538fe8d9..06dc4df49 100644 --- a/docs/command-replacements.md +++ b/docs/command-replacements.md @@ -18,6 +18,7 @@ The following values are replaced with the specific values at runtime. * `{runtime_dir}`: Path to the runtime directory for the task * `{tools_dir}`: Path to the task specific `tools` directory * `{setup_dir}` : Path to the setup directory +* `{coverage_dir}`: Path to the coverage directory for the task * `{job_id}`: UUID that indicates the Job ID * `{task_id}`: UUID that indicates the Task ID * `{reports_dir}`: Path to the directory to write crash reports @@ -64,7 +65,8 @@ These are currently used in the following tasks: * generic\_supervisor: `crashes`, `runtime_dir`, `target_exe`, `target_options`, `input_corpus`, `input`, `supervisor_exe`, `supervisor_options`, `tools_dir`, `job_id`, `task_id`, `crashes_account`, `crashes_container`, `reports_dir`, - `microsoft_telemetry_key`, `instance_telemetry_key`, `setup_dir` + `microsoft_telemetry_key`, `instance_telemetry_key`, `setup_dir`, + `coverage_dir` * generic\_merge: `input`, `input_corpus`, `output_dir`, `target_exe`, `target_options`, `supervisor_exe`, `supervisor_options`, `tools_dir`, `job_id`, `task_id`, `microsoft_telemetry_key`, `instance_telemetry_key`, diff --git a/src/agent/onefuzz-agent/src/tasks/fuzz/supervisor.rs b/src/agent/onefuzz-agent/src/tasks/fuzz/supervisor.rs index df950c281..15038ff36 100644 --- a/src/agent/onefuzz-agent/src/tasks/fuzz/supervisor.rs +++ b/src/agent/onefuzz-agent/src/tasks/fuzz/supervisor.rs @@ -15,7 +15,10 @@ use onefuzz::{ fs::{has_files, set_executable, OwnedDir}, jitter::delay_with_jitter, process::monitor_process, - syncdir::{SyncOperation::Pull, SyncedDir}, + syncdir::{ + SyncOperation::{Pull, Push}, + SyncedDir, + }, }; use onefuzz_telemetry::Event::{new_coverage, new_result}; use serde::Deserialize; @@ -49,6 +52,7 @@ pub struct SupervisorConfig { pub reports: Option, pub unique_reports: Option, pub no_repro: Option, + pub coverage: Option, #[serde(flatten)] pub common: CommonConfig, } @@ -73,6 +77,12 @@ pub async fn spawn(config: SupervisorConfig) -> Result<(), Error> { crashes.init().await?; let monitor_crashes = crashes.monitor_results(new_result, false); + // setup coverage + if let Some(coverage) = &config.coverage { + coverage.init_pull().await?; + } + let monitor_coverage_future = monitor_coverage(&config.coverage, config.ensemble_sync_delay); + // setup reports let reports_dir = tempdir()?; if let Some(unique_reports) = &config.unique_reports { @@ -151,11 +161,22 @@ pub async fn spawn(config: SupervisorConfig) -> Result<(), Error> { monitor_inputs, continuous_sync_task, monitor_reports_future, + monitor_coverage_future, )?; Ok(()) } +async fn monitor_coverage( + coverage: &Option, + ensemble_sync_delay: Option, +) -> Result<()> { + if let Some(coverage) = coverage { + coverage.continuous_sync(Push, ensemble_sync_delay).await?; + } + Ok(()) +} + async fn heartbeat_process( stopped: &Notify, heartbeat_client: Option, @@ -186,6 +207,9 @@ async fn start_supervisor( .set_optional_ref(&config.tools, |expand, tools| { expand.tools_dir(&tools.local_path) }) + .set_optional_ref(&config.coverage, |expand, coverage| { + expand.coverage_dir(&coverage.local_path) + }) .set_optional_ref(&config.target_exe, |expand, target_exe| { expand.target_exe(target_exe) }) @@ -345,6 +369,7 @@ mod tests { reports: None, unique_reports: None, no_repro: None, + coverage: None, common: CommonConfig::default(), }; diff --git a/src/agent/onefuzz/src/expand.rs b/src/agent/onefuzz/src/expand.rs index 6e02c9d5d..508b104ea 100644 --- a/src/agent/onefuzz/src/expand.rs +++ b/src/agent/onefuzz/src/expand.rs @@ -32,6 +32,7 @@ pub enum PlaceHolder { InputFileName, RuntimeDir, ToolsDir, + CoverageDir, GeneratorExe, GeneratorOptions, SupervisorExe, @@ -63,6 +64,7 @@ impl PlaceHolder { Self::InputFileName => "{input_file_name}", Self::RuntimeDir => "{runtime_dir}", Self::ToolsDir => "{tools_dir}", + Self::CoverageDir => "{coverage_dir}", Self::GeneratorExe => "{generator_exe}", Self::GeneratorOptions => "{generator_options}", Self::SupervisorExe => "{supervisor_exe}", @@ -267,6 +269,12 @@ impl<'a> Expand<'a> { self.set_value(PlaceHolder::SetupDir, ExpandedValue::Path(path)) } + pub fn coverage_dir(self, arg: impl AsRef) -> Self { + let arg = arg.as_ref(); + let path = String::from(arg.to_string_lossy()); + self.set_value(PlaceHolder::CoverageDir, ExpandedValue::Path(path)) + } + pub fn task_id(self, arg: &Uuid) -> Self { let value = arg.to_hyphenated().to_string(); self.set_value(PlaceHolder::TaskId, ExpandedValue::Scalar(value)) diff --git a/src/api-service/__app__/onefuzzlib/tasks/defs.py b/src/api-service/__app__/onefuzzlib/tasks/defs.py index 67e741be1..33bdd80a7 100644 --- a/src/api-service/__app__/onefuzzlib/tasks/defs.py +++ b/src/api-service/__app__/onefuzzlib/tasks/defs.py @@ -319,6 +319,16 @@ TASK_DEFINITIONS = { ContainerPermission.List, ], ), + ContainerDefinition( + type=ContainerType.coverage, + compare=Compare.AtMost, + value=1, + permissions=[ + ContainerPermission.Write, + ContainerPermission.Read, + ContainerPermission.List, + ], + ), ], monitor_queue=None, ),