Skip to main content

hanzo_process_hardening/
lib.rs

1/// This is designed to be called pre-main() (using `#[ctor::ctor]`) to perform
2/// various process hardening steps, such as
3/// - disabling core dumps
4/// - disabling ptrace attach on Linux and macOS.
5/// - removing dangerous environment variables such as LD_PRELOAD and DYLD_*
6pub fn pre_main_hardening() {
7    #[cfg(any(target_os = "linux", target_os = "android"))]
8    pre_main_hardening_linux();
9
10    #[cfg(target_os = "macos")]
11    pre_main_hardening_macos();
12
13    #[cfg(windows)]
14    pre_main_hardening_windows();
15}
16
17#[cfg(any(target_os = "linux", target_os = "android"))]
18const PRCTL_FAILED_EXIT_CODE: i32 = 5;
19
20#[cfg(target_os = "macos")]
21const PTRACE_DENY_ATTACH_FAILED_EXIT_CODE: i32 = 6;
22
23#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))]
24const SET_RLIMIT_CORE_FAILED_EXIT_CODE: i32 = 7;
25
26#[cfg(any(target_os = "linux", target_os = "android"))]
27pub(crate) fn pre_main_hardening_linux() {
28    // Disable ptrace attach / mark process non-dumpable.
29    let ret_code = unsafe { libc::prctl(libc::PR_SET_DUMPABLE, 0, 0, 0, 0) };
30    if ret_code != 0 {
31        eprintln!(
32            "ERROR: prctl(PR_SET_DUMPABLE, 0) failed: {}",
33            std::io::Error::last_os_error()
34        );
35        std::process::exit(PRCTL_FAILED_EXIT_CODE);
36    }
37
38    // For "defense in depth," set the core file size limit to 0.
39    set_core_file_size_limit_to_zero();
40
41    // Official Codex releases are MUSL-linked, which means that variables such
42    // as LD_PRELOAD are ignored anyway, but just to be sure, clear them here.
43    let ld_keys: Vec<String> = std::env::vars()
44        .filter_map(|(key, _)| {
45            if key.starts_with("LD_") {
46                Some(key)
47            } else {
48                None
49            }
50        })
51        .collect();
52
53    for key in ld_keys {
54        unsafe {
55            std::env::remove_var(key);
56        }
57    }
58}
59
60#[cfg(target_os = "macos")]
61pub(crate) fn pre_main_hardening_macos() {
62    // Prevent debuggers from attaching to this process.
63    let ret_code = unsafe { libc::ptrace(libc::PT_DENY_ATTACH, 0, std::ptr::null_mut(), 0) };
64    if ret_code == -1 {
65        eprintln!(
66            "ERROR: ptrace(PT_DENY_ATTACH) failed: {}",
67            std::io::Error::last_os_error()
68        );
69        std::process::exit(PTRACE_DENY_ATTACH_FAILED_EXIT_CODE);
70    }
71
72    // Set the core file size limit to 0 to prevent core dumps.
73    set_core_file_size_limit_to_zero();
74
75    // Remove all DYLD_ environment variables, which can be used to subvert
76    // library loading.
77    let dyld_keys: Vec<String> = std::env::vars()
78        .filter_map(|(key, _)| {
79            if key.starts_with("DYLD_") {
80                Some(key)
81            } else {
82                None
83            }
84        })
85        .collect();
86
87    for key in dyld_keys {
88        unsafe {
89            std::env::remove_var(key);
90        }
91    }
92}
93
94#[cfg(unix)]
95fn set_core_file_size_limit_to_zero() {
96    let rlim = libc::rlimit {
97        rlim_cur: 0,
98        rlim_max: 0,
99    };
100
101    let ret_code = unsafe { libc::setrlimit(libc::RLIMIT_CORE, &rlim) };
102    if ret_code != 0 {
103        eprintln!(
104            "ERROR: setrlimit(RLIMIT_CORE) failed: {}",
105            std::io::Error::last_os_error()
106        );
107        std::process::exit(SET_RLIMIT_CORE_FAILED_EXIT_CODE);
108    }
109}
110
111#[cfg(windows)]
112pub(crate) fn pre_main_hardening_windows() {
113    // TODO(mbolin): Perform the appropriate configuration for Windows.
114}