summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md3
-rw-r--r--src/sys/ptrace.rs71
-rw-r--r--test/sys/test_ptrace.rs49
-rw-r--r--test/sys/test_wait.rs11
4 files changed, 122 insertions, 12 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 31902b24..fe19e11c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
### Added
+- Added specialized wrappers: `sys::ptrace::{traceme, syscall, cont, attach}`. Using the matching routines
+ with `sys::ptrace::ptrace` is now deprecated.
- Added `nix::poll` module for all platforms
([#672](https://github.com/nix-rust/nix/pull/672))
- Added `nix::ppoll` function for FreeBSD and DragonFly
@@ -17,6 +19,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Changed
- Renamed existing `ptrace` wrappers to encourage namespacing ([#692](https://github.com/nix-rust/nix/pull/692))
+- Marked `sys::ptrace::ptrace` as `unsafe`.
- Changed function signature of `socket()` and `socketpair()`. The `protocol` argument
has changed type from `c_int` to `SockProtocol`.
It accepts a `None` value for default protocol that was specified with zero using `c_int`.
diff --git a/src/sys/ptrace.rs b/src/sys/ptrace.rs
index 877bfcb0..8bb9c63a 100644
--- a/src/sys/ptrace.rs
+++ b/src/sys/ptrace.rs
@@ -1,7 +1,10 @@
+//! For detailed description of the ptrace requests, consult `man ptrace`.
+
use std::{mem, ptr};
use {Errno, Error, Result};
use libc::{c_void, c_long, siginfo_t};
use ::unistd::Pid;
+use sys::signal::Signal;
pub mod ptrace {
use libc::c_int;
@@ -70,7 +73,11 @@ mod ffi {
/// Performs a ptrace request. If the request in question is provided by a specialised function
/// this function will return an unsupported operation error.
-pub fn ptrace(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
+#[deprecated(
+ since="0.10.0",
+ note="usages of `ptrace()` should be replaced with the specialized helper functions instead"
+)]
+pub unsafe fn ptrace(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
use self::ptrace::*;
match request {
@@ -103,8 +110,8 @@ fn ptrace_get_data<T>(request: ptrace::PtraceRequest, pid: Pid) -> Result<T> {
Ok(data)
}
-fn ptrace_other(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
- Errno::result(unsafe { ffi::ptrace(request, pid.into(), addr, data) }).map(|_| 0)
+unsafe fn ptrace_other(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
+ Errno::result(ffi::ptrace(request, pid.into(), addr, data)).map(|_| 0)
}
/// Set options, as with `ptrace(PTRACE_SETOPTIONS,...)`.
@@ -140,3 +147,61 @@ pub fn setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> {
Err(e) => Err(e),
}
}
+
+/// Sets the process as traceable, as with `ptrace(PTRACE_TRACEME, ...)`
+///
+/// Indicates that this process is to be traced by its parent.
+/// This is the only ptrace request to be issued by the tracee.
+pub fn traceme() -> Result<()> {
+ unsafe {
+ ptrace_other(
+ ptrace::PTRACE_TRACEME,
+ Pid::from_raw(0),
+ ptr::null_mut(),
+ ptr::null_mut(),
+ ).map(|_| ()) // ignore the useless return value
+ }
+}
+
+/// Ask for next syscall, as with `ptrace(PTRACE_SYSCALL, ...)`
+///
+/// Arranges for the tracee to be stopped at the next entry to or exit from a system call.
+pub fn syscall(pid: Pid) -> Result<()> {
+ unsafe {
+ ptrace_other(
+ ptrace::PTRACE_SYSCALL,
+ pid,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ ).map(|_| ()) // ignore the useless return value
+ }
+}
+
+/// Attach to a running process, as with `ptrace(PTRACE_ATTACH, ...)`
+///
+/// Attaches to the process specified in pid, making it a tracee of the calling process.
+pub fn attach(pid: Pid) -> Result<()> {
+ unsafe {
+ ptrace_other(
+ ptrace::PTRACE_ATTACH,
+ pid,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ ).map(|_| ()) // ignore the useless return value
+ }
+}
+
+/// Restart the stopped tracee process, as with `ptrace(PTRACE_CONT, ...)`
+///
+/// Continues the execution of the process with PID `pid`, optionally
+/// delivering a signal specified by `sig`.
+pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
+ let data = match sig.into() {
+ Some(s) => s as i32 as *mut c_void,
+ None => ptr::null_mut(),
+ };
+ unsafe {
+ ptrace_other(ptrace::PTRACE_CONT, pid, ptr::null_mut(), data).map(|_| ()) // ignore the useless return value
+ }
+}
+
diff --git a/test/sys/test_ptrace.rs b/test/sys/test_ptrace.rs
index 0614c13f..16b24110 100644
--- a/test/sys/test_ptrace.rs
+++ b/test/sys/test_ptrace.rs
@@ -3,14 +3,13 @@ use nix::errno::Errno;
use nix::unistd::getpid;
use nix::sys::ptrace;
-use std::{mem, ptr};
+use std::mem;
#[test]
fn test_ptrace() {
- use nix::sys::ptrace::ptrace::PTRACE_ATTACH;
// Just make sure ptrace can be called at all, for now.
// FIXME: qemu-user doesn't implement ptrace on all arches, so permit ENOSYS
- let err = ptrace::ptrace(PTRACE_ATTACH, getpid(), ptr::null_mut(), ptr::null_mut()).unwrap_err();
+ let err = ptrace::attach(getpid()).unwrap_err();
assert!(err == Error::Sys(Errno::EPERM) || err == Error::Sys(Errno::ENOSYS));
}
@@ -47,3 +46,47 @@ fn test_ptrace_setsiginfo() {
_ => (),
}
}
+
+
+#[test]
+fn test_ptrace_cont() {
+ use nix::sys::ptrace;
+ use nix::sys::signal::{raise, Signal};
+ use nix::sys::wait::{waitpid, WaitStatus};
+ use nix::unistd::fork;
+ use nix::unistd::ForkResult::*;
+
+ // FIXME: qemu-user doesn't implement ptrace on all architectures
+ // and retunrs ENOSYS in this case.
+ // We (ab)use this behavior to detect the affected platforms
+ // and skip the test then.
+ // On valid platforms the ptrace call should return Errno::EPERM, this
+ // is already tested by `test_ptrace`.
+ let err = ptrace::attach(getpid()).unwrap_err();
+ if err == Error::Sys(Errno::ENOSYS) {
+ return;
+ }
+
+ match fork() {
+ Ok(Child) => {
+ ptrace::traceme().unwrap();
+ // As recommended by ptrace(2), raise SIGTRAP to pause the child
+ // until the parent is ready to continue
+ loop {
+ raise(Signal::SIGTRAP).unwrap();
+ }
+
+ },
+ Ok(Parent { child }) => {
+ assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, Signal::SIGTRAP)));
+ ptrace::cont(child, None).unwrap();
+ assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, Signal::SIGTRAP)));
+ ptrace::cont(child, Signal::SIGKILL).unwrap();
+ match waitpid(child, None) {
+ Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _)) if pid == child => {}
+ _ => panic!("The process should have been killed"),
+ }
+ },
+ Err(_) => panic!("Error: Fork Failed")
+ }
+}
diff --git a/test/sys/test_wait.rs b/test/sys/test_wait.rs
index 620a4e33..0193e262 100644
--- a/test/sys/test_wait.rs
+++ b/test/sys/test_wait.rs
@@ -60,14 +60,13 @@ mod ptrace {
use nix::sys::wait::*;
use nix::unistd::*;
use nix::unistd::ForkResult::*;
- use std::ptr;
use libc::_exit;
fn ptrace_child() -> ! {
- ptrace::ptrace(PTRACE_TRACEME, Pid::from_raw(0), ptr::null_mut(), ptr::null_mut()).unwrap();
+ ptrace::traceme().unwrap();
// As recommended by ptrace(2), raise SIGTRAP to pause the child
// until the parent is ready to continue
- let _ = raise(SIGTRAP);
+ raise(SIGTRAP).unwrap();
unsafe { _exit(0) }
}
@@ -78,13 +77,13 @@ mod ptrace {
assert!(ptrace::setoptions(child, PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXIT).is_ok());
// First, stop on the next system call, which will be exit()
- assert!(ptrace::ptrace(PTRACE_SYSCALL, child, ptr::null_mut(), ptr::null_mut()).is_ok());
+ assert!(ptrace::syscall(child).is_ok());
assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child)));
// Then get the ptrace event for the process exiting
- assert!(ptrace::ptrace(PTRACE_CONT, child, ptr::null_mut(), ptr::null_mut()).is_ok());
+ assert!(ptrace::cont(child, None).is_ok());
assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceEvent(child, SIGTRAP, PTRACE_EVENT_EXIT)));
// Finally get the normal wait() result, now that the process has exited
- assert!(ptrace::ptrace(PTRACE_CONT, child, ptr::null_mut(), ptr::null_mut()).is_ok());
+ assert!(ptrace::cont(child, None).is_ok());
assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 0)));
}