diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c4847c7da..e08a97ae32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,7 @@ This project adheres to [Semantic Versioning](https://semver.org/). - Added `Ipv6DontFrag` for android, iOS, linux and macOS. - Added `IpDontFrag` for iOS, macOS. (#[1692](https://github.com/nix-rust/nix/pull/1692)) +- Added `ptrace::read_user` and `ptrace::write_user` for Linux. ### 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..8d1988e80b 100644 --- a/test/sys/test_ptrace.rs +++ b/test/sys/test_ptrace.rs @@ -197,15 +197,36 @@ 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 get_rax_offset = |user_struct_ptr: *const libc::user| { + unsafe { &(*user_struct_ptr).regs.orig_rax as *const _ } + }; + #[cfg(target_arch = "x86")] + let get_rax_offset = |user_struct_ptr: *const libc::user| { + unsafe { &(*user_struct_ptr).regs.orig_eax as *const _ } + }; + + let get_syscall_from_user_area = || { + // Find the offset of `user.regs.rax` (or `eax` for x86) + let user_struct = std::mem::MaybeUninit::::uninit(); + let user_struct_ptr = user_struct.as_ptr(); + let rax_offset = get_rax_offset(user_struct_ptr) as usize - user_struct_ptr as usize; + + 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();