Skip to content

Commit dfe7f87

Browse files
Merge pull request #255 from microsoft/main
Fork Sync: Update from parent repository
2 parents 5836beb + 6933521 commit dfe7f87

File tree

13 files changed

+251
-49
lines changed

13 files changed

+251
-49
lines changed

docs/unmanaged-nodes.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,11 @@ linux
142142
```
143143
docker run --it --rm --entrypoint bash <image_name>
144144
```
145+
146+
### mount a local folder in the container
147+
148+
docker allows you to [mount](https://docs.docker.com/storage/bind-mounts/#mount-into-a-non-empty-directory-on-the-container) a local folder when running a container
149+
150+
```
151+
docker run -it --rm --mount type=bind,source=<local_path>,target=<path_in_container>
152+
```

src/agent/.rustfmt.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
edition = "2018"
1+
edition = "2018"
2+
newline_style = "Native"

src/agent/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/agent/dynamic-library/src/linux.rs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use std::collections::{HashMap, HashSet};
66
use std::ffi::OsStr;
7-
use std::io;
7+
use std::io::{self};
88
use std::path::Path;
99
use std::process::Command;
1010

@@ -37,6 +37,19 @@ pub fn find_missing(mut cmd: Command) -> Result<HashSet<MissingDynamicLibrary>,
3737
Ok(logs.missing())
3838
}
3939

40+
pub fn get_linked_library_logs(cmd: &Command) -> Result<std::process::Output, io::Error> {
41+
let library_path = explicit_library_path(cmd);
42+
let output = LinkedDynamicLibraries::run(cmd.get_program(), library_path)?;
43+
Ok(output)
44+
}
45+
46+
pub fn get_loaded_libraries_logs(cmd: Command) -> Result<std::process::Output, io::Error> {
47+
let mut cmd = cmd;
48+
cmd.env("LD_DEBUG", "libs");
49+
let output = cmd.output()?;
50+
Ok(output)
51+
}
52+
4053
// Compute the `LD_LIBRARY_PATH` value that a `Command` sets, if any.
4154
//
4255
// If the command either inherits or unsets the variable, returns `None`.
@@ -253,19 +266,24 @@ impl LinkedDynamicLibraries {
253266
module: impl AsRef<OsStr>,
254267
library_path: Option<&OsStr>,
255268
) -> Result<Self, io::Error> {
269+
let output = Self::run(module, library_path)?;
270+
let linked = Self::parse(&*output.stdout);
271+
Ok(linked)
272+
}
273+
274+
pub fn run(
275+
module: impl AsRef<OsStr>,
276+
library_path: Option<&OsStr>,
277+
) -> Result<std::process::Output, io::Error> {
256278
let mut cmd = Command::new("ldd");
257279
cmd.arg(module);
258-
259280
if let Some(library_path) = library_path {
260281
cmd.env(LD_LIBRARY_PATH, library_path);
261282
} else {
262283
cmd.env_remove(LD_LIBRARY_PATH);
263284
}
264-
265285
let output = cmd.output()?;
266-
let linked = Self::parse(&*output.stdout);
267-
268-
Ok(linked)
286+
Ok(output)
269287
}
270288

