Skip to content

Commit 4409962

Browse files
committed
Merge #709
709: Mark nix::sys::ptrace::ptrace as unsafe, add safe variants of some routines r=Susurrus These include: * PTRACE_TRACEME * PTRACE_CONT * PTRACE_ATTACH * PTRACE_SYSCALL This is a part of #666, which is being split up into a couple smaller PRs.
2 parents 4e9dd25 + abcec0f commit 4409962

File tree

4 files changed

+122
-12
lines changed

4 files changed

+122
-12
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
66
## [Unreleased]
77

88
### Added
9+
- Added specialized wrappers: `sys::ptrace::{traceme, syscall, cont, attach}`. Using the matching routines
10+
with `sys::ptrace::ptrace` is now deprecated.
911
- Added `nix::poll` module for all platforms
1012
([#672](https://github.com/nix-rust/nix/pull/672))
1113
- Added `nix::ppoll` function for FreeBSD and DragonFly
@@ -17,6 +19,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
1719

1820
### Changed
1921
- Renamed existing `ptrace` wrappers to encourage namespacing ([#692](https://github.com/nix-rust/nix/pull/692))
22+
- Marked `sys::ptrace::ptrace` as `unsafe`.
2023
- Changed function signature of `socket()` and `socketpair()`. The `protocol` argument
2124
has changed type from `c_int` to `SockProtocol`.
2225
It accepts a `None` value for default protocol that was specified with zero using `c_int`.

src/sys/ptrace.rs

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
//! For detailed description of the ptrace requests, consult `man ptrace`.
2+
13
use std::{mem, ptr};
24
use {Errno, Error, Result};
35
use libc::{c_void, c_long, siginfo_t};
46
use ::unistd::Pid;
7+
use sys::signal::Signal;
58

69
pub mod ptrace {
710
use libc::c_int;
@@ -70,7 +73,11 @@ mod ffi {
7073

7174
/// Performs a ptrace request. If the request in question is provided by a specialised function
7275
/// this function will return an unsupported operation error.
73-
pub fn ptrace(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
76+
#[deprecated(
77+
since="0.10.0",
78+
note="usages of `ptrace()` should be replaced with the specialized helper functions instead"
79+
)]
80+
pub unsafe fn ptrace(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
7481
use self::ptrace::*;
7582

7683
match request {
@@ -103,8 +110,8 @@ fn ptrace_get_data<T>(request: ptrace::PtraceRequest, pid: Pid) -> Result<T> {
103110
Ok(data)
104111
}
105112

106-
fn ptrace_other(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
107-
Errno::result(unsafe { ffi::ptrace(request, pid.into(), addr, data) }).map(|_| 0)
113+
unsafe fn ptrace_other(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
114+
Errno::result(ffi::ptrace(request, pid.into(), addr, data)).map(|_| 0)
108115
}
109116

110117
/// Set options, as with `ptrace(PTRACE_SETOPTIONS,...)`.
@@ -140,3 +147,61 @@ pub fn setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> {
140147
Err(e) => Err(e),
141148
}
142149
}
150+
151+
/// Sets the process as traceable, as with `ptrace(PTRACE_TRACEME, ...)`
152+
///
153+
/// Indicates that this process is to be traced by its parent.
154+
/// This is the only ptrace request to be issued by the tracee.
155+
pub fn traceme() -> Result<()> {
156+
unsafe {
157+
ptrace_other(
158+
ptrace::PTRACE_TRACEME,
159+
Pid::from_raw(0),
160+
ptr::null_mut(),
161+
ptr::null_mut(),
162+
).map(|_| ()) // ignore the useless return value
163+
}
164+
}
165+
166+
/// Ask for next syscall, as with `ptrace(PTRACE_SYSCALL, ...)`
167+
///
168+
/// Arranges for the tracee to be stopped at the next entry to or exit from a system call.
169+
pub fn syscall(pid: Pid) -> Result<()> {
170+
unsafe {
171+
ptrace_other(
172+
ptrace::PTRACE_SYSCALL,
173+
pid,
174+
ptr::null_mut(),
175+
ptr::null_mut(),
176+
).map(|_| ()) // ignore the useless return value
177+
}
178+
}
179+
180+
/// Attach to a running process, as with `ptrace(PTRACE_ATTACH, ...)`
181+
///
182+
/// Attaches to the process specified in pid, making it a tracee of the calling process.
183+
pub fn attach(pid: Pid) -> Result<()> {
184+
unsafe {
185+
ptrace_other(
186+
ptrace::PTRACE_ATTACH,
187+
pid,
188+
ptr::null_mut(),
189+
ptr::null_mut(),
190+
).map(|_| ()) // ignore the useless return value
191+
}
192+
}
193+
194+
/// Restart the stopped tracee process, as with `ptrace(PTRACE_CONT, ...)`
195+
///
196+
/// Continues the execution of the process with PID `pid`, optionally
197+
/// delivering a signal specified by `sig`.
198+
pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
199+
let data = match sig.into() {
200+
Some(s) => s as i32 as *mut c_void,
201+
None => ptr::null_mut(),
202+
};
203+
unsafe {
204+
ptrace_other(ptrace::PTRACE_CONT, pid, ptr::null_mut(), data).map(|_| ()) // ignore the useless return value
205+
}
206+
}
207+

test/sys/test_ptrace.rs

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@ use nix::errno::Errno;
33
use nix::unistd::getpid;
44
use nix::sys::ptrace;
55

6-
use std::{mem, ptr};
6+
use std::mem;
77

88
#[test]
99
fn test_ptrace() {
10-
use nix::sys::ptrace::ptrace::PTRACE_ATTACH;
1110
// Just make sure ptrace can be called at all, for now.
1211
// FIXME: qemu-user doesn't implement ptrace on all arches, so permit ENOSYS
13-
let err = ptrace::ptrace(PTRACE_ATTACH, getpid(), ptr::null_mut(), ptr::null_mut()).unwrap_err();
12+
let err = ptrace::attach(getpid()).unwrap_err();
1413
assert!(err == Error::Sys(Errno::EPERM) || err == Error::Sys(Errno::ENOSYS));
1514
}
1615

@@ -47,3 +46,47 @@ fn test_ptrace_setsiginfo() {
4746
_ => (),
4847
}
4948
}
49+
50+
51+
#[test]
52+
fn test_ptrace_cont() {
53+
use nix::sys::ptrace;
54+
use nix::sys::signal::{raise, Signal};
55+
use nix::sys::wait::{waitpid, WaitStatus};
56+
use nix::unistd::fork;
57+
use nix::unistd::ForkResult::*;
58+
59+
// FIXME: qemu-user doesn't implement ptrace on all architectures
60+
// and retunrs ENOSYS in this case.
61+
// We (ab)use this behavior to detect the affected platforms
62+
// and skip the test then.
63+
// On valid platforms the ptrace call should return Errno::EPERM, this
64+
// is already tested by `test_ptrace`.
65+
let err = ptrace::attach(getpid()).unwrap_err();
66+
if err == Error::Sys(Errno::ENOSYS) {
67+
return;
68+
}
69+
70+
match fork() {
71+
Ok(Child) => {
72+
ptrace::traceme().unwrap();
73+
// As recommended by ptrace(2), raise SIGTRAP to pause the child
74+
// until the parent is ready to continue
75+
loop {
76+
raise(Signal::SIGTRAP).unwrap();
77+
}
78+
79+
},
80+
Ok(Parent { child }) => {
81+
assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, Signal::SIGTRAP)));
82+
ptrace::cont(child, None).unwrap();
83+
assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, Signal::SIGTRAP)));
84+
ptrace::cont(child, Signal::SIGKILL).unwrap();
85+
match waitpid(child, None) {
86+
Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _)) if pid == child => {}
87+
_ => panic!("The process should have been killed"),
88+
}
89+
},
90+
Err(_) => panic!("Error: Fork Failed")
91+
}
92+
}

