xref: /relibc/src/platform/redox/ptrace.rs (revision 041d1604b526a0b908d4222e39baa0030a94fd8f)
1 //! Note: This module is not going to be clean. We're not going to be
2 //! able to follow the specs 100%. Linux ptrace is very, very,
3 //! different to Redox. Many people agree that Linux ptrace is bad, so
4 //! we are NOT going to bend our API for the sake of
5 //! compatibility. So, this module will be a hellhole.
6 
7 use super::super::{errno, types::*, Pal, PalPtrace, PalSignal, Sys};
8 #[cfg(target_arch = "aarch64")]
9 use crate::header::arch_aarch64_user::user_regs_struct;
10 #[cfg(target_arch = "x86_64")]
11 use crate::header::arch_x64_user::user_regs_struct;
12 use crate::{
13     c_str::CString,
14     fs::File,
15     header::{errno as errnoh, fcntl, signal, sys_ptrace},
16     io::{self, prelude::*},
17     sync::{Mutex, Once},
18 };
19 
20 use alloc::collections::{btree_map::Entry, BTreeMap};
21 use core::mem;
22 use syscall;
23 
24 pub struct Session {
25     pub first: bool,
26     pub fpregs: File,
27     pub mem: File,
28     pub regs: File,
29     pub tracer: File,
30 }
31 pub struct State {
32     pub sessions: Mutex<BTreeMap<pid_t, Session>>,
33 }
34 impl State {
35     fn new() -> Self {
36         Self {
37             sessions: Mutex::new(BTreeMap::new()),
38         }
39     }
40 }
41 
42 #[thread_local]
43 static mut STATE: Option<State> = None;
44 
45 pub fn init_state() -> &'static State {
46     // Safe due to STATE being thread_local
47     unsafe {
48         if STATE.is_none() {
49             STATE = Some(State::new())
50         }
51         STATE.as_ref().unwrap()
52     }
53 }
54 pub fn is_traceme(pid: pid_t) -> bool {
55     // Skip special PIDs (<=0)
56     if pid <= 0 {
57         return false;
58     }
59     File::open(
60         &CString::new(format!("chan:ptrace-relibc/{}/traceme", pid)).unwrap(),
61         fcntl::O_PATH,
62     )
63     .is_ok()
64 }
65 pub fn get_session(
66     sessions: &mut BTreeMap<pid_t, Session>,
67     pid: pid_t,
68 ) -> io::Result<&mut Session> {
69     const NEW_FLAGS: c_int = fcntl::O_RDWR | fcntl::O_CLOEXEC;
70 
71     match sessions.entry(pid) {
72         Entry::Vacant(entry) => {
73             if is_traceme(pid) {
74                 Ok(entry.insert(Session {
75                     first: true,
76                     tracer: File::open(
77                         &CString::new(format!("proc:{}/trace", pid)).unwrap(),
78                         NEW_FLAGS,
79                     )?,
80                     mem: File::open(
81                         &CString::new(format!("proc:{}/mem", pid)).unwrap(),
82                         NEW_FLAGS,
83                     )?,
84                     regs: File::open(
85                         &CString::new(format!("proc:{}/regs/int", pid)).unwrap(),
86                         NEW_FLAGS,
87                     )?,
88                     fpregs: File::open(
89                         &CString::new(format!("proc:{}/regs/float", pid)).unwrap(),
90                         NEW_FLAGS,
91                     )?,
92                 }))
93             } else {
94                 unsafe {
95                     errno = errnoh::ESRCH;
96                 }
97                 Err(io::last_os_error())
98             }
99         }
100         Entry::Occupied(entry) => Ok(entry.into_mut()),
101     }
102 }
103 
104 #[cfg(target_arch = "aarch64")]
105 fn inner_ptrace(
106     request: c_int,
107     pid: pid_t,
108     addr: *mut c_void,
109     data: *mut c_void,
110 ) -> io::Result<c_int> {
111     //TODO: aarch64
112     unimplemented!("inner_ptrace not implemented on aarch64");
113 }
114 
115 #[cfg(target_arch = "x86")]
116 fn inner_ptrace(
117     request: c_int,
118     pid: pid_t,
119     addr: *mut c_void,
120     data: *mut c_void,
121 ) -> io::Result<c_int> {
122     //TODO: x86
123     unimplemented!("inner_ptrace not implemented on x86");
124 }
125 
126 #[cfg(target_arch = "x86_64")]
127 fn inner_ptrace(
128     request: c_int,
129     pid: pid_t,
130     addr: *mut c_void,
131     data: *mut c_void,
132 ) -> io::Result<c_int> {
133     let state = init_state();
134 
135     if request == sys_ptrace::PTRACE_TRACEME {
136         // Mark this child as traced, parent will check for this marker file
137         let pid = Sys::getpid();
138         mem::forget(File::open(
139             &CString::new(format!("chan:ptrace-relibc/{}/traceme", pid)).unwrap(),
140             fcntl::O_CREAT | fcntl::O_PATH | fcntl::O_EXCL,
141         )?);
142         return Ok(0);
143     }
144 
145     let mut sessions = state.sessions.lock();
146     let session = get_session(&mut sessions, pid)?;
147 
148     match request {
149         sys_ptrace::PTRACE_CONT
150         | sys_ptrace::PTRACE_SINGLESTEP
151         | sys_ptrace::PTRACE_SYSCALL
152         | sys_ptrace::PTRACE_SYSEMU
153         | sys_ptrace::PTRACE_SYSEMU_SINGLESTEP => {
154             Sys::kill(pid, signal::SIGCONT as _);
155 
156             // TODO: Translate errors
157             let syscall = syscall::PTRACE_STOP_PRE_SYSCALL | syscall::PTRACE_STOP_POST_SYSCALL;
158             (&mut &session.tracer).write(&match request {
159                 sys_ptrace::PTRACE_CONT => syscall::PtraceFlags::empty(),
160                 sys_ptrace::PTRACE_SINGLESTEP => syscall::PTRACE_STOP_SINGLESTEP,
161                 // Skip the first post-syscall when connected
162                 sys_ptrace::PTRACE_SYSCALL if session.first => syscall::PTRACE_STOP_PRE_SYSCALL,
163                 sys_ptrace::PTRACE_SYSCALL => syscall,
164                 // Skip the first post-syscall when connected
165                 sys_ptrace::PTRACE_SYSEMU if session.first => {
166                     syscall::PTRACE_FLAG_IGNORE | syscall::PTRACE_STOP_PRE_SYSCALL
167                 }
168                 sys_ptrace::PTRACE_SYSEMU => syscall::PTRACE_FLAG_IGNORE | syscall,
169                 sys_ptrace::PTRACE_SYSEMU_SINGLESTEP => {
170                     syscall::PTRACE_FLAG_IGNORE | syscall::PTRACE_STOP_SINGLESTEP
171                 }
172                 _ => unreachable!("unhandled ptrace request type {}", request),
173             })?;
174 
175             session.first = false;
176             Ok(0)
177         }
178         sys_ptrace::PTRACE_GETREGS => {
179             let c_regs = unsafe { &mut *(data as *mut user_regs_struct) };
180             let mut redox_regs = syscall::IntRegisters::default();
181             (&mut &session.regs).read(&mut redox_regs)?;
182             *c_regs = user_regs_struct {
183                 r15: redox_regs.r15 as _,
184                 r14: redox_regs.r14 as _,
185                 r13: redox_regs.r13 as _,
186                 r12: redox_regs.r12 as _,
187                 rbp: redox_regs.rbp as _,
188                 rbx: redox_regs.rbx as _,
189                 r11: redox_regs.r11 as _,
190                 r10: redox_regs.r10 as _,
191                 r9: redox_regs.r9 as _,
192                 r8: redox_regs.r8 as _,
193                 rax: redox_regs.rax as _,
194                 rcx: redox_regs.rcx as _,
195                 rdx: redox_regs.rdx as _,
196                 rsi: redox_regs.rsi as _,
197                 rdi: redox_regs.rdi as _,
198                 orig_rax: redox_regs.rax as _, // redox_regs.orig_rax as _,
199                 rip: redox_regs.rip as _,
200                 cs: redox_regs.cs as _,
201                 eflags: redox_regs.rflags as _,
202                 rsp: redox_regs.rsp as _,
203                 ss: redox_regs.ss as _,
204                 fs_base: 0, // fs_base: redox_regs.fs_base as _,
205                 gs_base: 0, // gs_base: redox_regs.gs_base as _,
206                 ds: 0,      // ds: redox_regs.ds as _,
207                 es: 0,      // es: redox_regs.es as _,
208                 fs: redox_regs.fs as _,
209                 gs: 0, // gs: redox_regs.gs as _,
210             };
211             Ok(0)
212         }
213         sys_ptrace::PTRACE_SETREGS => {
214             let c_regs = unsafe { &*(data as *mut user_regs_struct) };
215             let redox_regs = syscall::IntRegisters {
216                 r15: c_regs.r15 as _,
217                 r14: c_regs.r14 as _,
218                 r13: c_regs.r13 as _,
219                 r12: c_regs.r12 as _,
220                 rbp: c_regs.rbp as _,
221                 rbx: c_regs.rbx as _,
222                 r11: c_regs.r11 as _,
223                 r10: c_regs.r10 as _,
224                 r9: c_regs.r9 as _,
225                 r8: c_regs.r8 as _,
226                 rax: c_regs.orig_rax as _, // c_regs.rax as _,
227                 rcx: c_regs.rcx as _,
228                 rdx: c_regs.rdx as _,
229                 rsi: c_regs.rsi as _,
230                 rdi: c_regs.rdi as _,
231                 // orig_rax: c_regs.orig_rax as _,
232                 rip: c_regs.rip as _,
233                 cs: c_regs.cs as _,
234                 rflags: c_regs.eflags as _,
235                 rsp: c_regs.rsp as _,
236                 ss: c_regs.ss as _,
237                 // fs_base: c_regs.fs_base as _,
238                 // gs_base: c_regs.gs_base as _,
239                 // ds: c_regs.ds as _,
240                 // es: c_regs.es as _,
241                 fs: c_regs.fs as _,
242                 // gs: c_regs.gs as _,
243             };
244             (&mut &session.regs).write(&redox_regs)?;
245             Ok(0)
246         }
247         _ => unimplemented!(),
248     }
249 }
250 
251 impl PalPtrace for Sys {
252     fn ptrace(request: c_int, pid: pid_t, addr: *mut c_void, data: *mut c_void) -> c_int {
253         inner_ptrace(request, pid, addr, data).unwrap_or(-1)
254     }
255 }
256