271289
pub fn parse<R: io::Read>(readable: R) -> Self {

src/agent/dynamic-library/src/windows.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,28 @@ pub enum CheckDynamicLibrariesError {
2727
pub fn find_missing(
2828
cmd: Command,
2929
) -> Result<Vec<MissingDynamicLibrary>, CheckDynamicLibrariesError> {
30-
let image_file = ImageFile::new(cmd.get_program())?;
31-
let _sls = image_file.show_loader_snaps()?;
30+
let mut handler = LoaderSnapsHandler::default();
31+
setup_debugger(cmd, &mut handler)?;
32+
Ok(handler.missing_libraries())
33+
}
3234

35+
pub fn get_logs(cmd: Command) -> Result<Vec<String>, CheckDynamicLibrariesError> {
3336
let mut handler = LoaderSnapsHandler::default();
37+
setup_debugger(cmd, &mut handler)?;
38+
Ok(handler.debug_strings)
39+
}
3440

41+
fn setup_debugger(
42+
cmd: Command,
43+
handler: &mut LoaderSnapsHandler,
44+
) -> Result<(), CheckDynamicLibrariesError> {
45+
let image_file = ImageFile::new(cmd.get_program())?;
46+
let _sls = image_file.show_loader_snaps()?;
3547
let (mut dbg, _child) =
36-
Debugger::init(cmd, &mut handler).map_err(CheckDynamicLibrariesError::Debugger)?;
37-
48+
Debugger::init(cmd, handler).map_err(CheckDynamicLibrariesError::Debugger)?;
3849
while !dbg.target().exited() {
3950
if !dbg
40-
.process_event(&mut handler, 1000)
51+
.process_event(handler, 1000)
4152
.map_err(CheckDynamicLibrariesError::Debugger)?
4253
{
4354
break;
@@ -47,7 +58,7 @@ pub fn find_missing(
4758
.map_err(CheckDynamicLibrariesError::Debugger)?;
4859
}
4960

50-
Ok(handler.missing_libraries())
61+
Ok(())
5162
}
5263

5364
#[derive(Debug, Error)]

src/agent/onefuzz-agent/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ reqwest-retry = { path = "../reqwest-retry" }
3030
onefuzz-telemetry = { path = "../onefuzz-telemetry" }
3131
backtrace = "0.3"
3232
ipc-channel = { git = "https://github.com/servo/ipc-channel", rev = "7f432aa" }
33+
dynamic-library = {path = "../dynamic-library" }
3334

3435

3536
[target.'cfg(target_family = "unix")'.dependencies]

src/agent/onefuzz-agent/src/main.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ pub mod panic;
4040
pub mod reboot;
4141
pub mod scheduler;
4242
pub mod setup;
43+
pub mod validations;
4344
pub mod work;
4445
pub mod worker;
4546

@@ -48,6 +49,8 @@ enum Opt {
4849
Run(RunOpt),
4950
#[clap(subcommand)]
5051
Debug(debug::DebugOpt),
52+
#[clap(subcommand)]
53+
Validate(validations::ValidationCommand),
5154
Licenses,
5255
Version,
5356
}
@@ -84,11 +87,17 @@ fn main() -> Result<()> {
8487
Opt::Debug(opt) => debug::debug(opt)?,
8588
Opt::Licenses => licenses()?,
8689
Opt::Version => version(),
90+
Opt::Validate(opt) => validate(opt)?,
8791
};
8892

8993
Ok(())
9094
}
9195

96+
fn validate(validation_command: validations::ValidationCommand) -> Result<()> {
97+
let rt = tokio::runtime::Runtime::new()?;
98+
rt.block_on(async { validations::validate(validation_command).await })
99+
}
100+
92101
fn version() {
93102
println!(
94103
"{} onefuzz:{} git:{}",

src/agent/onefuzz-agent/src/setup.rs

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,24 @@ impl SetupRunner {
8080
onefuzz::fs::set_executable(&setup_dir).await?;
8181

8282
// Create setup container directory symlinks for tasks.
83-
for work_unit in &work_set.work_units {
84-
create_setup_symlink(&setup_dir, work_unit, self.machine_id).await?;
83+
let working_dirs = work_set
84+
.work_units
85+
.iter()
86+
.map(|w| w.working_dir(self.machine_id))
87+
.collect::<Vec<_>>()
88+
.into_iter()
89+
.collect::<Result<Vec<_>>>()?;
90+
91+
for work_dir in working_dirs {
92+
create_setup_symlink(&setup_dir, work_dir).await?;
8593
}
8694

95+
Self::run_setup_script(setup_dir).await
96+
}
97+
98+
pub async fn run_setup_script(
99+
setup_dir: impl AsRef<Path>,
100+
) -> std::result::Result<Option<Output>, anyhow::Error> {
87101
// Run setup script, if any.
88102
let setup_script = SetupScript::new(setup_dir).await?;
89103

@@ -111,7 +125,6 @@ impl SetupRunner {
111125
Some(output)
112126
} else {
113127
info!("no setup script to run");
114-
115128
None
116129
};
117130

@@ -120,15 +133,11 @@ impl SetupRunner {
120133
}
121134

122135
#[cfg(target_family = "windows")]
123-
async fn create_setup_symlink(
124-
setup_dir: &Path,
125-
work_unit: &WorkUnit,
126-
machine_id: Uuid,
127-
) -> Result<()> {
136+
async fn create_setup_symlink(setup_dir: &Path, working_dir: impl AsRef<Path>) -> Result<()> {
128137
use std::os::windows::fs::symlink_dir;
129138
use tokio::task::spawn_blocking;
130139

131-
let working_dir = work_unit.working_dir(machine_id)?;
140+
let working_dir = working_dir.as_ref();
132141

133142
let create_work_dir = fs::create_dir_all(&working_dir).await.with_context(|| {
134143
format!(
@@ -162,14 +171,10 @@ async fn create_setup_symlink(
162171
}
163172

164173
#[cfg(target_family = "unix")]
165-
async fn create_setup_symlink(
166-
setup_dir: &Path,
167-
work_unit: &WorkUnit,
168-
machine_id: Uuid,
169-
) -> Result<()> {
174+
async fn create_setup_symlink(setup_dir: &Path, working_dir: impl AsRef<Path>) -> Result<()> {
170175
use tokio::fs::symlink;
171176

172-
let working_dir = work_unit.working_dir(machine_id)?;
177+
let working_dir = working_dir.as_ref();
173178

174179
tokio::fs::create_dir_all(&working_dir)
175180
.await
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
use std::path::{Path, PathBuf};
2+
3+
use crate::setup::SetupRunner;
4+
use anyhow::Result;
5+
use clap::Parser;
6+
use onefuzz::{libfuzzer::LibFuzzer, machine_id::MachineIdentity};
7+
use uuid::Uuid;
8+
9+
#[derive(Parser, Debug)]
10+
#[clap(rename_all = "snake_case")]
11+
pub enum ValidationCommand {
12+
/// Run the setup script
13+
RunSetup { setup_folder: PathBuf },
14+
/// Validate the libfuzzer target
15+
ValidateLibfuzzer(ValidationConfig),
16+
/// Get the execution logs to debug loading issues
17+
ExecutionLog(ValidationConfig),
18+
}
19+
20+
fn parse_key_val<T, U>(
21+
s: &str,
22+
) -> Result<(T, U), Box<dyn std::error::Error + Send + Sync + 'static>>
23+
where
24+
T: std::str::FromStr,
25+
T::Err: std::error::Error + Send + Sync + 'static,
26+
U: std::str::FromStr,
27+
U::Err: std::error::Error + Send + Sync + 'static,
28+
{
29+
let pos = s
30+
.find('=')
31+
.ok_or_else(|| format!("invalid KEY=value: no `=` found in `{s}`"))?;
32+
33+
println!("******** pos: {}", pos);
34+
35+
Ok((s[..pos].parse()?, s[pos + 1..].parse()?))
36+
}
37+
38+
#[derive(Parser, Debug, Deserialize)]
39+
#[clap(rename_all = "snake_case")]
40+
pub struct ValidationConfig {
41+
#[clap(long = "seeds")]
42+
pub seeds: Option<PathBuf>,
43+
#[clap(long = "target_exe")]
44+
pub target_exe: PathBuf,
45+
#[clap(long = "setup_folder")]
46+
pub setup_folder: Option<PathBuf>,
47+
#[clap(long = "target_options")]
48+
pub target_options: Vec<String>,
49+
#[arg(value_parser = parse_key_val::<String, String>, long = "target_env")]
50+
pub target_env: Vec<(String, String)>,
51+
}
52+
53+
pub async fn validate(command: ValidationCommand) -> Result<()> {
54+
match command {
55+
ValidationCommand::RunSetup { setup_folder } => run_setup(setup_folder).await,
56+
ValidationCommand::ValidateLibfuzzer(validation_config) => {
57+
validate_libfuzzer(validation_config).await
58+
}
59+
ValidationCommand::ExecutionLog(validation_config) => get_logs(validation_config).await,
60+
}
61+
}
62+
63+
async fn validate_libfuzzer(config: ValidationConfig) -> Result<()> {
64+
let libfuzzer = LibFuzzer::new(
65+
&config.target_exe,
66+
config.target_options.clone(),
67+
config.target_env.into_iter().collect(),
68+
config
69+
.setup_folder
70+
.unwrap_or(config.target_exe.parent().unwrap().to_path_buf()),
71+
None::<&PathBuf>,
72+
MachineIdentity {
73+
machine_id: Uuid::nil(),
74+
machine_name: "".to_string(),
75+
scaleset_name: None,
76+
},
77+
);
78+
79+
if let Some(seeds) = config.seeds {
80+
libfuzzer.verify(true, Some(vec![seeds])).await?;
81+
}
82+
83+
Ok(())
84+
}
85+
86+
async fn run_setup(setup_folder: impl AsRef<Path>) -> Result<()> {
87+
let output = SetupRunner::run_setup_script(setup_folder.as_ref()).await?;
88+
match output {
89+
Some(output) => {
90+
if !output.exit_status.success {
91+
let error = "error running target setup script".to_owned();
92+
bail!("{}", error);
93+
}
94+
}
95+
None => {
96+
println!("no setup script to run")
97+
}
98+
}
99+
Ok(())
100+
}
101+
102+
async fn get_logs(config: ValidationConfig) -> Result<()> {
103+
let libfuzzer = LibFuzzer::new(
104+
&config.target_exe,
105+
config.target_options.clone(),
106+
config.target_env.into_iter().collect(),
107+
config
108+
.setup_folder
109+
.unwrap_or(config.target_exe.parent().unwrap().to_path_buf()),
110+
None::<&PathBuf>,
111+
MachineIdentity {
112+
machine_id: Uuid::nil(),
113+
machine_name: "".to_string(),
114+
scaleset_name: None,
115+
},
116+
);
117+
let cmd = libfuzzer.build_std_command(None, None, None).await?;
118+
print_logs(cmd)?;
119+
Ok(())
120+
}
121+
122+
#[cfg(target_os = "windows")]
123+
fn print_logs(cmd: std::process::Command) -> Result<(), anyhow::Error> {
124+
let logs = dynamic_library::windows::get_logs(cmd)?;
125+
for log in logs {
126+
println!("{log:x?}");
127+
}
128+
129+
Ok(())
130+
}
131+
132+
#[cfg(target_os = "linux")]
133+
fn print_logs(cmd: std::process::Command) -> Result<(), anyhow::Error> {
134+
let logs = dynamic_library::linux::get_linked_library_logs(&cmd)?;
135+
for log in logs.stdout {
136+
println!("{log:x?}");
137+
}
138+
139+
let logs2 = dynamic_library::linux::get_loaded_libraries_logs(cmd)?;
140+
for log in logs2.stderr {
141+
println!("{log:x?}");
142+
}
143+
144+
Ok(())
145+
}

0 commit comments

Comments
 (0)