From 6620faa0e1757de2e71e0dc77e3a626ad96a428a Mon Sep 17 00:00:00 2001 From: Nikita Baksalyar Date: Thu, 14 Apr 2022 01:05:30 +0100 Subject: [PATCH] Add ptrace::read_user and ptrace::write_user --- CHANGELOG.md | 2 ++ src/sys/ptrace/linux.rs | 21 +++++++++++++++++++++ test/sys/test_ptrace.rs | 21 +++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cb9d2aec5..1a93f3d077 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -104,6 +104,8 @@ 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. + (#[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..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();