Skip to main content

murk_cli/
hardening.rs

1//! Process hardening: best-effort defense-in-depth measures.
2
3/// Disable core dumps for this process.
4///
5/// Sets `RLIMIT_CORE` to 0 on Unix. A core file written after a crash while
6/// murk holds decrypted secret material is the worst possible leak — disabling
7/// it up front removes the failure mode regardless of system defaults, which
8/// vary across Linux distros, macOS, and BSDs.
9///
10/// Best-effort: a failed syscall is swallowed rather than blocking the
11/// command. Idempotent — a second call on a process that's already at zero is
12/// a no-op. On non-Unix targets this is a no-op.
13pub fn disable_core_dumps() {
14    #[cfg(unix)]
15    {
16        let limit = libc::rlimit {
17            rlim_cur: 0,
18            rlim_max: 0,
19        };
20        // SAFETY: setrlimit reads through the pointer for the duration of
21        // the call; `limit` lives for that scope. Return code is intentionally
22        // ignored — see the doc comment.
23        unsafe {
24            libc::setrlimit(libc::RLIMIT_CORE, &raw const limit);
25        }
26    }
27}
28
29#[cfg(all(test, unix))]
30mod tests {
31    use super::*;
32
33    #[test]
34    fn disables_core_dumps() {
35        disable_core_dumps();
36
37        let mut current = libc::rlimit {
38            rlim_cur: 1,
39            rlim_max: 1,
40        };
41        // SAFETY: getrlimit writes into `&mut current` for the duration of
42        // the call.
43        let rc = unsafe { libc::getrlimit(libc::RLIMIT_CORE, &raw mut current) };
44        assert_eq!(rc, 0, "getrlimit failed");
45        assert_eq!(current.rlim_cur, 0);
46        assert_eq!(current.rlim_max, 0);
47    }
48}