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}