diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index 7d2e0e92634512119cbce64bc6460275df414421..984890cc1b9b083e4a27e41fc7ce89ebeaa6e775 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -925,22 +925,38 @@ impl Pal for Sys { let mut status = 0; // First, allow ptrace to handle waitpid + // TODO: Handle special PIDs here (such as -1) let state = ptrace::init_state(); - let sessions = state.sessions.lock(); - if let Some(session) = sessions.get(&pid) { + let mut sessions = state.sessions.lock(); + if let Ok(session) = ptrace::get_session(&mut sessions, pid) { if options & sys_wait::WNOHANG != sys_wait::WNOHANG { let _ = (&mut &session.tracer).write(&[syscall::PTRACE_WAIT]); - res = Some(e(syscall::waitpid(pid as usize, &mut status, (options | sys_wait::WNOHANG) as usize))); + res = Some(e(syscall::waitpid( + pid as usize, &mut status, + (options | sys_wait::WNOHANG | sys_wait::WUNTRACED) as usize + ))); if res == Some(0) { // WNOHANG, just pretend ptrace SIGSTOP:ped this status = (syscall::SIGSTOP << 8) | 0x7f; + assert!(syscall::wifstopped(status)); + assert_eq!(syscall::wstopsig(status), syscall::SIGSTOP); res = Some(pid as usize); } } } - // If ptrace didn't impact this waitpid, proceed as normal - let res = res.unwrap_or_else(|| e(syscall::waitpid(pid as usize, &mut status, options as usize))); + // If ptrace didn't impact this waitpid, proceed *almost* as + // normal: We still need to add WUNTRACED, but we only return + // it if (and only if) a ptrace traceme was activated during + // the wait. + let res = res.unwrap_or_else(|| loop { + let res = e(syscall::waitpid(pid as usize, &mut status, (options | sys_wait::WUNTRACED) as usize)); + + // TODO: Also handle special PIDs here + if !syscall::wifstopped(res) || ptrace::is_traceme(pid) { + break res; + } + }); // If stat_loc is non-null, set that and the return unsafe { diff --git a/src/platform/redox/ptrace.rs b/src/platform/redox/ptrace.rs index da82cd76f332aa4f99ff667bc5a4a6b053c27969..e80a00bce879aa3bdd4a7b35539327bcd332ff1e 100644 --- a/src/platform/redox/ptrace.rs +++ b/src/platform/redox/ptrace.rs @@ -14,6 +14,7 @@ use crate::io::{self, prelude::*}; use crate::sync::{Mutex, Once}; use alloc::collections::BTreeMap; use alloc::collections::btree_map::Entry; +use core::mem; use syscall; pub struct Session { @@ -38,34 +39,51 @@ static STATE: Once<State> = Once::new(); pub fn init_state() -> &'static State { STATE.call_once(|| State::new()) } +pub fn is_traceme(pid: pid_t) -> bool { + File::open(&CString::new(format!("chan:ptrace-relibc/{}/traceme", pid)).unwrap(), fcntl::O_PATH).is_ok() +} +pub fn get_session(sessions: &mut BTreeMap<pid_t, Session>, pid: pid_t) -> io::Result<&Session> { + const NEW_FLAGS: c_int = fcntl::O_RDWR | fcntl::O_CLOEXEC; + + match sessions.entry(pid) { + Entry::Vacant(entry) => if is_traceme(pid) { + Ok(entry.insert(Session { + tracer: File::open(&CString::new(format!("proc:{}/trace", pid)).unwrap(), NEW_FLAGS | fcntl::O_NONBLOCK)?, + mem: File::open(&CString::new(format!("proc:{}/mem", pid)).unwrap(), NEW_FLAGS)?, + regs: File::open(&CString::new(format!("proc:{}/regs/int", pid)).unwrap(), NEW_FLAGS)?, + fpregs: File::open(&CString::new(format!("proc:{}/regs/float", pid)).unwrap(), NEW_FLAGS)?, + })) + } else { + unsafe { errno = errnoh::ESRCH; } + Err(io::last_os_error()) + }, + Entry::Occupied(entry) => Ok(entry.into_mut()) + } +} fn inner_ptrace(request: c_int, pid: pid_t, addr: *mut c_void, data: *mut c_void) -> io::Result<c_int> { let state = init_state(); if request == sys_ptrace::PTRACE_TRACEME { - // let pid = Sys::getpid(); - // todo: only auto-open session on host if this happens + // Mark this child as traced, parent will check for this marker file + let pid = Sys::getpid(); + mem::forget(File::open( + &CString::new(format!("chan:ptrace-relibc/{}/traceme", pid)).unwrap(), + fcntl::O_CREAT | fcntl::O_PATH | fcntl::O_EXCL + )?); return Ok(0); } - const NEW_FLAGS: c_int = fcntl::O_RDWR | fcntl::O_CLOEXEC; - let mut sessions = state.sessions.lock(); - let session = match sessions.entry(pid) { - Entry::Vacant(entry) => entry.insert(Session { - tracer: File::open(&CString::new(format!("proc:{}/trace", pid)).unwrap(), NEW_FLAGS | fcntl::O_NONBLOCK)?, - mem: File::open(&CString::new(format!("proc:{}/mem", pid)).unwrap(), NEW_FLAGS)?, - regs: File::open(&CString::new(format!("proc:{}/regs/int", pid)).unwrap(), NEW_FLAGS)?, - fpregs: File::open(&CString::new(format!("proc:{}/regs/float", pid)).unwrap(), NEW_FLAGS)?, - }), - Entry::Occupied(entry) => entry.into_mut() - }; + let session = get_session(&mut sessions, pid)?; + match request { sys_ptrace::PTRACE_CONT | sys_ptrace::PTRACE_SINGLESTEP | sys_ptrace::PTRACE_SYSCALL | sys_ptrace::PTRACE_SYSEMU | sys_ptrace::PTRACE_SYSEMU_SINGLESTEP => { Sys::kill(pid, signal::SIGCONT as _); + // TODO: Translate errors (&mut &session.tracer).write(&[match request { sys_ptrace::PTRACE_CONT => syscall::PTRACE_CONT, sys_ptrace::PTRACE_SINGLESTEP => syscall::PTRACE_SINGLESTEP, diff --git a/tests/ptrace.c b/tests/ptrace.c index c2a1760b30eba9b3c50b4242c563377b4f4c0f2d..4a5c5d4d78e328276f26064992181c405875c2c7 100644 --- a/tests/ptrace.c +++ b/tests/ptrace.c @@ -8,11 +8,26 @@ #include "test_helpers.h" +#ifdef __linux__ + +const int SYS_write = 1; + +#endif +#ifdef __redox__ + +const int SYS_write = 0x21000004; + +#endif + int main() { int pid = fork(); ERROR_IF(fork, pid, == -1); if (pid == 0) { + // Test behavior on Redox when TRACEME hasn't been activated + // before waitpid is invoked! + sleep(1); + int result = ptrace(PTRACE_TRACEME, 0, NULL, NULL); ERROR_IF(ptrace, result, == -1); UNEXP_IF(ptrace, result, != 0); @@ -28,7 +43,7 @@ int main() { puts("Big surprise, right!"); } else { // Wait for child process to be ready - int result = waitpid(pid, NULL, WUNTRACED); + int result = waitpid(pid, NULL, 0); ERROR_IF(waitpid, result, == -1); int status; @@ -45,7 +60,7 @@ int main() { result = ptrace(PTRACE_GETREGS, pid, NULL, ®s); ERROR_IF(ptrace, result, == -1); - if (regs.orig_rax == 1 || regs.orig_rax == 0x21000004) { // SYS_write on Redox and Linux + if (regs.orig_rax == SYS_write || regs.orig_rax == SYS_write) { regs.rdi = 2; result = ptrace(PTRACE_SETREGS, pid, NULL, ®s); ERROR_IF(ptrace, result, == -1);