Skip to content

Commit

Permalink
Merge #1422
Browse files Browse the repository at this point in the history
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 <blaind@blaind.net>
  • Loading branch information
bors[bot] and blaind committed Aug 9, 2021
2 parents 4c4be11 + 1cbde2b commit c5590df
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 5 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
- Added `sendfile64` (#[1439](https://github.com/nix-rust/nix/pull/1439))
- Added `MS_LAZYTIME` to `MsFlags`
(#[1437](https://github.com/nix-rust/nix/pull/1437))
- Added `ptrace::interrupt` method for platforms that support `PTRACE_INTERRUPT`
(#[1422](https://github.com/nix-rust/nix/pull/1422))

### Changed
- Made `forkpty` unsafe, like `fork`
Expand Down
18 changes: 13 additions & 5 deletions src/sys/ptrace/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,9 @@ libc_enum!{
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips64"))))]
PTRACE_SETREGSET,
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips64"))))]
#[cfg(target_os = "linux")]
PTRACE_SEIZE,
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips64"))))]
#[cfg(target_os = "linux")]
PTRACE_INTERRUPT,
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips64"))))]
Expand Down Expand Up @@ -339,7 +337,7 @@ pub fn attach(pid: Pid) -> Result<()> {
/// Attach to a running process, as with `ptrace(PTRACE_SEIZE, ...)`
///
/// Attaches to the process specified in pid, making it a tracee of the calling process.
#[cfg(all(target_os = "linux", not(any(target_arch = "mips", target_arch = "mips64"))))]
#[cfg(target_os = "linux")]
pub fn seize(pid: Pid, options: Options) -> Result<()> {
unsafe {
ptrace_other(
Expand Down Expand Up @@ -384,6 +382,16 @@ pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
}
}

/// Stop a tracee, as with `ptrace(PTRACE_INTERRUPT, ...)`
///
/// This request is equivalent to `ptrace(PTRACE_INTERRUPT, ...)`
#[cfg(target_os = "linux")]
pub fn interrupt(pid: Pid) -> Result<()> {
unsafe {
ptrace_other(Request::PTRACE_INTERRUPT, pid, ptr::null_mut(), ptr::null_mut()).map(drop)
}
}

/// Issues a kill request as with `ptrace(PTRACE_KILL, ...)`
///
/// This request is equivalent to `ptrace(PTRACE_CONT, ..., SIGKILL);`
Expand Down
42 changes: 42 additions & 0 deletions test/sys/test_ptrace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,48 @@ fn test_ptrace_cont() {
}
}

#[cfg(target_os = "linux")]
#[test]
fn test_ptrace_interrupt() {
use nix::sys::ptrace;
use nix::sys::signal::Signal;
use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus};
use nix::unistd::fork;
use nix::unistd::ForkResult::*;
use std::thread::sleep;
use std::time::Duration;

require_capability!(CAP_SYS_PTRACE);

let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");

match unsafe{fork()}.expect("Error: Fork Failed") {
Child => {
loop {
sleep(Duration::from_millis(1000));
}

},
Parent { child } => {
ptrace::seize(child, ptrace::Options::PTRACE_O_TRACESYSGOOD).unwrap();
ptrace::interrupt(child).unwrap();
assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceEvent(child, Signal::SIGTRAP, 128)));
ptrace::syscall(child, None).unwrap();
assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child)));
ptrace::detach(child, Some(Signal::SIGKILL)).unwrap();
match waitpid(child, None) {
Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _)) if pid == child => {
let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
while ptrace::cont(child, Some(Signal::SIGKILL)).is_ok() {
let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
}
}
_ => panic!("The process should have been killed"),
}
},
}
}

// ptrace::{setoptions, getregs} are only available in these platforms
#[cfg(all(target_os = "linux",
any(target_arch = "x86_64",
Expand Down

0 comments on commit c5590df

Please sign in to comment.