Prefix target_exe with setup dir at use sites (#2405)

This commit is contained in:
Joe Ranweiler
2022-09-29 13:47:04 -07:00
committed by GitHub
parent 0c4cd5414d
commit 41f973184e
18 changed files with 264 additions and 52 deletions

View File

@ -160,11 +160,11 @@ public class Config : IConfig {
} }
if (definition.Features.Contains(TaskFeature.TargetExe)) { if (definition.Features.Contains(TaskFeature.TargetExe)) {
config.TargetExe = $"setup/{task.Config.Task.TargetExe}"; config.TargetExe = task.Config.Task.TargetExe;
} }
if (definition.Features.Contains(TaskFeature.TargetExeOptional) && config.TargetExe != null) { if (definition.Features.Contains(TaskFeature.TargetExeOptional) && config.TargetExe != null) {
config.TargetExe = $"setup/{task.Config.Task.TargetExe}"; config.TargetExe = task.Config.Task.TargetExe;
} }
if (definition.Features.Contains(TaskFeature.TargetEnv)) { if (definition.Features.Contains(TaskFeature.TargetEnv)) {
@ -257,9 +257,8 @@ public class Config : IConfig {
} }
if (definition.Features.Contains(TaskFeature.CoverageFilter)) { if (definition.Features.Contains(TaskFeature.CoverageFilter)) {
var coverageFilter = task.Config.Task.CoverageFilter; if (task.Config.Task.CoverageFilter != null) {
if (coverageFilter != null) { config.CoverageFilter = task.Config.Task.CoverageFilter;
config.CoverageFilter = $"setup/{coverageFilter}";
} }
} }

View File

@ -3,6 +3,7 @@
use crate::tasks::{ use crate::tasks::{
config::CommonConfig, heartbeat::HeartbeatSender, report::crash_report::monitor_reports, config::CommonConfig, heartbeat::HeartbeatSender, report::crash_report::monitor_reports,
utils::try_resolve_setup_relative_path,
}; };
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use onefuzz::{az_copy, blob::url::BlobUrl}; use onefuzz::{az_copy, blob::url::BlobUrl};
@ -194,11 +195,14 @@ pub async fn run_tool(
config: &Config, config: &Config,
reports_dir: &Option<PathBuf>, reports_dir: &Option<PathBuf>,
) -> Result<()> { ) -> Result<()> {
let target_exe =
try_resolve_setup_relative_path(&config.common.setup_dir, &config.target_exe).await?;
let expand = Expand::new() let expand = Expand::new()
.machine_id() .machine_id()
.await? .await?
.input_path(&input) .input_path(&input)
.target_exe(&config.target_exe) .target_exe(&target_exe)
.target_options(&config.target_options) .target_options(&config.target_options)
.analyzer_exe(&config.analyzer_exe) .analyzer_exe(&config.analyzer_exe)
.analyzer_options(&config.analyzer_options) .analyzer_options(&config.analyzer_options)

View File

@ -26,6 +26,7 @@ use crate::tasks::{
coverage::COBERTURA_COVERAGE_FILE, coverage::COBERTURA_COVERAGE_FILE,
generic::input_poller::{CallbackImpl, InputPoller, Processor}, generic::input_poller::{CallbackImpl, InputPoller, Processor},
heartbeat::{HeartbeatSender, TaskHeartbeatClient}, heartbeat::{HeartbeatSender, TaskHeartbeatClient},
utils::try_resolve_setup_relative_path,
}; };
const MAX_COVERAGE_RECORDING_ATTEMPTS: usize = 2; const MAX_COVERAGE_RECORDING_ATTEMPTS: usize = 2;
@ -257,13 +258,17 @@ impl<'a> TaskContext<'a> {
} }
async fn command_for_input(&self, input: &Path) -> Result<Command> { async fn command_for_input(&self, input: &Path) -> Result<Command> {
let target_exe =
try_resolve_setup_relative_path(&self.config.common.setup_dir, &self.config.target_exe)
.await?;
let expand = Expand::new() let expand = Expand::new()
.machine_id() .machine_id()
.await? .await?
.input_path(input) .input_path(input)
.job_id(&self.config.common.job_id) .job_id(&self.config.common.job_id)
.setup_dir(&self.config.common.setup_dir) .setup_dir(&self.config.common.setup_dir)
.target_exe(&self.config.target_exe) .target_exe(&target_exe)
.target_options(&self.config.target_options) .target_options(&self.config.target_options)
.task_id(&self.config.common.task_id); .task_id(&self.config.common.task_id);

View File

@ -27,6 +27,7 @@ use url::Url;
use crate::tasks::config::CommonConfig; use crate::tasks::config::CommonConfig;
use crate::tasks::generic::input_poller::{CallbackImpl, InputPoller, Processor}; use crate::tasks::generic::input_poller::{CallbackImpl, InputPoller, Processor};
use crate::tasks::heartbeat::{HeartbeatSender, TaskHeartbeatClient}; use crate::tasks::heartbeat::{HeartbeatSender, TaskHeartbeatClient};
use crate::tasks::utils::{resolve_setup_relative_path, try_resolve_setup_relative_path};
use super::COBERTURA_COVERAGE_FILE; use super::COBERTURA_COVERAGE_FILE;
@ -138,9 +139,17 @@ impl CoverageTask {
return Ok(CmdFilter::default()); return Ok(CmdFilter::default());
}; };
// Ensure users can locate the filter relative to the setup container. let resolved =
let expand = Expand::new().setup_dir(&self.config.common.setup_dir); resolve_setup_relative_path(&self.config.common.setup_dir, raw_filter_path).await?;
let filter_path = expand.evaluate_value(raw_filter_path)?; let filter_path = if let Some(path) = resolved {
path
} else {
error!(
"unable to resolve setup-relative coverage filter path: {}",
raw_filter_path
);
return Ok(CmdFilter::default());
};
let data = fs::read(&filter_path).await?; let data = fs::read(&filter_path).await?;
let def: CmdFilterDef = serde_json::from_slice(&data)?; let def: CmdFilterDef = serde_json::from_slice(&data)?;
@ -275,17 +284,21 @@ impl<'a> TaskContext<'a> {
} }
async fn command_for_input(&self, input: &Path) -> Result<Command> { async fn command_for_input(&self, input: &Path) -> Result<Command> {
let target_exe =
try_resolve_setup_relative_path(&self.config.common.setup_dir, &self.config.target_exe)
.await?;
let expand = Expand::new() let expand = Expand::new()
.machine_id() .machine_id()
.await? .await?
.input_path(input) .input_path(input)
.job_id(&self.config.common.job_id) .job_id(&self.config.common.job_id)
.setup_dir(&self.config.common.setup_dir) .setup_dir(&self.config.common.setup_dir)
.target_exe(&self.config.target_exe) .target_exe(&target_exe)
.target_options(&self.config.target_options) .target_options(&self.config.target_options)
.task_id(&self.config.common.task_id); .task_id(&self.config.common.task_id);
let mut cmd = Command::new(&self.config.target_exe); let mut cmd = Command::new(&target_exe);
let target_options = expand.evaluate(&self.config.target_options)?; let target_options = expand.evaluate(&self.config.target_options)?;
cmd.args(target_options); cmd.args(target_options);

View File

@ -4,7 +4,7 @@
use crate::tasks::{ use crate::tasks::{
config::CommonConfig, config::CommonConfig,
heartbeat::{HeartbeatSender, TaskHeartbeatClient}, heartbeat::{HeartbeatSender, TaskHeartbeatClient},
utils::{self, default_bool_true}, utils::{self, default_bool_true, try_resolve_setup_relative_path},
}; };
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use onefuzz::{ use onefuzz::{
@ -93,9 +93,13 @@ impl GeneratorTask {
} }
async fn fuzzing_loop(&self, heartbeat_client: Option<TaskHeartbeatClient>) -> Result<()> { async fn fuzzing_loop(&self, heartbeat_client: Option<TaskHeartbeatClient>) -> Result<()> {
let target_exe =
try_resolve_setup_relative_path(&self.config.common.setup_dir, &self.config.target_exe)
.await?;
let tester = Tester::new( let tester = Tester::new(
&self.config.common.setup_dir, &self.config.common.setup_dir,
&self.config.target_exe, &target_exe,
&self.config.target_options, &self.config.target_options,
&self.config.target_env, &self.config.target_env,
) )

View File

@ -51,7 +51,7 @@ pub trait LibFuzzerType: Send + Sync {
/// ///
/// This may include things like setting special environment variables, or overriding /// This may include things like setting special environment variables, or overriding
/// the defaults or values of some command arguments. /// the defaults or values of some command arguments.
fn from_config(config: &Config<Self>) -> LibFuzzer; async fn from_config(config: &Config<Self>) -> Result<LibFuzzer>;
/// Perform any environmental setup common to all targets of this fuzzer type. /// Perform any environmental setup common to all targets of this fuzzer type.
/// ///
@ -141,7 +141,7 @@ where
directories.append(&mut dirs); directories.append(&mut dirs);
} }
let fuzzer = L::from_config(&self.config); let fuzzer = L::from_config(&self.config).await?;
fuzzer fuzzer
.verify(self.config.check_fuzzer_help, Some(directories)) .verify(self.config.check_fuzzer_help, Some(directories))
@ -236,12 +236,7 @@ where
.for_each(|d| inputs.push(&d.local_path)); .for_each(|d| inputs.push(&d.local_path));
} }
let fuzzer = LibFuzzer::new( let fuzzer = L::from_config(&self.config).await?;
&self.config.target_exe,
self.config.target_options.clone(),
self.config.target_env.clone(),
&self.config.common.setup_dir,
);
let mut running = fuzzer.fuzz(crash_dir.path(), local_inputs, &inputs).await?; let mut running = fuzzer.fuzz(crash_dir.path(), local_inputs, &inputs).await?;
let notify = Arc::new(Notify::new()); let notify = Arc::new(Notify::new());

View File

@ -46,7 +46,7 @@ pub struct LibFuzzerDotnetConfig {
impl common::LibFuzzerType for LibFuzzerDotnet { impl common::LibFuzzerType for LibFuzzerDotnet {
type Config = LibFuzzerDotnetConfig; type Config = LibFuzzerDotnetConfig;
fn from_config(config: &common::Config<Self>) -> LibFuzzer { async fn from_config(config: &common::Config<Self>) -> Result<LibFuzzer> {
// Configure loader to fuzz user target DLL. // Configure loader to fuzz user target DLL.
let mut env = config.target_env.clone(); let mut env = config.target_env.clone();
env.insert( env.insert(
@ -65,12 +65,12 @@ impl common::LibFuzzerType for LibFuzzerDotnet {
let mut options = config.target_options.clone(); let mut options = config.target_options.clone();
options.push(format!("--target_path={}", LOADER_PATH)); options.push(format!("--target_path={}", LOADER_PATH));
LibFuzzer::new( Ok(LibFuzzer::new(
LIBFUZZER_DOTNET_PATH, LIBFUZZER_DOTNET_PATH,
options, options,
env, env,
&config.common.setup_dir, &config.common.setup_dir,
) ))
} }
async fn extra_setup(config: &common::Config<Self>) -> Result<()> { async fn extra_setup(config: &common::Config<Self>) -> Result<()> {

View File

@ -1,10 +1,12 @@
// Copyright (c) Microsoft Corporation. // Copyright (c) Microsoft Corporation.
// Licensed under the MIT License. // Licensed under the MIT License.
use anyhow::Result;
use async_trait::async_trait; use async_trait::async_trait;
use onefuzz::libfuzzer::LibFuzzer; use onefuzz::libfuzzer::LibFuzzer;
use crate::tasks::fuzz::libfuzzer::common; use crate::tasks::fuzz::libfuzzer::common;
use crate::tasks::utils::try_resolve_setup_relative_path;
/// Generic LibFuzzer with no special extra configuration. /// Generic LibFuzzer with no special extra configuration.
/// ///
@ -17,13 +19,16 @@ pub struct GenericLibFuzzer;
impl common::LibFuzzerType for GenericLibFuzzer { impl common::LibFuzzerType for GenericLibFuzzer {
type Config = (); type Config = ();
fn from_config(config: &common::Config<Self>) -> LibFuzzer { async fn from_config(config: &common::Config<Self>) -> Result<LibFuzzer> {
LibFuzzer::new( let target_exe =
&config.target_exe, try_resolve_setup_relative_path(&config.common.setup_dir, &config.target_exe).await?;
Ok(LibFuzzer::new(
&target_exe,
config.target_options.clone(), config.target_options.clone(),
config.target_env.clone(), config.target_env.clone(),
&config.common.setup_dir, &config.common.setup_dir,
) ))
} }
} }

View File

@ -7,7 +7,7 @@ use crate::tasks::{
heartbeat::{HeartbeatSender, TaskHeartbeatClient}, heartbeat::{HeartbeatSender, TaskHeartbeatClient},
report::crash_report::monitor_reports, report::crash_report::monitor_reports,
stats::common::{monitor_stats, StatsFormat}, stats::common::{monitor_stats, StatsFormat},
utils::CheckNotify, utils::{try_resolve_setup_relative_path, CheckNotify},
}; };
use anyhow::{Context, Error, Result}; use anyhow::{Context, Error, Result};
use onefuzz::{ use onefuzz::{
@ -198,6 +198,12 @@ async fn start_supervisor(
inputs: &SyncedDir, inputs: &SyncedDir,
reports_dir: PathBuf, reports_dir: PathBuf,
) -> Result<Child> { ) -> Result<Child> {
let target_exe = if let Some(target_exe) = &config.target_exe {
Some(try_resolve_setup_relative_path(&config.common.setup_dir, target_exe).await?)
} else {
None
};
let expand = Expand::new() let expand = Expand::new()
.machine_id() .machine_id()
.await? .await?
@ -216,7 +222,7 @@ async fn start_supervisor(
.set_optional_ref(&config.coverage, |expand, coverage| { .set_optional_ref(&config.coverage, |expand, coverage| {
expand.coverage_dir(&coverage.local_path) expand.coverage_dir(&coverage.local_path)
}) })
.set_optional_ref(&config.target_exe, |expand, target_exe| { .set_optional_ref(&target_exe, |expand, target_exe| {
expand.target_exe(target_exe) expand.target_exe(target_exe)
}) })
.set_optional_ref(&config.supervisor_input_marker, |expand, input_marker| { .set_optional_ref(&config.supervisor_input_marker, |expand, input_marker| {

View File

@ -1,7 +1,11 @@
// Copyright (c) Microsoft Corporation. // Copyright (c) Microsoft Corporation.
// Licensed under the MIT License. // Licensed under the MIT License.
use crate::tasks::{config::CommonConfig, heartbeat::HeartbeatSender, utils}; use crate::tasks::{
config::CommonConfig,
heartbeat::HeartbeatSender,
utils::{self, try_resolve_setup_relative_path},
};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use onefuzz::{ use onefuzz::{
expand::Expand, fs::set_executable, http::ResponseExt, jitter::delay_with_jitter, expand::Expand, fs::set_executable, http::ResponseExt, jitter::delay_with_jitter,
@ -123,6 +127,9 @@ async fn try_delete_blob(input_url: Url) -> Result<()> {
} }
async fn merge(config: &Config, output_dir: impl AsRef<Path>) -> Result<()> { async fn merge(config: &Config, output_dir: impl AsRef<Path>) -> Result<()> {
let target_exe =
try_resolve_setup_relative_path(&config.common.setup_dir, &config.target_exe).await?;
let expand = Expand::new() let expand = Expand::new()
.machine_id() .machine_id()
.await? .await?
@ -132,7 +139,7 @@ async fn merge(config: &Config, output_dir: impl AsRef<Path>) -> Result<()> {
.supervisor_exe(&config.supervisor_exe) .supervisor_exe(&config.supervisor_exe)
.supervisor_options(&config.supervisor_options) .supervisor_options(&config.supervisor_options)
.generated_inputs(output_dir) .generated_inputs(output_dir)
.target_exe(&config.target_exe) .target_exe(&target_exe)
.setup_dir(&config.common.setup_dir) .setup_dir(&config.common.setup_dir)
.tools_dir(&config.tools.local_path) .tools_dir(&config.tools.local_path)
.job_id(&config.common.job_id) .job_id(&config.common.job_id)

View File

@ -4,7 +4,7 @@
use crate::tasks::{ use crate::tasks::{
config::CommonConfig, config::CommonConfig,
report::{crash_report::CrashTestResult, generic}, report::{crash_report::CrashTestResult, generic},
utils::default_bool_true, utils::{default_bool_true, try_resolve_setup_relative_path},
}; };
use anyhow::Result; use anyhow::Result;
use async_trait::async_trait; use async_trait::async_trait;
@ -56,10 +56,14 @@ pub struct GenericRegressionTask {
#[async_trait] #[async_trait]
impl RegressionHandler for GenericRegressionTask { impl RegressionHandler for GenericRegressionTask {
async fn get_crash_result(&self, input: PathBuf, input_url: Url) -> Result<CrashTestResult> { async fn get_crash_result(&self, input: PathBuf, input_url: Url) -> Result<CrashTestResult> {
let target_exe =
try_resolve_setup_relative_path(&self.config.common.setup_dir, &self.config.target_exe)
.await?;
let args = generic::TestInputArgs { let args = generic::TestInputArgs {
input_url: Some(input_url), input_url: Some(input_url),
input: &input, input: &input,
target_exe: &self.config.target_exe, target_exe: &target_exe,
target_options: &self.config.target_options, target_options: &self.config.target_options,
target_env: &self.config.target_env, target_env: &self.config.target_env,
setup_dir: &self.config.common.setup_dir, setup_dir: &self.config.common.setup_dir,

View File

@ -4,7 +4,7 @@
use crate::tasks::{ use crate::tasks::{
config::CommonConfig, config::CommonConfig,
report::{crash_report::CrashTestResult, libfuzzer_report}, report::{crash_report::CrashTestResult, libfuzzer_report},
utils::default_bool_true, utils::{default_bool_true, try_resolve_setup_relative_path},
}; };
use anyhow::{Context, Result}; use anyhow::{Context, Result};
@ -55,10 +55,14 @@ pub struct LibFuzzerRegressionTask {
#[async_trait] #[async_trait]
impl RegressionHandler for LibFuzzerRegressionTask { impl RegressionHandler for LibFuzzerRegressionTask {
async fn get_crash_result(&self, input: PathBuf, input_url: Url) -> Result<CrashTestResult> { async fn get_crash_result(&self, input: PathBuf, input_url: Url) -> Result<CrashTestResult> {
let target_exe =
try_resolve_setup_relative_path(&self.config.common.setup_dir, &self.config.target_exe)
.await?;
let args = libfuzzer_report::TestInputArgs { let args = libfuzzer_report::TestInputArgs {
input_url: Some(input_url), input_url: Some(input_url),
input: &input, input: &input,
target_exe: &self.config.target_exe, target_exe: &target_exe,
target_options: &self.config.target_options, target_options: &self.config.target_options,
target_env: &self.config.target_env, target_env: &self.config.target_env,
setup_dir: &self.config.common.setup_dir, setup_dir: &self.config.common.setup_dir,

View File

@ -20,7 +20,7 @@ use crate::tasks::{
config::CommonConfig, config::CommonConfig,
generic::input_poller::*, generic::input_poller::*,
heartbeat::{HeartbeatSender, TaskHeartbeatClient}, heartbeat::{HeartbeatSender, TaskHeartbeatClient},
utils::default_bool_true, utils::{default_bool_true, try_resolve_setup_relative_path},
}; };
const DOTNET_DUMP_TOOL_NAME: &str = "dotnet-dump"; const DOTNET_DUMP_TOOL_NAME: &str = "dotnet-dump";
@ -128,12 +128,11 @@ impl AsanProcessor {
let job_id = self.config.common.task_id; let job_id = self.config.common.task_id;
let task_id = self.config.common.task_id; let task_id = self.config.common.task_id;
let executable = self.config.target_exe.to_owned(); let executable =
try_resolve_setup_relative_path(&self.config.common.setup_dir, &self.config.target_exe)
.await?;
let mut args = vec![ let mut args = vec!["dotnet".to_owned(), executable.display().to_string()];
"dotnet".to_owned(),
self.config.target_exe.display().to_string(),
];
args.extend(self.config.target_options.clone()); args.extend(self.config.target_options.clone());
let env = self.config.target_env.clone(); let env = self.config.target_env.clone();

View File

@ -6,7 +6,7 @@ use crate::tasks::{
config::CommonConfig, config::CommonConfig,
generic::input_poller::{CallbackImpl, InputPoller, Processor}, generic::input_poller::{CallbackImpl, InputPoller, Processor},
heartbeat::{HeartbeatSender, TaskHeartbeatClient}, heartbeat::{HeartbeatSender, TaskHeartbeatClient},
utils::default_bool_true, utils::{default_bool_true, try_resolve_setup_relative_path},
}; };
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use async_trait::async_trait; use async_trait::async_trait;
@ -193,10 +193,14 @@ impl<'a> GenericReportProcessor<'a> {
) -> Result<CrashTestResult> { ) -> Result<CrashTestResult> {
self.heartbeat_client.alive(); self.heartbeat_client.alive();
let target_exe =
try_resolve_setup_relative_path(&self.config.common.setup_dir, &self.config.target_exe)
.await?;
let args = TestInputArgs { let args = TestInputArgs {
input_url, input_url,
input, input,
target_exe: &self.config.target_exe, target_exe: &target_exe,
target_options: &self.config.target_options, target_options: &self.config.target_options,
target_env: &self.config.target_env, target_env: &self.config.target_env,
setup_dir: &self.config.common.setup_dir, setup_dir: &self.config.common.setup_dir,

View File

@ -6,7 +6,7 @@ use crate::tasks::{
config::CommonConfig, config::CommonConfig,
generic::input_poller::*, generic::input_poller::*,
heartbeat::{HeartbeatSender, TaskHeartbeatClient}, heartbeat::{HeartbeatSender, TaskHeartbeatClient},
utils::default_bool_true, utils::{default_bool_true, try_resolve_setup_relative_path},
}; };
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use async_trait::async_trait; use async_trait::async_trait;
@ -65,8 +65,12 @@ impl ReportTask {
} }
pub async fn verify(&self) -> Result<()> { pub async fn verify(&self) -> Result<()> {
let target_exe =
try_resolve_setup_relative_path(&self.config.common.setup_dir, &self.config.target_exe)
.await?;
let fuzzer = LibFuzzer::new( let fuzzer = LibFuzzer::new(
&self.config.target_exe, &target_exe,
self.config.target_options.clone(), self.config.target_options.clone(),
self.config.target_env.clone(), self.config.target_env.clone(),
&self.config.common.setup_dir, &self.config.common.setup_dir,
@ -194,10 +198,15 @@ impl AsanProcessor {
input: &Path, input: &Path,
) -> Result<CrashTestResult> { ) -> Result<CrashTestResult> {
self.heartbeat_client.alive(); self.heartbeat_client.alive();
let target_exe =
try_resolve_setup_relative_path(&self.config.common.setup_dir, &self.config.target_exe)
.await?;
let args = TestInputArgs { let args = TestInputArgs {
input_url, input_url,
input, input,
target_exe: &self.config.target_exe, target_exe: &target_exe,
target_options: &self.config.target_options, target_options: &self.config.target_options,
target_env: &self.config.target_env, target_env: &self.config.target_env,
setup_dir: &self.config.common.setup_dir, setup_dir: &self.config.common.setup_dir,

View File

@ -96,3 +96,157 @@ pub fn parse_key_value(value: String) -> Result<(String, String)> {
pub fn default_bool_true() -> bool { pub fn default_bool_true() -> bool {
true true
} }
/// Try to resolve an ambiguous setup-relative subpath, returning an error if not found.
pub async fn try_resolve_setup_relative_path(
setup_dir: impl AsRef<Path>,
subpath: impl AsRef<Path>,
) -> Result<PathBuf> {
let setup_dir = setup_dir.as_ref();
let subpath = subpath.as_ref();
resolve_setup_relative_path(setup_dir, subpath)
.await?
.ok_or_else(|| {
anyhow::format_err!(
"unable to resolve subpath `{}` under setup dir `{}`",
subpath.display(),
setup_dir.display()
)
})
}
/// Try to resolve an ambiguous setup-relative subpath, returning `None` if not found.
pub async fn resolve_setup_relative_path(
setup_dir: impl AsRef<Path>,
subpath: impl AsRef<Path>,
) -> Result<Option<PathBuf>> {
let setup_dir = setup_dir.as_ref();
let subpath = subpath.as_ref();
// Case: non-legacy `subpath`, relativized to `setup_dir`.
//
// Even if `subpath` is prefixed by `setup`, it is because it truly names a file in a
// non-root subdirectory like `{setup_dir}/setup`.
{
let resolved = setup_dir.join(subpath);
if exists(&resolved).await {
return Ok(Some(resolved));
}
}
// Case: non-legacy `subpath` that uses the `{setup_dir}` placeholder variable.
//
// Note that we do not do full expansion, just a form of restricted find/replace.
{
let subpath = subpath.strip_prefix("{setup_dir}").unwrap_or(subpath);
let resolved = setup_dir.join(subpath);
if exists(&resolved).await {
return Ok(Some(resolved));
}
}
// Case: legacy `subpath`.
//
// The `setup_dir`-relativized `subpath` was prefixed server-side with the hardcoded
// relative path `setup`. We only expect to see this after upgrading deployments that
// have pending tasks with legacy configs.
{
let subpath = subpath.strip_prefix("setup").unwrap_or(subpath);
let resolved = setup_dir.join(subpath);
if exists(&resolved).await {
return Ok(Some(resolved));
}
}
Ok(None)
}
async fn exists(path: impl AsRef<Path>) -> bool {
fs::metadata(path).await.is_ok()
}
#[cfg(test)]
mod tests {
use std::path::Path;
use anyhow::Result;
use tempfile::TempDir;
use super::*;
fn init_setup_dir(actual: impl AsRef<Path>) -> Result<TempDir> {
let actual = actual.as_ref();
let setup_dir = TempDir::new()?;
// If the actual file is nested in any subdirectories of `setup`, ensure that the
// intermediates exist.
if let Some(parent) = actual.parent() {
let intermediate = setup_dir.path().join(parent);
std::fs::create_dir_all(intermediate)?;
}
// Create an (empty) file on-disk for the file being referenced.
std::fs::write(setup_dir.path().join(actual), "")?;
Ok(setup_dir)
}
async fn test_case(relative: impl AsRef<Path>, given: impl AsRef<Path>) -> Result<()> {
let relative = relative.as_ref();
let setup_dir = init_setup_dir(relative)?;
let expected = setup_dir.path().join(relative);
let resolved = resolve_setup_relative_path(setup_dir.path(), given).await?;
assert_eq!(Some(expected), resolved);
Ok(())
}
#[tokio::test]
async fn test_resolve_setup_relative_path_root() -> Result<()> {
const RELATIVE: &str = "fuzz.exe";
test_case(RELATIVE, "fuzz.exe").await?;
test_case(RELATIVE, "setup/fuzz.exe").await?;
test_case(RELATIVE, "{setup_dir}/fuzz.exe").await?;
Ok(())
}
#[tokio::test]
async fn test_resolve_setup_relative_path_nested() -> Result<()> {
const RELATIVE: &str = "x/fuzz.exe";
test_case(RELATIVE, "x/fuzz.exe").await?;
test_case(RELATIVE, "setup/x/fuzz.exe").await?;
test_case(RELATIVE, "{setup_dir}/x/fuzz.exe").await?;
Ok(())
}
#[tokio::test]
async fn test_resolve_setup_relative_path_double_nested() -> Result<()> {
const RELATIVE: &str = "x/y/fuzz.exe";
test_case(RELATIVE, "x/y/fuzz.exe").await?;
test_case(RELATIVE, "setup/x/y/fuzz.exe").await?;
test_case(RELATIVE, "{setup_dir}/x/y/fuzz.exe").await?;
Ok(())
}
#[tokio::test]
async fn test_resolve_setup_relative_path_nested_setup() -> Result<()> {
const RELATIVE: &str = "setup/fuzz.exe";
test_case(RELATIVE, "setup/fuzz.exe").await?;
test_case(RELATIVE, "setup/setup/fuzz.exe").await?;
test_case(RELATIVE, "{setup_dir}/setup/fuzz.exe").await?;
Ok(())
}
}

View File

@ -348,13 +348,13 @@ def build_task_config(job: Job, task: Task) -> TaskUnitConfig:
config.supervisor_input_marker = task_config.task.supervisor_input_marker config.supervisor_input_marker = task_config.task.supervisor_input_marker
if TaskFeature.target_exe in definition.features: if TaskFeature.target_exe in definition.features:
config.target_exe = "setup/%s" % task_config.task.target_exe config.target_exe = task_config.task.target_exe
if ( if (
TaskFeature.target_exe_optional in definition.features TaskFeature.target_exe_optional in definition.features
and task_config.task.target_exe and task_config.task.target_exe
): ):
config.target_exe = "setup/%s" % task_config.task.target_exe config.target_exe = task_config.task.target_exe
if TaskFeature.target_env in definition.features: if TaskFeature.target_env in definition.features:
config.target_env = task_config.task.target_env or EMPTY_DICT config.target_env = task_config.task.target_env or EMPTY_DICT
@ -438,7 +438,7 @@ def build_task_config(job: Job, task: Task) -> TaskUnitConfig:
coverage_filter = task_config.task.coverage_filter coverage_filter = task_config.task.coverage_filter
if coverage_filter is not None: if coverage_filter is not None:
config.coverage_filter = "setup/%s" % coverage_filter config.coverage_filter = coverage_filter
return config return config

View File

@ -550,7 +550,7 @@ class Libfuzzer(Command):
if target_options is None: if target_options is None:
target_options = [] target_options = []
target_options = [ target_options = [
"--target_path={setup_dir}/" f"{target_harness}" "--target_path={setup_dir}/" + "{target_harness}"
] + target_options ] + target_options
helper = JobHelper( helper = JobHelper(