diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-08-09 20:16:35 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-09 20:16:35 +0000 |
commit | c5590df59c225353b7d267de9df8c36ca10a180b (patch) | |
tree | ac2820918c5ff7b20f6d75d430cbefc21d9d5488 | |
parent | 4c4be116f6414dbc9258f67102ea4b525efc037f (diff) | |
parent | 1cbde2bb472507bdc6b84ec9ba6172dfcc2c01c5 (diff) | |
download | nix-c5590df59c225353b7d267de9df8c36ca10a180b.zip |
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 <blaind@blaind.net>
-rw-r--r-- | CHANGELOG.md | 2 | ||||
-rw-r--r-- | src/sys/ptrace/linux.rs | 18 | ||||
-rw-r--r-- | test/sys/test_ptrace.rs | 42 |
3 files changed, 57 insertions, 5 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index d3594798..783d6675 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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` diff --git a/src/sys/ptrace/linux.rs b/src/sys/ptrace/linux.rs index 74a23e03..ef1dafbb 100644 --- a/src/sys/ptrace/linux.rs +++ b/src/sys/ptrace/linux.rs @@ -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"))))] @@ -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( @@ -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);` diff --git a/test/sys/test_ptrace.rs b/test/sys/test_ptrace.rs index 985945d1..ceb39b9b 100644 --- a/test/sys/test_ptrace.rs +++ b/test/sys/test_ptrace.rs @@ -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", |