libdd_common/unix_utils/
fork.rs1#[cfg(target_os = "macos")]
5pub fn alt_fork() -> i32 {
6 unsafe { libc::fork() }
11}
12
13#[cfg(target_os = "linux")]
14use std::fs::File;
15#[cfg(target_os = "linux")]
16use std::io::{self, Read};
17
18#[cfg(target_os = "linux")]
19fn is_being_traced() -> io::Result<bool> {
20 let file = File::open("/proc/self/status")?;
26 is_being_traced_internal(file)
27}
28
29#[cfg(target_os = "linux")]
30const BUFFER_SIZE: usize = 1024;
31
32#[cfg(target_os = "linux")]
33fn is_being_traced_internal(mut file: File) -> io::Result<bool> {
34 const TRACER_PID_MARKER: &[u8] = b"TracerPid:";
35 let mut buffer = [0u8; BUFFER_SIZE];
36 let mut data_len = 0;
37 let mut offset = 0;
38
39 loop {
40 if offset > 0 && offset < data_len {
42 let leftover_len = data_len - offset;
43 buffer.copy_within(offset..data_len, 0);
44 data_len = leftover_len;
45 offset = 0;
46
47 } else if offset == data_len || (offset == 0 && data_len == BUFFER_SIZE) {
50 data_len = 0;
51 offset = 0;
52 }
53
54 let bytes_read = file.read(&mut buffer[data_len..])?;
55 if bytes_read == 0 {
56 break;
58 }
59 data_len += bytes_read;
60
61 while let Some(newline_pos) = buffer[offset..data_len].iter().position(|&b| b == b'\n') {
63 let line_end = offset + newline_pos;
64
65 if let Some(result) =
66 check_tracer_pid_line(&buffer[offset..line_end], TRACER_PID_MARKER)?
67 {
68 return Ok(result);
69 }
70
71 offset = line_end + 1;
72 }
73 }
74
75 if offset < data_len {
77 if let Some(result) = check_tracer_pid_line(&buffer[offset..data_len], TRACER_PID_MARKER)? {
78 return Ok(result);
79 }
80 }
81 Ok(false)
82}
83
84#[cfg(target_os = "linux")]
85fn check_tracer_pid_line(line: &[u8], marker: &[u8]) -> io::Result<Option<bool>> {
86 if line.starts_with(marker) && line.len() > marker.len() {
87 if let Ok(line_str) = std::str::from_utf8(line) {
88 let tracer_pid = line_str.split_whitespace().nth(1).unwrap_or("0");
89 return Ok(Some(tracer_pid != "0"));
90 }
91 }
92 Ok(None)
93}
94
95#[cfg(target_os = "linux")]
96pub fn alt_fork() -> libc::pid_t {
97 use libc::{
98 c_ulong, c_void, pid_t, syscall, SYS_clone, CLONE_CHILD_CLEARTID, CLONE_CHILD_SETTID,
99 CLONE_PTRACE, SIGCHLD,
100 };
101
102 let mut _ptid: pid_t = 0;
103 let mut _ctid: pid_t = 0;
104
105 let being_traced = is_being_traced().unwrap_or(false);
107 let extra_flags = if being_traced { CLONE_PTRACE } else { 0 };
108
109 let res = unsafe {
114 syscall(
115 SYS_clone,
116 (CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID | SIGCHLD | extra_flags) as c_ulong,
117 std::ptr::null_mut::<c_void>(),
118 &mut _ptid as *mut pid_t,
119 &mut _ctid as *mut pid_t,
120 0 as c_ulong,
121 )
122 };
123
124 if (res as i64) > (pid_t::MAX as i64) {
126 pid_t::MAX
127 } else if (res as i64) < (pid_t::MIN as i64) {
128 pid_t::MIN
129 } else {
130 res as pid_t
131 }
132}
133
134#[cfg(target_os = "linux")]
135#[cfg(test)]
136mod tests {
137 use crate::unix_utils::fork::is_being_traced_internal;
138 use crate::unix_utils::fork::BUFFER_SIZE;
139 use std::fs::File;
140 use std::io::Seek;
141 use std::io::Write;
142
143 #[test]
144 fn test_is_being_traced_in_middle() {
145 let lines: &[&[u8]] = &[b"First:item\n", b"TracerPid: 2\n", b"Another: 21"];
146 let f = create_temp_file(lines);
147 assert!(is_being_traced_internal(f).unwrap_or(false))
148 }
149
150 #[test]
151 fn test_is_being_traced_at_the_end() {
152 let lines: &[&[u8]] = &[b"First:item\n", b"Another: 21\n", b"TracerPid: 2\n"];
153 let f = create_temp_file(lines);
154 assert!(is_being_traced_internal(f).unwrap_or(false))
155 }
156
157 #[test]
158 fn test_is_being_traced_at_the_end_with_no_newline_at_the_end() {
159 let lines: &[&[u8]] = &[b"First:item\n", b"Another: 21\n", b"TracerPid: 2"];
160 let f = create_temp_file(lines);
161 assert!(is_being_traced_internal(f).unwrap_or(false))
162 }
163
164 #[test]
165 fn test_is_being_traced_at_the_beginning() {
166 let lines: &[&[u8]] = &[b"TracerPid: 2\n", b"nFirst:item\n", b"Another: 21"];
167 let f = create_temp_file(lines);
168 assert!(is_being_traced_internal(f).unwrap_or(false))
169 }
170
171 #[test]
172 fn test_is_being_traced_with_first_string_larger_than_buffer() {
173 let large_string = "A".repeat(BUFFER_SIZE + 20);
175 let lines: &[&[u8]] = &[
176 large_string.as_bytes(),
177 b"\n",
178 b"Another:12\n",
179 b"TracerPid: 42\n",
180 ];
181 let f = create_temp_file(lines);
182 assert!(is_being_traced_internal(f).unwrap_or(false))
183 }
184
185 #[test]
186 fn test_is_being_traced_with_marker_in_between_buffer_read() {
187 let large_string = "A".repeat(BUFFER_SIZE - 4);
188 let lines: &[&[u8]] = &[
189 large_string.as_bytes(),
190 b"\n",
191 b"TracerPid: 42\n",
192 b"AnotherItem: 42\n",
193 ];
194 let f = create_temp_file(lines);
195 assert!(is_being_traced_internal(f).unwrap_or(false))
196 }
197
198 #[test]
199 fn test_is_being_traced_with_value_zero() {
200 let lines: &[&[u8]] = &[b"First:item\n", b"TracerPid: 0\n", b"AnotherItem: 21\n"];
201 let f = create_temp_file(lines);
202 assert!(!is_being_traced_internal(f).unwrap_or(true))
203 }
204
205 #[test]
206 fn test_is_being_traced_with_no_tracerpid() {
207 let lines: &[&[u8]] = &[b"First:item\n", b"AnotherItem: 21\n"];
208 let f = create_temp_file(lines);
209 assert!(!is_being_traced_internal(f).unwrap_or(true))
210 }
211
212 #[test]
213 fn test_is_being_traced_with_very_large_content_and_no_tracerpid() {
214 let large_string = "A".repeat(2 * BUFFER_SIZE + 20);
216 let lines: &[&[u8]] = &[large_string.as_bytes(), b"\n", b"Another:12\n"];
217 let f = create_temp_file(lines);
218 assert!(!is_being_traced_internal(f).unwrap_or(true))
219 }
220
221 fn create_temp_file(lines: &[&[u8]]) -> File {
222 let mut f = tempfile::tempfile().unwrap();
223 for line in lines {
224 f.write_all(line).unwrap();
225 }
226
227 f.flush().unwrap();
228 f.rewind().unwrap();
229
230 f
231 }
232}