underhill_core/
livedump.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use std::process::Stdio;
5
6pub(crate) async fn livedump() {
7    // If a livedump fails we don't want to panic, just log the error.
8    match livedump_core().await {
9        Err(e) => tracing::error!(?e, "livedump failed"),
10        Ok(()) => tracing::info!("livedump succeeded"),
11    }
12}
13
14async fn livedump_core() -> anyhow::Result<()> {
15    if underhill_confidentiality::confidential_filtering_enabled() {
16        tracing::info!("livedump disabled due to CVM");
17        return Ok(());
18    }
19
20    let (dump_read, dump_write) = pal::unix::pipe::pair()?;
21
22    // Spawn underhill-crash to forward the crash dump to the host.
23    // Give it what arguments we can, but as this is a live dump they're not quite as relevant.
24    let crash_proc = std::process::Command::new("underhill-crash")
25        .stdin(dump_read)
26        .stdout(Stdio::piped())
27        .stderr(Stdio::null())
28        // Don't redirect output so we can capture it.
29        .env("UNDERHILL_CRASH_NO_REDIRECT", "1")
30        // Don't include kmsg, since underhill-dump will do that.
31        .env("UNDERHILL_CRASH_NO_KMSG", "1")
32        .arg(std::process::id().to_string()) // pid
33        .arg(0.to_string()) // tid
34        .arg(0.to_string()) // sig
35        .arg(std::env::current_exe().unwrap_or_default()) // comm
36        .spawn()?;
37
38    // Spawn underhill-dump to create the dump.
39    // This needs to be done after underhill-crash, as underhill-dump will pause us.
40    let dump_result = std::process::Command::new("underhill-dump")
41        .arg(format!("{}", std::process::id()))
42        .stdin(Stdio::null())
43        .stdout(dump_write)
44        .stderr(Stdio::piped())
45        .output()?;
46
47    // underhill-dump should finish first, as it's the producer.
48    let crash_result = crash_proc.wait_with_output()?;
49
50    // Check for errors. If both failed log both outputs, as one crashing may cause the other to fail.
51    if !dump_result.status.success() {
52        let dump_output = String::from_utf8_lossy(&dump_result.stderr);
53        for line in dump_output.lines() {
54            tracing::info!("underhill-dump output: {}", line);
55        }
56    }
57
58    if !crash_result.status.success() {
59        let crash_output = String::from_utf8_lossy(&crash_result.stdout);
60        for line in crash_output.lines() {
61            tracing::info!("underhill-crash output: {}", line);
62        }
63        anyhow::bail!("underhill-crash failed: {}", crash_result.status);
64    }
65
66    if !dump_result.status.success() {
67        anyhow::bail!("underhill-dump failed: {}", dump_result.status);
68    }
69
70    Ok(())
71}