Skip to content
This repository has been archived by the owner on Nov 1, 2023. It is now read-only.

Commit

Permalink
Tevoinea/migrate task types to template (#3397)
Browse files Browse the repository at this point in the history
* Add analysis task

* generic report

* generator task

* Libfuzzer crash report

* Add libfuzzer merge

* libfuzzer regression

* Add libfuzzer test input

* Cleanup

* Update schema

* Some light docs and example templates

* Add testinput task

* Reorganize the code

* Cleanup

* Improve test, update model
  • Loading branch information
tevoinea authored Aug 17, 2023
1 parent b45446a commit 98e986e
Show file tree
Hide file tree
Showing 16 changed files with 1,398 additions and 245 deletions.
67 changes: 65 additions & 2 deletions src/agent/onefuzz-task/src/local/coverage.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use std::path::PathBuf;
use std::{collections::HashMap, path::PathBuf};

use crate::{
local::common::{
Expand All @@ -15,11 +15,17 @@ use crate::{
},
};
use anyhow::Result;
use async_trait::async_trait;
use clap::{Arg, ArgAction, Command};
use flume::Sender;
use onefuzz::syncdir::SyncedDir;
use schemars::JsonSchema;
use storage_queue::QueueClient;

use super::common::{SyncCountDirMonitor, UiEvent};
use super::{
common::{SyncCountDirMonitor, UiEvent},
template::{RunContext, Template},
};

pub fn build_coverage_config(
args: &clap::ArgMatches,
Expand Down Expand Up @@ -127,3 +133,60 @@ pub fn args(name: &'static str) -> Command {
.about("execute a local-only coverage task")
.args(&build_shared_args(false))
}

#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema)]
pub struct Coverage {
target_exe: PathBuf,
target_env: HashMap<String, String>,
target_options: Vec<String>,
target_timeout: Option<u64>,
module_allowlist: Option<String>,
source_allowlist: Option<String>,
input_queue: Option<PathBuf>,
readonly_inputs: Vec<PathBuf>,
coverage: PathBuf,
}

#[async_trait]
impl Template for Coverage {
async fn run(&self, context: &RunContext) -> Result<()> {
let ri: Result<Vec<SyncedDir>> = self
.readonly_inputs
.iter()
.enumerate()
.map(|(index, input)| context.to_sync_dir(format!("readonly_inputs_{index}"), input))
.collect();

let input_q = if let Some(w) = &self.input_queue {
Some(context.monitor_dir(w).await?)
} else {
None
};

let coverage_config = crate::tasks::coverage::generic::Config {
target_exe: self.target_exe.clone(),
target_env: self.target_env.clone(),
target_options: self.target_options.clone(),
target_timeout: None,
readonly_inputs: ri?,
input_queue: input_q,
common: CommonConfig {
task_id: uuid::Uuid::new_v4(),
..context.common.clone()
},
coverage_filter: None,
coverage: context.to_monitored_sync_dir("coverage", self.coverage.clone())?,
module_allowlist: self.module_allowlist.clone(),
source_allowlist: self.source_allowlist.clone(),
};

context
.spawn(async move {
let mut coverage =
crate::tasks::coverage::generic::CoverageTask::new(coverage_config);
coverage.run().await
})
.await;
Ok(())
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# yaml-language-server: $schema=schema.json
# yaml-language-server: $schema=../schema.json

# What I had to do to get this working:
# 1. Update target_exe to point to the target exe
Expand Down Expand Up @@ -26,7 +26,7 @@ tasks:
crashes: *crash
reports: "./reports"
unique_reports: "./unique_reports"
no_repro: "./noe_repro"
no_repro: "./no_repro"
check_fuzzer_help: true

- type: "Coverage"
Expand All @@ -36,8 +36,3 @@ tasks:
input_queue: *inputs
readonly_inputs: [*inputs]
coverage: "./coverage"

# - type: Analysis
# <<: *target_args


21 changes: 21 additions & 0 deletions src/agent/onefuzz-task/src/local/example_templates/radamsa.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# yaml-language-server: $schema=../schema.json

# This template file demonstrates how to configure a radamsa task

target_args: &target_args
target_env: {}
target_exe: "C:\\temp\\onefuzz\\integration\\windows-libfuzzer\\fuzz.exe"
target_options: []

tasks:
- type: Generator
<<: *target_args
crashes: "./crashes"
generator_env: {}
generator_exe: "./path/to/generator"
generator_options: []
readonly_inputs: ["./path/to/readonly-inputs"]
rename_output: true

- type: Report
<<: *target_args
76 changes: 75 additions & 1 deletion src/agent/onefuzz-task/src/local/generic_analysis.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use std::path::PathBuf;
use std::{collections::HashMap, path::PathBuf};

use crate::{
local::common::{
Expand All @@ -16,10 +16,14 @@ use crate::{
},
};
use anyhow::Result;
use async_trait::async_trait;
use clap::{Arg, Command};
use flume::Sender;
use schemars::JsonSchema;
use storage_queue::QueueClient;

use super::template::{RunContext, Template};

pub fn build_analysis_config(
args: &clap::ArgMatches,
input_queue: Option<QueueClient>,
Expand Down Expand Up @@ -131,3 +135,73 @@ pub fn args(name: &'static str) -> Command {
.about("execute a local-only generic analysis")
.args(&build_shared_args(true))
}

#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema)]
pub struct Analysis {
analyzer_exe: String,
analyzer_options: Vec<String>,
analyzer_env: HashMap<String, String>,
target_exe: PathBuf,
target_options: Vec<String>,
input_queue: Option<PathBuf>,
crashes: Option<PathBuf>,
analysis: PathBuf,
tools: PathBuf,
reports: Option<PathBuf>,
unique_reports: Option<PathBuf>,
no_repro: Option<PathBuf>,
}

#[async_trait]
impl Template for Analysis {
async fn run(&self, context: &RunContext) -> Result<()> {
let input_q = if let Some(w) = &self.input_queue {
Some(context.monitor_dir(w).await?)
} else {
None
};

let analysis_config = crate::tasks::analysis::generic::Config {
analyzer_exe: self.analyzer_exe.clone(),
analyzer_options: self.analyzer_options.clone(),
analyzer_env: self.analyzer_env.clone(),

target_exe: self.target_exe.clone(),
target_options: self.target_options.clone(),
input_queue: input_q,
crashes: self
.crashes
.as_ref()
.and_then(|path| context.to_monitored_sync_dir("crashes", path).ok()),

analysis: context.to_monitored_sync_dir("analysis", self.analysis.clone())?,
tools: context
.to_monitored_sync_dir("tools", self.tools.clone())
.ok(),

reports: self
.reports
.as_ref()
.and_then(|path| context.to_monitored_sync_dir("reports", path).ok()),
unique_reports: self
.unique_reports
.as_ref()
.and_then(|path| context.to_monitored_sync_dir("unique_reports", path).ok()),
no_repro: self
.no_repro
.as_ref()
.and_then(|path| context.to_monitored_sync_dir("no_repro", path).ok()),

common: CommonConfig {
task_id: uuid::Uuid::new_v4(),
..context.common.clone()
},
};

context
.spawn(async move { crate::tasks::analysis::generic::run(analysis_config).await })
.await;

Ok(())
}
}
96 changes: 95 additions & 1 deletion src/agent/onefuzz-task/src/local/generic_crash_report.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use std::path::PathBuf;
use std::{collections::HashMap, path::PathBuf};

use crate::{
local::common::{
Expand All @@ -13,13 +13,19 @@ use crate::{
tasks::{
config::CommonConfig,
report::generic::{Config, ReportTask},
utils::default_bool_true,
},
};
use anyhow::Result;
use async_trait::async_trait;
use clap::{Arg, ArgAction, Command};
use flume::Sender;
use futures::future::OptionFuture;
use schemars::JsonSchema;
use storage_queue::QueueClient;

use super::template::{RunContext, Template};

pub fn build_report_config(
args: &clap::ArgMatches,
input_queue: Option<QueueClient>,
Expand Down Expand Up @@ -140,3 +146,91 @@ pub fn args(name: &'static str) -> Command {
.about("execute a local-only generic crash report")
.args(&build_shared_args())
}

#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema)]
pub struct CrashReport {
target_exe: PathBuf,
target_options: Vec<String>,
target_env: HashMap<String, String>,

input_queue: Option<PathBuf>,
crashes: Option<PathBuf>,
reports: Option<PathBuf>,
unique_reports: Option<PathBuf>,
no_repro: Option<PathBuf>,

target_timeout: Option<u64>,

#[serde(default)]
check_asan_log: bool,
#[serde(default = "default_bool_true")]
check_debugger: bool,
#[serde(default)]
check_retry_count: u64,

#[serde(default = "default_bool_true")]
check_queue: bool,

#[serde(default)]
minimized_stack_depth: Option<usize>,
}
#[async_trait]
impl Template for CrashReport {
async fn run(&self, context: &RunContext) -> Result<()> {
let input_q_fut: OptionFuture<_> = self
.input_queue
.iter()
.map(|w| context.monitor_dir(w))
.next()
.into();
let input_q = input_q_fut.await.transpose()?;

let crash_report_config = crate::tasks::report::generic::Config {
target_exe: self.target_exe.clone(),
target_env: self.target_env.clone(),
target_options: self.target_options.clone(),
target_timeout: self.target_timeout,

input_queue: input_q,
crashes: self
.crashes
.clone()
.map(|c| context.to_monitored_sync_dir("crashes", c))
.transpose()?,
reports: self
.reports
.clone()
.map(|c| context.to_monitored_sync_dir("reports", c))
.transpose()?,
unique_reports: self
.unique_reports
.clone()
.map(|c| context.to_monitored_sync_dir("unique_reports", c))
.transpose()?,
no_repro: self
.no_repro
.clone()
.map(|c| context.to_monitored_sync_dir("no_repro", c))
.transpose()?,

check_asan_log: self.check_asan_log,
check_debugger: self.check_debugger,
check_retry_count: self.check_retry_count,
check_queue: self.check_queue,
minimized_stack_depth: self.minimized_stack_depth,
common: CommonConfig {
task_id: uuid::Uuid::new_v4(),
..context.common.clone()
},
};

context
.spawn(async move {
let mut report =
crate::tasks::report::generic::ReportTask::new(crash_report_config);
report.managed_run().await
})
.await;
Ok(())
}
}
Loading

0 comments on commit 98e986e

Please sign in to comment.