Skip to content

Commit d633a18

Browse files
Expand harden_process: PR_SET_DUMPABLE=0, PR_SET_NO_NEW_PRIVS=1 on Linux (#61)
harden_process previously only called setrlimit(RLIMIT_CORE, 0). Two low-cost Linux additions close meaningful attack surface: - PR_SET_DUMPABLE=0: makes /proc/<pid>/mem readable only by root, not the same UID, and denies ptrace attach to non-root peers. The same-UID attacker who was the biggest residual threat against decrypted plaintext in process memory now loses their ptrace / /proc/mem handle. - PR_SET_NO_NEW_PRIVS=1: an exec*() after this point can't inherit setuid or file-capabilities privileges. The enclave apps never setuid, and the wrapped child processes they spawn (npm, git, awscli) don't benefit from it either — so this is free. Both are done via direct libc::prctl calls under #[allow(unsafe_code)] with tracing::warn!-on-failure semantics matching the existing setrlimit path. Linux-only (cfg(target_os = "linux")) — the prctl calls don't exist elsewhere. Still not applied: RLIMIT_AS, seccomp-bpf, macOS PT_DENY_ATTACH, Windows SetProcessMitigationPolicy. Threat-model section rewritten to describe the new posture and the remaining gaps. Co-authored-by: Jay Gowdy <jay@gowdy.me>
1 parent c935a50 commit d633a18

2 files changed

Lines changed: 58 additions & 4 deletions

File tree

THREAT_MODEL.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,13 @@ Unsafe FFI surfaces are trusted by design but fragile.
112112

113113
### Process hardening scope
114114

115-
`enclaveapp_core::process::harden_process()` (`crates/enclaveapp-core/src/process.rs`) currently only calls `disable_core_dumps` (`setrlimit(RLIMIT_CORE, 0)` on Unix; no-op on Windows). It does **not** apply `PR_SET_DUMPABLE=0`, `NO_NEW_PRIVS`, ptrace-blocking, `RLIMIT_AS`, or Windows `SetProcessMitigationPolicy`. A same-UID attacker can still `ptrace` into a running process and read decrypted plaintext in the window between hardware-decrypt and consumer handoff. Applications that want stricter memory protection must add their own mitigations on top.
115+
`enclaveapp_core::process::harden_process()` (`crates/enclaveapp-core/src/process.rs`) applies:
116+
117+
- **All Unix:** `setrlimit(RLIMIT_CORE, 0)` — no core dumps.
118+
- **Linux:** `prctl(PR_SET_DUMPABLE, 0)``/proc/<pid>/mem` becomes root-only; `ptrace` attach from non-root peers is denied by the kernel even within the same UID.
119+
- **Linux:** `prctl(PR_SET_NO_NEW_PRIVS, 1)` — subsequent `exec*()` cannot gain setuid / file-capabilities privileges; shrinks the surface for wrapped-child-process escalation.
120+
121+
Still not applied: `RLIMIT_AS`, seccomp-bpf system-call filtering, macOS `ptrace(PT_DENY_ATTACH)` (deprecated and fragile), and Windows `SetProcessMitigationPolicy`. Root can still dump memory unconditionally on any platform. Applications that want stricter memory protection must add their own mitigations on top.
116122

117123
### Zeroize coverage
118124

crates/enclaveapp-core/src/process.rs

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
11
//! Process-level security hardening.
22
//!
33
//! Call `harden_process()` early in `main()` to apply platform-appropriate
4-
//! protections: disable core dumps and lock secret memory pages.
4+
//! protections: disable core dumps, block ptrace-based memory reads on
5+
//! Linux, prevent privilege gain from setuid/fcaps (Linux), and lock
6+
//! secret memory pages.
57
68
/// Apply process-level security hardening.
79
///
8-
/// - Disables core dumps (prevents secrets from appearing in crash dumps)
9-
/// - Should be called early in main() before any secrets are loaded
10+
/// - Disables core dumps (prevents secrets from appearing in crash dumps).
11+
/// - On Linux, sets `PR_SET_DUMPABLE=0` so `/proc/<pid>/mem` is only
12+
/// readable by root, not the same user, and ptrace attach is denied
13+
/// to non-root peers.
14+
/// - On Linux, sets `PR_SET_NO_NEW_PRIVS=1` so any `exec*()` after this
15+
/// point cannot gain privileges via setuid / file capabilities.
1016
///
17+
/// Should be called early in main() before any secrets are loaded.
1118
/// Errors are logged but not fatal — hardening is best-effort.
1219
pub fn harden_process() {
1320
disable_core_dumps();
21+
#[cfg(target_os = "linux")]
22+
{
23+
set_dumpable_zero();
24+
set_no_new_privs();
25+
}
1426
}
1527

1628
/// Disable core dumps to prevent secrets from appearing in crash dumps.
@@ -39,6 +51,42 @@ fn disable_core_dumps() {
3951
// WER (Windows Error Reporting) settings control this globally.
4052
}
4153

54+
/// Linux: set `PR_SET_DUMPABLE = 0`.
55+
///
56+
/// Prevents ptrace attach from non-root peers and makes `/proc/<pid>/mem`
57+
/// readable only by root. This closes the main in-memory attack vector
58+
/// against a running enclave-app process from a same-UID attacker.
59+
#[cfg(target_os = "linux")]
60+
#[allow(unsafe_code)]
61+
fn set_dumpable_zero() {
62+
let result = unsafe { libc::prctl(libc::PR_SET_DUMPABLE, 0_u64, 0, 0, 0) };
63+
if result != 0 {
64+
tracing::warn!(
65+
"failed to set PR_SET_DUMPABLE=0: {}",
66+
std::io::Error::last_os_error()
67+
);
68+
}
69+
}
70+
71+
/// Linux: set `PR_SET_NO_NEW_PRIVS = 1`.
72+
///
73+
/// Once set, subsequent `exec*()` calls cannot grant the child new
74+
/// privileges via setuid bits or file capabilities. The enclave apps
75+
/// do not rely on setuid execution, so the restriction is safe and
76+
/// shrinks the post-exec privilege-escalation surface for any child
77+
/// processes the app spawns (e.g. wrapped `npm`, `git`, `awscli`).
78+
#[cfg(target_os = "linux")]
79+
#[allow(unsafe_code)]
80+
fn set_no_new_privs() {
81+
let result = unsafe { libc::prctl(libc::PR_SET_NO_NEW_PRIVS, 1_u64, 0, 0, 0) };
82+
if result != 0 {
83+
tracing::warn!(
84+
"failed to set PR_SET_NO_NEW_PRIVS=1: {}",
85+
std::io::Error::last_os_error()
86+
);
87+
}
88+
}
89+
4290
/// Lock a memory region to prevent it from being paged to swap.
4391
///
4492
/// Call this on buffers containing secrets. The memory will remain in RAM

0 commit comments

Comments
 (0)