vtcode_process_hardening/
lib.rs1#[cfg(unix)]
2use std::ffi::OsString;
3#[cfg(unix)]
4use std::os::unix::ffi::OsStrExt;
5
6pub fn pre_main_hardening() {
12 #[cfg(any(target_os = "linux", target_os = "android"))]
13 pre_main_hardening_linux();
14
15 #[cfg(target_os = "macos")]
16 pre_main_hardening_macos();
17
18 #[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
19 pre_main_hardening_bsd();
20
21 #[cfg(windows)]
22 pre_main_hardening_windows();
23}
24
25#[cfg(any(target_os = "linux", target_os = "android"))]
26const PRCTL_FAILED_EXIT_CODE: i32 = 5;
27
28#[cfg(target_os = "macos")]
29const PTRACE_DENY_ATTACH_FAILED_EXIT_CODE: i32 = 6;
30
31#[cfg(any(
32 target_os = "linux",
33 target_os = "android",
34 target_os = "macos",
35 target_os = "freebsd",
36 target_os = "netbsd",
37 target_os = "openbsd"
38))]
39const SET_RLIMIT_CORE_FAILED_EXIT_CODE: i32 = 7;
40
41#[cfg(any(target_os = "linux", target_os = "android"))]
42pub(crate) fn pre_main_hardening_linux() {
43 let ret_code = unsafe { libc::prctl(libc::PR_SET_DUMPABLE, 0, 0, 0, 0) };
45 if ret_code != 0 {
46 eprintln!(
47 "ERROR: prctl(PR_SET_DUMPABLE, 0) failed: {}",
48 std::io::Error::last_os_error()
49 );
50 std::process::exit(PRCTL_FAILED_EXIT_CODE);
51 }
52
53 set_core_file_size_limit_to_zero();
55
56 let ld_keys = env_keys_with_prefix(std::env::vars_os(), b"LD_");
59 for key in ld_keys {
60 unsafe {
61 std::env::remove_var(key);
62 }
63 }
64}
65
66#[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
67pub(crate) fn pre_main_hardening_bsd() {
68 set_core_file_size_limit_to_zero();
70
71 let ld_keys = env_keys_with_prefix(std::env::vars_os(), b"LD_");
72 for key in ld_keys {
73 unsafe {
74 std::env::remove_var(key);
75 }
76 }
77}
78
79#[cfg(target_os = "macos")]
80pub(crate) fn pre_main_hardening_macos() {
81 let ret_code = unsafe { libc::ptrace(libc::PT_DENY_ATTACH, 0, std::ptr::null_mut(), 0) };
83 if ret_code == -1 {
84 eprintln!(
85 "ERROR: ptrace(PT_DENY_ATTACH) failed: {}",
86 std::io::Error::last_os_error()
87 );
88 std::process::exit(PTRACE_DENY_ATTACH_FAILED_EXIT_CODE);
89 }
90
91 set_core_file_size_limit_to_zero();
93
94 let dyld_keys = env_keys_with_prefix(std::env::vars_os(), b"DYLD_");
97 for key in dyld_keys {
98 unsafe {
99 std::env::remove_var(key);
100 }
101 }
102}
103
104#[cfg(unix)]
105fn set_core_file_size_limit_to_zero() {
106 let rlim = libc::rlimit {
107 rlim_cur: 0,
108 rlim_max: 0,
109 };
110 let ret_code = unsafe { libc::setrlimit(libc::RLIMIT_CORE, &rlim) };
111 if ret_code != 0 {
112 eprintln!(
113 "ERROR: setrlimit(RLIMIT_CORE) failed: {}",
114 std::io::Error::last_os_error()
115 );
116 std::process::exit(SET_RLIMIT_CORE_FAILED_EXIT_CODE);
117 }
118}
119
120#[cfg(windows)]
121pub(crate) fn pre_main_hardening_windows() {
122 }
126
127#[cfg(unix)]
128fn env_keys_with_prefix<I>(vars: I, prefix: &[u8]) -> Vec<OsString>
129where
130 I: IntoIterator<Item = (OsString, OsString)>,
131{
132 vars.into_iter()
133 .filter_map(|(key, _)| {
134 key.as_os_str()
135 .as_bytes()
136 .starts_with(prefix)
137 .then_some(key)
138 })
139 .collect()
140}
141
142#[cfg(all(test, unix))]
143mod tests {
144 use super::*;
145 use pretty_assertions::assert_eq;
146 use std::ffi::OsStr;
147 use std::os::unix::ffi::OsStrExt;
148 use std::os::unix::ffi::OsStringExt;
149
150 #[test]
151 fn env_keys_with_prefix_handles_non_utf8_entries() {
152 let non_utf8_key1 = OsStr::from_bytes(b"R\xD6DBURK").to_os_string();
154 assert!(non_utf8_key1.clone().into_string().is_err());
155
156 let non_utf8_key2 = OsString::from_vec(vec![b'L', b'D', b'_', 0xF0]);
157 assert!(non_utf8_key2.clone().into_string().is_err());
158
159 let non_utf8_value = OsString::from_vec(vec![0xF0, 0x9F, 0x92, 0xA9]);
160
161 let keys = env_keys_with_prefix(
162 vec![
163 (non_utf8_key1, non_utf8_value.clone()),
164 (non_utf8_key2.clone(), non_utf8_value),
165 ],
166 b"LD_",
167 );
168
169 assert_eq!(
170 keys,
171 vec![non_utf8_key2],
172 "non-UTF-8 env entries with LD_ prefix should be retained"
173 );
174 }
175
176 #[test]
177 fn env_keys_with_prefix_filters_only_matching_keys() {
178 let ld_test_var = OsStr::from_bytes(b"LD_TEST");
179 let vars = vec![
180 (OsString::from("PATH"), OsString::from("/usr/bin")),
181 (ld_test_var.to_os_string(), OsString::from("1")),
182 (OsString::from("DYLD_FOO"), OsString::from("bar")),
183 ];
184
185 let keys = env_keys_with_prefix(vars, b"LD_");
186 assert_eq!(keys.len(), 1);
187 assert_eq!(keys[0].as_os_str(), ld_test_var);
188 }
189}