test/sys/test_wait.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,13 @@ mod ptrace {
6060
use nix::sys::wait::*;
6161
use nix::unistd::*;
6262
use nix::unistd::ForkResult::*;
63-
use std::ptr;
6463
use libc::_exit;
6564

6665
fn ptrace_child() -> ! {
67-
ptrace::ptrace(PTRACE_TRACEME, Pid::from_raw(0), ptr::null_mut(), ptr::null_mut()).unwrap();
66+
ptrace::traceme().unwrap();
6867
// As recommended by ptrace(2), raise SIGTRAP to pause the child
6968
// until the parent is ready to continue
70-
let _ = raise(SIGTRAP);
69+
raise(SIGTRAP).unwrap();
7170
unsafe { _exit(0) }
7271
}
7372

@@ -78,13 +77,13 @@ mod ptrace {
7877
assert!(ptrace::setoptions(child, PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXIT).is_ok());
7978

8079
// First, stop on the next system call, which will be exit()
81-
assert!(ptrace::ptrace(PTRACE_SYSCALL, child, ptr::null_mut(), ptr::null_mut()).is_ok());
80+
assert!(ptrace::syscall(child).is_ok());
8281
assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child)));
8382
// Then get the ptrace event for the process exiting
84-
assert!(ptrace::ptrace(PTRACE_CONT, child, ptr::null_mut(), ptr::null_mut()).is_ok());
83+
assert!(ptrace::cont(child, None).is_ok());
8584
assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceEvent(child, SIGTRAP, PTRACE_EVENT_EXIT)));
8685
// Finally get the normal wait() result, now that the process has exited
87-
assert!(ptrace::ptrace(PTRACE_CONT, child, ptr::null_mut(), ptr::null_mut()).is_ok());
86+
assert!(ptrace::cont(child, None).is_ok());
8887
assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 0)));
8988
}
9089

0 commit comments

Comments
 (0)