#![allow(unsafe_code)]
use super::{ProcessProtection, ProtectionError};
pub struct MacosProcessProtection;
const PT_DENY_ATTACH: libc::c_int = 31;
fn last_errno() -> i32 {
unsafe { *libc::__error() }
}
impl ProcessProtection for MacosProcessProtection {
fn lock_memory(&self) -> Result<(), ProtectionError> {
Err(ProtectionError::Unsupported(
"macOS mlockall unavailable — use per-region mlock / Secure Enclave",
))
}
fn disable_core_dump(&self) -> Result<(), ProtectionError> {
let rlim = libc::rlimit {
rlim_cur: 0,
rlim_max: 0,
};
let rc = unsafe { libc::setrlimit(libc::RLIMIT_CORE, &rlim) };
if rc == 0 {
Ok(())
} else {
Err(ProtectionError::SyscallFailed {
op: "setrlimit(RLIMIT_CORE)",
code: last_errno(),
})
}
}
fn disable_ptrace(&self) -> Result<(), ProtectionError> {
let rc = unsafe { libc::ptrace(PT_DENY_ATTACH, 0, std::ptr::null_mut(), 0) };
if rc == 0 {
Ok(())
} else {
Err(ProtectionError::SyscallFailed {
op: "ptrace(PT_DENY_ATTACH)",
code: last_errno(),
})
}
}
}
#[cfg(test)]
#[allow(clippy::panic, clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn lock_memory_is_unsupported() {
let proto = MacosProcessProtection;
assert!(
matches!(proto.lock_memory(), Err(ProtectionError::Unsupported(_))),
"lock_memory on macOS must be Unsupported"
);
}
#[test]
fn disable_core_dump_either_succeeds_or_reports_errno() {
let proto = MacosProcessProtection;
match proto.disable_core_dump() {
Ok(()) => {}
Err(ProtectionError::SyscallFailed { op, code }) => {
assert_eq!(op, "setrlimit(RLIMIT_CORE)");
assert!(code != 0);
}
Err(other) => panic!("unexpected error variant: {other:?}"),
}
}
#[test]
fn apply_all_propagates_lock_memory_unsupported() {
let proto = MacosProcessProtection;
assert!(
matches!(proto.apply_all(), Err(ProtectionError::Unsupported(_))),
"apply_all must surface lock_memory Unsupported"
);
}
}