Skip to content

Commit c5590df

Browse files
bors[bot]blaind
andauthored
Merge #1422
1422: Add PTRACE_INTERRUPT call as `ptrace::interrupt(pid)` r=asomers a=blaind I've based the test on `fn test_ptrace_cont`. Removed some parts, but not 100% sure what's the proper way of testing in nix context. From ptrace-man page: ``` PTRACE_INTERRUPT (since Linux 3.4) Stop a tracee. If the tracee is running or sleeping in kernel space and PTRACE_SYSCALL is in effect, the system call is interrupted and syscall-exit-stop is reported. (The interrupted system call is restarted when the tracee is restarted.) If the tracee was already stopped by a signal and PTRACE_LISTEN was sent to it, the tracee stops with PTRACE_EVENT_STOP and WSTOPSIG(status) returns the stop signal. If any other ptrace-stop is generated at the same time (for example, if a signal is sent to the tracee), this ptrace-stop happens. If none of the above applies (for example, if the tracee is running in user space), it stops with PTRACE_EVENT_STOP with WSTOPSIG(status) == SIGTRAP. PTRACE_INTERRUPT only works on tracees attached by PTRACE_SEIZE. ``` Co-authored-by: Mika Vatanen <[email protected]>
2 parents 4c4be11 + 1cbde2b commit c5590df

File tree

3 files changed

+57
-5
lines changed

3 files changed

+57
-5
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
9090
- Added `sendfile64` (#[1439](https://github.com/nix-rust/nix/pull/1439))
9191
- Added `MS_LAZYTIME` to `MsFlags`
9292
(#[1437](https://github.com/nix-rust/nix/pull/1437))
93+
- Added `ptrace::interrupt` method for platforms that support `PTRACE_INTERRUPT`
94+
(#[1422](https://github.com/nix-rust/nix/pull/1422))
9395

9496
### Changed
9597
- Made `forkpty` unsafe, like `fork`

src/sys/ptrace/linux.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,9 @@ libc_enum!{
9898
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
9999
target_arch = "mips64"))))]
100100
PTRACE_SETREGSET,
101-
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
102-
target_arch = "mips64"))))]
101+
#[cfg(target_os = "linux")]
103102
PTRACE_SEIZE,
104-
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
105-
target_arch = "mips64"))))]
103+
#[cfg(target_os = "linux")]
106104
PTRACE_INTERRUPT,
107105
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
108106
target_arch = "mips64"))))]
@@ -339,7 +337,7 @@ pub fn attach(pid: Pid) -> Result<()> {
339337
/// Attach to a running process, as with `ptrace(PTRACE_SEIZE, ...)`
340338
///
341339
/// Attaches to the process specified in pid, making it a tracee of the calling process.
342-
#[cfg(all(target_os = "linux", not(any(target_arch = "mips", target_arch = "mips64"))))]
340+
#[cfg(target_os = "linux")]
343341
pub fn seize(pid: Pid, options: Options) -> Result<()> {
344342
unsafe {
345343
ptrace_other(
@@ -384,6 +382,16 @@ pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
384382
}
385383
}
386384

385+
/// Stop a tracee, as with `ptrace(PTRACE_INTERRUPT, ...)`
386+
///
387+
/// This request is equivalent to `ptrace(PTRACE_INTERRUPT, ...)`
388+
#[cfg(target_os = "linux")]
389+
pub fn interrupt(pid: Pid) -> Result<()> {
390+
unsafe {
391+
ptrace_other(Request::PTRACE_INTERRUPT, pid, ptr::null_mut(), ptr::null_mut()).map(drop)
392+
}
393+
}
394+
387395
/// Issues a kill request as with `ptrace(PTRACE_KILL, ...)`
388396
///
389397
/// This request is equivalent to `ptrace(PTRACE_CONT, ..., SIGKILL);`

test/sys/test_ptrace.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,48 @@ fn test_ptrace_cont() {
114114
}
115115
}
116116

117+
#[cfg(target_os = "linux")]
118+
#[test]
119+
fn test_ptrace_interrupt() {
120+
use nix::sys::ptrace;
121+
use nix::sys::signal::Signal;
122+
use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus};
123+
use nix::unistd::fork;
124+
use nix::unistd::ForkResult::*;
125+
use std::thread::sleep;
126+
use std::time::Duration;
127+
128+
require_capability!(CAP_SYS_PTRACE);
129+
130+
let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
131+
132+
match unsafe{fork()}.expect("Error: Fork Failed") {
133+
Child => {
134+
loop {
135+
sleep(Duration::from_millis(1000));
136+
}
137+
138+
},
139+
Parent { child } => {
140+
ptrace::seize(child, ptrace::Options::PTRACE_O_TRACESYSGOOD).unwrap();
141+
ptrace::interrupt(child).unwrap();
142+
assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceEvent(child, Signal::SIGTRAP, 128)));
143+
ptrace::syscall(child, None).unwrap();
144+
assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child)));
145+
ptrace::detach(child, Some(Signal::SIGKILL)).unwrap();
146+
match waitpid(child, None) {
147+
Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _)) if pid == child => {
148+
let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
149+
while ptrace::cont(child, Some(Signal::SIGKILL)).is_ok() {
150+
let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
151+
}
152+
}
153+
_ => panic!("The process should have been killed"),
154+
}
155+
},
156+
}
157+
}
158+
117159
// ptrace::{setoptions, getregs} are only available in these platforms
118160
#[cfg(all(target_os = "linux",
119161
any(target_arch = "x86_64",

0 commit comments

Comments
 (0)