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