diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cb9d2aec5..59d25af03a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ This project adheres to [Semantic Versioning](https://semver.org/). - Fixed compilation and updated support on Haiku - Added support for the `x86_64-unknown-haiku` target. (#[1703](https://github.com/nix-rust/nix/pull/1703)) +- Added `ptrace::read_user` and `ptrace::write_user` for Linux. + (#[1697](https://github.com/nix-rust/nix/pull/1697)) ### Changed diff --git a/src/sys/ptrace/linux.rs b/src/sys/ptrace/linux.rs index 24152d7d5a..1d9b241c1f 100644 --- a/src/sys/ptrace/linux.rs +++ b/src/sys/ptrace/linux.rs @@ -481,3 +481,24 @@ pub unsafe fn write( { ptrace_other(Request::PTRACE_POKEDATA, pid, addr, data).map(drop) } + +/// Reads a word from a user area at `offset`. +/// The user struct definition can be found in `/usr/include/sys/user.h`. +pub fn read_user(pid: Pid, offset: AddressType) -> Result { + ptrace_peek(Request::PTRACE_PEEKUSER, pid, offset, ptr::null_mut()) +} + +/// Writes a word to a user area at `offset`. +/// The user struct definition can be found in `/usr/include/sys/user.h`. +/// +/// # Safety +/// +/// The `data` argument is passed directly to `ptrace(2)`. Read that man page +/// for guidance. +pub unsafe fn write_user( + pid: Pid, + offset: AddressType, + data: *mut c_void) -> Result<()> +{ + ptrace_other(Request::PTRACE_POKEUSER, pid, offset, data).map(drop) +} diff --git a/test/sys/test_ptrace.rs b/test/sys/test_ptrace.rs index 89c4e2ddad..8556a54845 100644 --- a/test/sys/test_ptrace.rs +++ b/test/sys/test_ptrace.rs @@ -1,3 +1,8 @@ +#[cfg(all(target_os = "linux", + any(target_arch = "x86_64", + target_arch = "x86"), + target_env = "gnu"))] +use memoffset::offset_of; use nix::errno::Errno; use nix::unistd::getpid; use nix::sys::ptrace; @@ -197,15 +202,29 @@ fn test_ptrace_syscall() { #[cfg(target_arch = "x86")] let get_syscall_id = || ptrace::getregs(child).unwrap().orig_eax as libc::c_long; + // this duplicates `get_syscall_id` for the purpose of testing `ptrace::read_user`. + #[cfg(target_arch = "x86_64")] + let rax_offset = offset_of!(libc::user_regs_struct, orig_rax); + #[cfg(target_arch = "x86")] + let rax_offset = offset_of!(libc::user_regs_struct, orig_eax); + + let get_syscall_from_user_area = || { + // Find the offset of `user.regs.rax` (or `user.regs.eax` for x86) + let rax_offset = offset_of!(libc::user, regs) + rax_offset; + ptrace::read_user(child, rax_offset as _).unwrap() as libc::c_long + }; + // kill entry ptrace::syscall(child, None).unwrap(); assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child))); assert_eq!(get_syscall_id(), ::libc::SYS_kill); + assert_eq!(get_syscall_from_user_area(), ::libc::SYS_kill); // kill exit ptrace::syscall(child, None).unwrap(); assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child))); assert_eq!(get_syscall_id(), ::libc::SYS_kill); + assert_eq!(get_syscall_from_user_area(), ::libc::SYS_kill); // receive signal ptrace::syscall(child, None).unwrap();