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

Commit eefc8be

Browse files
authored
Merge branch 'main' into extra-rw-container
2 parents 3dfd6cc + aff9ffe commit eefc8be

File tree

12 files changed

+141
-60
lines changed

12 files changed

+141
-60
lines changed

src/agent/Cargo.lock

Lines changed: 16 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/agent/coverage/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ license = "MIT"
88
anyhow = { version = "1.0", features = ["backtrace"] }
99
cobertura = { path = "../cobertura" }
1010
debuggable-module = { path = "../debuggable-module" }
11-
iced-x86 = "1.18"
11+
iced-x86 = "1.19"
1212
log = "0.4.17"
1313
regex = "1.8"
1414
symbolic = { version = "10.1", features = [
@@ -22,6 +22,7 @@ thiserror = "1.0"
2222
debugger = { path = "../debugger" }
2323

2424
[target.'cfg(target_os = "linux")'.dependencies]
25+
nix = "0.26"
2526
pete = "0.10"
2627
# For procfs, opt out of the `chrono` freature; it pulls in an old version
2728
# of `time`. We do not use the methods that the `chrono` feature enables.

src/agent/coverage/src/record.rs

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,19 +58,58 @@ impl CoverageRecorder {
5858

5959
#[cfg(target_os = "linux")]
6060
pub fn record(self) -> Result<Recorded> {
61+
use std::sync::Mutex;
62+
63+
use anyhow::bail;
64+
65+
use crate::timer;
6166
use linux::debugger::Debugger;
6267
use linux::LinuxRecorder;
6368

6469
let loader = self.loader.clone();
6570

66-
crate::timer::timed(self.timeout, move || {
67-
let mut recorder = LinuxRecorder::new(&loader, self.allowlist);
68-
let dbg = Debugger::new(&mut recorder);
69-
let output = dbg.run(self.cmd)?;
70-
let coverage = recorder.coverage;
71+
let child_pid: Arc<Mutex<Option<u32>>> = Arc::new(Mutex::new(None));
7172

72-
Ok(Recorded { coverage, output })
73-
})?
73+
let recorded = {
74+
let child_pid = child_pid.clone();
75+
76+
timer::timed(self.timeout, move || {
77+
let mut recorder = LinuxRecorder::new(&loader, self.allowlist);
78+
let mut dbg = Debugger::new(&mut recorder);
79+
let child = dbg.spawn(self.cmd)?;
80+
81+
// Save child PID so we can send SIGKILL on timeout.
82+
if let Ok(mut pid) = child_pid.lock() {
83+
*pid = Some(child.id());
84+
} else {
85+
bail!("couldn't lock mutex to save child PID ");
86+
}
87+
88+
let output = dbg.wait(child)?;
89+
let coverage = recorder.coverage;
90+
91+
Ok(Recorded { coverage, output })
92+
})
93+
};
94+
95+
if let Err(timer::TimerError::Timeout(..)) = &recorded {
96+
let Ok(pid) = child_pid.lock() else {
97+
bail!("couldn't lock mutex to kill child PID");
98+
};
99+
100+
if let Some(pid) = *pid {
101+
use nix::sys::signal::{kill, SIGKILL};
102+
103+
let pid = pete::Pid::from_raw(pid as i32);
104+
105+
// Try to clean up, ignore errors due to earlier exits.
106+
let _ = kill(pid, SIGKILL);
107+
} else {
108+
warn!("timeout before PID set for child process");
109+
}
110+
}
111+
112+
recorded?
74113
}
75114

76115
#[cfg(target_os = "windows")]

src/agent/coverage/src/record/linux/debugger.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
use std::collections::BTreeMap;
55
use std::io::Read;
6-
use std::process::Command;
6+
use std::process::{Child, Command};
77

88
use anyhow::{bail, format_err, Result};
99
use debuggable_module::path::FilePath;
@@ -39,9 +39,11 @@ impl<'eh> Debugger<'eh> {
3939
}
4040
}
4141

42-
pub fn run(mut self, cmd: Command) -> Result<Output> {
43-
let mut child = self.context.tracer.spawn(cmd)?;
42+
pub fn spawn(&mut self, cmd: Command) -> Result<Child> {
43+
Ok(self.context.tracer.spawn(cmd)?)
44+
}
4445

46+
pub fn wait(self, mut child: Child) -> Result<Output> {
4547
if let Err(err) = self.wait_on_stops() {
4648
// Ignore error if child already exited.
4749
let _ = child.kill();
@@ -52,22 +54,29 @@ impl<'eh> Debugger<'eh> {
5254
// Currently unavailable on Linux.
5355
let status = None;
5456

55-
let stdout = if let Some(mut pipe) = child.stdout {
57+
let stdout = if let Some(pipe) = &mut child.stdout {
5658
let mut stdout = Vec::new();
5759
pipe.read_to_end(&mut stdout)?;
5860
String::from_utf8_lossy(&stdout).into_owned()
5961
} else {
6062
"".into()
6163
};
6264

63-
let stderr = if let Some(mut pipe) = child.stderr {
65+
let stderr = if let Some(pipe) = &mut child.stderr {
6466
let mut stderr = Vec::new();
6567
pipe.read_to_end(&mut stderr)?;
6668
String::from_utf8_lossy(&stderr).into_owned()
6769
} else {
6870
"".into()
6971
};
7072

73+
// Clean up, ignoring output that we've already gathered.
74+
//
75+
// These calls should also be unnecessary no-ops, but we really want to avoid any dangling
76+
// or zombie child processes.
77+
let _ = child.kill();
78+
let _ = child.wait();
79+
7180
let output = Output {
7281
status,
7382
stderr,

src/agent/coverage/src/timer.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ use std::sync::mpsc;
55
use std::thread;
66
use std::time::Duration;
77

8-
use anyhow::{bail, Result};
8+
use thiserror::Error;
99

10-
pub fn timed<F, T>(timeout: Duration, function: F) -> Result<T>
10+
pub fn timed<F, T>(timeout: Duration, function: F) -> Result<T, TimerError>
1111
where
1212
T: Send + 'static,
1313
F: FnOnce() -> T + Send + 'static,
@@ -25,13 +25,23 @@ where
2525
let _ = timer_sender.send(Timed::Timeout);
2626
});
2727

28-
match receiver.recv()? {
29-
Timed::Done(out) => Ok(out),
30-
Timed::Timeout => bail!("function exceeded timeout of {:?}", timeout),
28+
match receiver.recv() {
29+
Ok(Timed::Done(out)) => Ok(out),
30+
Ok(Timed::Timeout) => Err(TimerError::Timeout(timeout)),
31+
Err(recv) => Err(TimerError::Recv(recv)),
3132
}
3233
}
3334

3435
enum Timed<T> {
3536
Done(T),
3637
Timeout,
3738
}
39+
40+
#[derive(Debug, Error)]
41+
pub enum TimerError {
42+
#[error("timer threads exited without sending messages")]
43+
Recv(mpsc::RecvError),
44+
45+
#[error("function exceeded timeout of {0:?}")]
46+
Timeout(Duration),
47+
}

src/agent/debuggable-module/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ anyhow = "1.0"
99
elsa = "1.8.1"
1010
gimli = "0.27.2"
1111
goblin = "0.6.0"
12-
iced-x86 = "1.18"
12+
iced-x86 = "1.19"
1313
log = "0.4.17"
1414
pdb = "0.8.0"
1515
regex = "1.8"

src/agent/debugger/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ license = "MIT"
99
anyhow = "1.0"
1010
fnv = "1.0"
1111
goblin = "0.5"
12-
iced-x86 = "1.18"
12+
iced-x86 = "1.19"
1313
log = "0.4"
1414
memmap2 = "0.5"
1515
rand = "0.8"

src/agent/onefuzz-task/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ strum = "0.24"
4343
strum_macros = "0.24"
4444
stacktrace-parser = { path = "../stacktrace-parser" }
4545
storage-queue = { path = "../storage-queue" }
46-
tempfile = "3.5.0"
46+
tempfile = "3.6.0"
4747
thiserror = "1.0"
4848
tokio = { version = "1.28", features = ["full"] }
4949
tokio-util = { version = "0.7", features = ["full"] }

src/agent/onefuzz-telemetry/src/lib.rs

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use chrono::DateTime;
55
use serde::{Deserialize, Serialize};
66
use std::fmt;
77
use std::sync::{LockResult, RwLockReadGuard, RwLockWriteGuard};
8+
use std::time::Duration;
89
use uuid::Uuid;
910

1011
pub use chrono::Utc;
@@ -15,6 +16,8 @@ use tokio::sync::broadcast::{self, Receiver};
1516
#[macro_use]
1617
extern crate lazy_static;
1718

19+
const DEAFAULT_CHANNEL_CLOSING_TIMEOUT: Duration = Duration::from_secs(30);
20+
1821
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
1922
#[serde(transparent)]
2023
pub struct MicrosoftTelemetryKey(Uuid);
@@ -355,18 +358,22 @@ pub async fn set_appinsights_clients(
355358
global::set_clients(instance_client, microsoft_client);
356359
}
357360

361+
pub async fn try_flush_and_close() {
362+
_try_flush_and_close(DEAFAULT_CHANNEL_CLOSING_TIMEOUT).await
363+
}
364+
358365
/// Try to submit any pending telemetry with a blocking call.
359366
///
360367
/// Meant for a final attempt at flushing pending items before an abnormal exit.
361368
/// After calling this function, any existing telemetry client will be dropped,
362369
/// and subsequent telemetry submission will be a silent no-op.
363-
pub async fn try_flush_and_close() {
370+
pub async fn _try_flush_and_close(timeout: Duration) {
364371
let clients = global::take_clients();
365-
366372
for client in clients {
367-
client.close_channel().await;
373+
if let Err(e) = tokio::time::timeout(timeout, client.close_channel()).await {
374+
log::warn!("Failed to close telemetry client: {}", e);
375+
}
368376
}
369-
370377
// dropping the broadcast sender to make sure all pending events are sent
371378
let _global_event_source = global::EVENT_SOURCE.write().unwrap().take();
372379
}
@@ -468,11 +475,15 @@ pub fn try_broadcast_trace(timestamp: DateTime<Utc>, msg: String, level: log::Le
468475
}
469476

470477
pub fn subscribe_to_events() -> Result<Receiver<LoggingEvent>> {
471-
let global_event_source = global::EVENT_SOURCE.read().unwrap();
472-
if let Some(evs) = global_event_source.clone() {
473-
Ok(evs.subscribe())
474-
} else {
475-
bail!("Event source not initialized");
478+
match global::EVENT_SOURCE.read() {
479+
Ok(global_event_source) => {
480+
if let Some(evs) = global_event_source.clone() {
481+
Ok(evs.subscribe())
482+
} else {
483+
bail!("Event source not initialized");
484+
}
485+
}
486+
Err(e) => bail!("failed to acquire event source lock: {}", e),
476487
}
477488
}
478489

src/agent/onefuzz/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ url-escape = "0.1.0"
4040
storage-queue = { path = "../storage-queue" }
4141
strum = "0.24"
4242
strum_macros = "0.24"
43-
tempfile = "3.5.0"
43+
tempfile = "3.6.0"
4444
process_control = "4.0"
4545
reqwest-retry = { path = "../reqwest-retry" }
4646
onefuzz-telemetry = { path = "../onefuzz-telemetry" }

0 commit comments

Comments
 (0)