Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Moved ptrace constants into enum types plus minor additions #749

Merged
merged 3 commits into from
Aug 28, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
([#739](https://github.com/nix-rust/nix/pull/739))
- Expose `signalfd` module on Android as well.
([#739](https://github.com/nix-rust/nix/pull/739))
- Added nix::sys::ptrace::detach.
([#749](https://github.com/nix-rust/nix/pull/749))

### Changed
- Renamed existing `ptrace` wrappers to encourage namespacing ([#692](https://github.com/nix-rust/nix/pull/692))
Expand All @@ -47,6 +49,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
([#731](https://github.com/nix-rust/nix/pull/731))
- Marked `pty::ptsname` function as `unsafe`
([#744](https://github.com/nix-rust/nix/pull/744))
- Moved constants ptrace request, event and options to enums and updated ptrace functions and argument types accordingly.
([#749](https://github.com/nix-rust/nix/pull/749))

# Fixed
- Fix compilation and tests for OpenBSD targets
Expand Down
17 changes: 17 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,23 @@ macro_rules! libc_enum {
}
};

// Munch an ident and cast it to the given type; covers terminating comma.
(@accumulate_entries
$prefix:tt,
[$($entries:tt)*];
$entry:ident as $ty:ty, $($tail:tt)*
) => {
libc_enum! {
@accumulate_entries
$prefix,
[
$($entries)*
$entry = libc::$entry as $ty,
];
$($tail)*
}
};

// (non-pub) Entry rule.
(
$(#[$attr:meta])*
Expand Down
235 changes: 153 additions & 82 deletions src/sys/ptrace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,68 +6,117 @@ use libc::{self, c_void, c_long, siginfo_t};
use ::unistd::Pid;
use sys::signal::Signal;

pub mod ptrace {
use libc::c_int;

cfg_if! {
if #[cfg(any(all(target_os = "linux", arch = "s390x"),
all(target_os = "linux", target_env = "gnu")))] {
pub type PtraceRequest = ::libc::c_uint;
} else {
pub type PtraceRequest = c_int;
}

cfg_if! {
if #[cfg(any(all(target_os = "linux", arch = "s390x"),
all(target_os = "linux", target_env = "gnu")))] {
#[doc(hidden)]
pub type RequestType = ::libc::c_uint;
} else {
#[doc(hidden)]
pub type RequestType = ::libc::c_int;
}
}

libc_enum!{
#[cfg_attr(not(any(target_env = "musl", target_os = "android")), repr(u32))]
#[cfg_attr(any(target_env = "musl", target_os = "android"), repr(i32))]
/// Ptrace Request enum defining the action to be taken.
pub enum Request {
PTRACE_TRACEME,
PTRACE_PEEKTEXT,
PTRACE_PEEKDATA,
PTRACE_PEEKUSER,
PTRACE_POKETEXT,
PTRACE_POKEDATA,
PTRACE_POKEUSER,
PTRACE_CONT,
PTRACE_KILL,
PTRACE_SINGLESTEP,
#[cfg(all(any(target_env = "musl", target_arch ="x86_64", target_arch = "s390x"), not(target_os = "android")))]
PTRACE_GETREGS,
#[cfg(all(any(target_env = "musl", target_arch ="x86_64", target_arch = "s390x"), not(target_os = "android")))]
PTRACE_SETREGS,
#[cfg(all(any(target_env = "musl", target_arch ="x86_64", target_arch = "s390x"), not(target_os = "android")))]
PTRACE_GETFPREGS,
#[cfg(all(any(target_env = "musl", target_arch ="x86_64", target_arch = "s390x"), not(target_os = "android")))]
PTRACE_SETFPREGS,
PTRACE_ATTACH,
PTRACE_DETACH,
#[cfg(all(any(target_env = "musl", target_arch ="x86_64"), not(target_os = "android")))]
PTRACE_GETFPXREGS,
#[cfg(all(any(target_env = "musl", target_arch ="x86_64"), not(target_os = "android")))]
PTRACE_SETFPXREGS,
PTRACE_SYSCALL,
PTRACE_SETOPTIONS,
PTRACE_GETEVENTMSG,
PTRACE_GETSIGINFO,
PTRACE_SETSIGINFO,
#[cfg(all(any(target_env = "musl", target_arch ="x86_64", target_arch = "s390x"), not(target_os = "android")))]
PTRACE_GETREGSET,
#[cfg(all(any(target_env = "musl", target_arch ="x86_64", target_arch = "s390x"), not(target_os = "android")))]
PTRACE_SETREGSET,
#[cfg(not(any(target_os = "android", target_arch = "mips", target_arch = "mips64")))]
PTRACE_SEIZE,
#[cfg(not(any(target_os = "android", target_arch = "mips", target_arch = "mips64")))]
PTRACE_INTERRUPT,
#[cfg(not(any(target_os = "android", target_arch = "mips", target_arch = "mips64")))]
PTRACE_LISTEN,
#[cfg(not(any(target_os = "android", target_arch = "mips", target_arch = "mips64")))]
PTRACE_PEEKSIGINFO,
}
}

libc_enum!{
#[repr(i32)]
/// Using the ptrace options the tracer can configure the tracee to stop
/// at certain events. This enum is used to define those events as defined
/// in `man ptrace`.
pub enum Event {
/// Event that stops before a return from fork or clone.
PTRACE_EVENT_FORK,
/// Event that stops before a return from vfork or clone.
PTRACE_EVENT_VFORK,
/// Event that stops before a return from clone.
PTRACE_EVENT_CLONE,
/// Event that stops before a return from execve.
PTRACE_EVENT_EXEC,
/// Event for a return from vfork.
PTRACE_EVENT_VFORK_DONE,
/// Event for a stop before an exit. Unlike the waitpid Exit status program.
/// registers can still be examined
PTRACE_EVENT_EXIT,
/// STop triggered by a seccomp rule on a tracee.
PTRACE_EVENT_SECCOMP,
// PTRACE_EVENT_STOP not provided by libc because it's defined in glibc 2.26
}
}

pub const PTRACE_TRACEME: PtraceRequest = 0;
pub const PTRACE_PEEKTEXT: PtraceRequest = 1;
pub const PTRACE_PEEKDATA: PtraceRequest = 2;
pub const PTRACE_PEEKUSER: PtraceRequest = 3;
pub const PTRACE_POKETEXT: PtraceRequest = 4;
pub const PTRACE_POKEDATA: PtraceRequest = 5;
pub const PTRACE_POKEUSER: PtraceRequest = 6;
pub const PTRACE_CONT: PtraceRequest = 7;
pub const PTRACE_KILL: PtraceRequest = 8;
pub const PTRACE_SINGLESTEP: PtraceRequest = 9;
pub const PTRACE_GETREGS: PtraceRequest = 12;
pub const PTRACE_SETREGS: PtraceRequest = 13;
pub const PTRACE_GETFPREGS: PtraceRequest = 14;
pub const PTRACE_SETFPREGS: PtraceRequest = 15;
pub const PTRACE_ATTACH: PtraceRequest = 16;
pub const PTRACE_DETACH: PtraceRequest = 17;
pub const PTRACE_GETFPXREGS: PtraceRequest = 18;
pub const PTRACE_SETFPXREGS: PtraceRequest = 19;
pub const PTRACE_SYSCALL: PtraceRequest = 24;
pub const PTRACE_SETOPTIONS: PtraceRequest = 0x4200;
pub const PTRACE_GETEVENTMSG: PtraceRequest = 0x4201;
pub const PTRACE_GETSIGINFO: PtraceRequest = 0x4202;
pub const PTRACE_SETSIGINFO: PtraceRequest = 0x4203;
pub const PTRACE_GETREGSET: PtraceRequest = 0x4204;
pub const PTRACE_SETREGSET: PtraceRequest = 0x4205;
pub const PTRACE_SEIZE: PtraceRequest = 0x4206;
pub const PTRACE_INTERRUPT: PtraceRequest = 0x4207;
pub const PTRACE_LISTEN: PtraceRequest = 0x4208;
pub const PTRACE_PEEKSIGINFO: PtraceRequest = 0x4209;

pub type PtraceEvent = c_int;

pub const PTRACE_EVENT_FORK: PtraceEvent = 1;
pub const PTRACE_EVENT_VFORK: PtraceEvent = 2;
pub const PTRACE_EVENT_CLONE: PtraceEvent = 3;
pub const PTRACE_EVENT_EXEC: PtraceEvent = 4;
pub const PTRACE_EVENT_VFORK_DONE: PtraceEvent = 5;
pub const PTRACE_EVENT_EXIT: PtraceEvent = 6;
pub const PTRACE_EVENT_SECCOMP: PtraceEvent = 6;
pub const PTRACE_EVENT_STOP: PtraceEvent = 128;

pub type PtraceOptions = c_int;
pub const PTRACE_O_TRACESYSGOOD: PtraceOptions = 1;
pub const PTRACE_O_TRACEFORK: PtraceOptions = (1 << PTRACE_EVENT_FORK);
pub const PTRACE_O_TRACEVFORK: PtraceOptions = (1 << PTRACE_EVENT_VFORK);
pub const PTRACE_O_TRACECLONE: PtraceOptions = (1 << PTRACE_EVENT_CLONE);
pub const PTRACE_O_TRACEEXEC: PtraceOptions = (1 << PTRACE_EVENT_EXEC);
pub const PTRACE_O_TRACEVFORKDONE: PtraceOptions = (1 << PTRACE_EVENT_VFORK_DONE);
pub const PTRACE_O_TRACEEXIT: PtraceOptions = (1 << PTRACE_EVENT_EXIT);
pub const PTRACE_O_TRACESECCOMP: PtraceOptions = (1 << PTRACE_EVENT_SECCOMP);
libc_bitflags! {
/// Ptrace options used in conjunction with the PTRACE_SETOPTIONS request.
/// See `man ptrace` for more details.
pub struct Options: libc::c_int {
/// When delivering system call traps set a bit to allow tracer to
/// distinguish between normal stops or syscall stops. May not work on
/// all systems.
PTRACE_O_TRACESYSGOOD;
/// Stop tracee at next fork and start tracing the forked process.
PTRACE_O_TRACEFORK;
/// Stop tracee at next vfork call and trace the vforked process.
PTRACE_O_TRACEVFORK;
/// Stop tracee at next clone call and trace the cloned process.
PTRACE_O_TRACECLONE;
/// Stop tracee at next execve call.
PTRACE_O_TRACEEXEC;
/// Stop tracee at vfork completion.
PTRACE_O_TRACEVFORKDONE;
/// Stop tracee at next exit call. Stops before exit commences allowing
/// tracer to see location of exit and register states.
PTRACE_O_TRACEEXIT;
/// Stop tracee when a SECCOMP_RET_TRACE rule is triggered. See `man seccomp` for more
/// details.
PTRACE_O_TRACESECCOMP;
}
}

/// Performs a ptrace request. If the request in question is provided by a specialised function
Expand All @@ -76,20 +125,19 @@ pub mod ptrace {
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::*;

pub unsafe fn ptrace(request: Request, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
use self::Request::*;
match request {
PTRACE_PEEKTEXT | PTRACE_PEEKDATA | PTRACE_PEEKUSER => ptrace_peek(request, pid, addr, data),
PTRACE_GETSIGINFO | PTRACE_GETEVENTMSG | PTRACE_SETSIGINFO | PTRACE_SETOPTIONS => Err(Error::UnsupportedOperation),
_ => ptrace_other(request, pid, addr, data)
}
}

fn ptrace_peek(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
fn ptrace_peek(request: Request, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
let ret = unsafe {
Errno::clear();
libc::ptrace(request, libc::pid_t::from(pid), addr, data)
libc::ptrace(request as RequestType, libc::pid_t::from(pid), addr, data)
};
match Errno::result(ret) {
Ok(..) | Err(Error::Sys(Errno::UnknownErrno)) => Ok(ret),
Expand All @@ -101,45 +149,54 @@ fn ptrace_peek(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, data
/// Some ptrace get requests populate structs or larger elements than c_long
/// and therefore use the data field to return values. This function handles these
/// requests.
fn ptrace_get_data<T>(request: ptrace::PtraceRequest, pid: Pid) -> Result<T> {
fn ptrace_get_data<T>(request: Request, pid: Pid) -> Result<T> {
// Creates an uninitialized pointer to store result in
let data: T = unsafe { mem::uninitialized() };
let res = unsafe { libc::ptrace(request, libc::pid_t::from(pid), ptr::null_mut::<T>(), &data as *const _ as *const c_void) };
let res = unsafe {
libc::ptrace(request as RequestType,
libc::pid_t::from(pid),
ptr::null_mut::<T>(),
&data as *const _ as *const c_void)
};
Errno::result(res)?;
Ok(data)
}

unsafe fn ptrace_other(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
Errno::result(libc::ptrace(request, libc::pid_t::from(pid), addr, data)).map(|_| 0)
unsafe fn ptrace_other(request: Request, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
Errno::result(libc::ptrace(request as RequestType, libc::pid_t::from(pid), addr, data)).map(|_| 0)
}

/// Set options, as with `ptrace(PTRACE_SETOPTIONS,...)`.
pub fn setoptions(pid: Pid, options: ptrace::PtraceOptions) -> Result<()> {
use self::ptrace::*;
pub fn setoptions(pid: Pid, options: Options) -> Result<()> {
use std::ptr;

let res = unsafe { libc::ptrace(PTRACE_SETOPTIONS, libc::pid_t::from(pid), ptr::null_mut::<libc::c_void>(), options as *mut c_void) };
let res = unsafe {
libc::ptrace(Request::PTRACE_SETOPTIONS as RequestType,
libc::pid_t::from(pid),
ptr::null_mut::<libc::c_void>(),
options.bits() as *mut c_void)
};
Errno::result(res).map(|_| ())
}

/// Gets a ptrace event as described by `ptrace(PTRACE_GETEVENTMSG,...)`
pub fn getevent(pid: Pid) -> Result<c_long> {
use self::ptrace::*;
ptrace_get_data::<c_long>(PTRACE_GETEVENTMSG, pid)
ptrace_get_data::<c_long>(Request::PTRACE_GETEVENTMSG, pid)
}

/// Get siginfo as with `ptrace(PTRACE_GETSIGINFO,...)`
pub fn getsiginfo(pid: Pid) -> Result<siginfo_t> {
use self::ptrace::*;
ptrace_get_data::<siginfo_t>(PTRACE_GETSIGINFO, pid)
ptrace_get_data::<siginfo_t>(Request::PTRACE_GETSIGINFO, pid)
}

/// Set siginfo as with `ptrace(PTRACE_SETSIGINFO,...)`
pub fn setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> {
use self::ptrace::*;
let ret = unsafe{
Errno::clear();
libc::ptrace(PTRACE_SETSIGINFO, libc::pid_t::from(pid), ptr::null_mut::<libc::c_void>(), sig as *const _ as *const c_void)
libc::ptrace(Request::PTRACE_SETSIGINFO as RequestType,
libc::pid_t::from(pid),
ptr::null_mut::<libc::c_void>(),
sig as *const _ as *const c_void)
};
match Errno::result(ret) {
Ok(_) => Ok(()),
Expand All @@ -154,7 +211,7 @@ pub fn setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> {
pub fn traceme() -> Result<()> {
unsafe {
ptrace_other(
ptrace::PTRACE_TRACEME,
Request::PTRACE_TRACEME,
Pid::from_raw(0),
ptr::null_mut(),
ptr::null_mut(),
Expand All @@ -168,7 +225,7 @@ pub fn traceme() -> Result<()> {
pub fn syscall(pid: Pid) -> Result<()> {
unsafe {
ptrace_other(
ptrace::PTRACE_SYSCALL,
Request::PTRACE_SYSCALL,
pid,
ptr::null_mut(),
ptr::null_mut(),
Expand All @@ -182,14 +239,28 @@ pub fn syscall(pid: Pid) -> Result<()> {
pub fn attach(pid: Pid) -> Result<()> {
unsafe {
ptrace_other(
ptrace::PTRACE_ATTACH,
Request::PTRACE_ATTACH,
pid,
ptr::null_mut(),
ptr::null_mut(),
).map(|_| ()) // ignore the useless return value
}
}

/// Detaches the current running process, as with `ptrace(PTRACE_DETACH, ...)`
///
/// Detaches from the process specified in pid allowing it to run freely
pub fn detach(pid: Pid) -> Result<()> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move the addition of this function into a separate commit following the existing two and give it a separate entry under "Added" in the CHANGELOG.

unsafe {
ptrace_other(
Request::PTRACE_DETACH,
pid,
ptr::null_mut(),
ptr::null_mut()
).map(|_| ())
}
}

/// Restart the stopped tracee process, as with `ptrace(PTRACE_CONT, ...)`
///
/// Continues the execution of the process with PID `pid`, optionally
Expand All @@ -200,7 +271,7 @@ pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
None => ptr::null_mut(),
};
unsafe {
ptrace_other(ptrace::PTRACE_CONT, pid, ptr::null_mut(), data).map(|_| ()) // ignore the useless return value
ptrace_other(Request::PTRACE_CONT, pid, ptr::null_mut(), data).map(|_| ()) // ignore the useless return value
}
}

3 changes: 1 addition & 2 deletions test/sys/test_ptrace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ fn test_ptrace() {
// Just make sure ptrace_setoptions can be called at all, for now.
#[test]
fn test_ptrace_setoptions() {
use nix::sys::ptrace::ptrace::PTRACE_O_TRACESYSGOOD;
let err = ptrace::setoptions(getpid(), PTRACE_O_TRACESYSGOOD).unwrap_err();
let err = ptrace::setoptions(getpid(), ptrace::PTRACE_O_TRACESYSGOOD).unwrap_err();
assert!(err != Error::UnsupportedOperation);
}

Expand Down
4 changes: 2 additions & 2 deletions test/sys/test_wait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ fn test_waitstatus_pid() {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
mod ptrace {
use nix::sys::ptrace;
use nix::sys::ptrace::ptrace::*;
use nix::sys::ptrace::*;
use nix::sys::signal::*;
use nix::sys::wait::*;
use nix::unistd::*;
Expand All @@ -81,7 +81,7 @@ mod ptrace {
assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child)));
// Then get the ptrace event for the process exiting
assert!(ptrace::cont(child, None).is_ok());
assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceEvent(child, SIGTRAP, PTRACE_EVENT_EXIT)));
assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceEvent(child, SIGTRAP, Event::PTRACE_EVENT_EXIT as i32)));
// Finally get the normal wait() result, now that the process has exited
assert!(ptrace::cont(child, None).is_ok());
assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 0)));
Expand Down