Skip to content

Commit b50cfae

Browse files
committed
PTRACE_GETREGSET and PTRACE_SETREGSET basic implementation on aarch64
1 parent 256707e commit b50cfae

File tree

2 files changed

+129
-2
lines changed

2 files changed

+129
-2
lines changed

src/sys/ptrace/linux.rs

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ pub type AddressType = *mut ::libc::c_void;
1414
target_os = "linux",
1515
any(all(target_arch = "x86_64",
1616
any(target_env = "gnu", target_env = "musl")),
17-
all(target_arch = "x86", target_env = "gnu"))
18-
))]
17+
all(target_arch = "x86", target_env = "gnu"),
18+
all(target_arch = "aarch64", target_os = "linux"),
19+
)))]
1920
use libc::user_regs_struct;
2021

2122
cfg_if! {
@@ -481,3 +482,88 @@ pub unsafe fn write(
481482
{
482483
ptrace_other(Request::PTRACE_POKEDATA, pid, addr, data).map(drop)
483484
}
485+
486+
/// Read the tracee's registers.
487+
///
488+
/// as with `ptrace(PTRACE_GETREGSET, ...)`
489+
///
490+
/// # Arguments
491+
///
492+
/// * `pid` - tracee's `nix::unistd::Pid`
493+
#[cfg(any(all(target_os = "linux", target_env = "gnu", target_arch = "aarch64")))]
494+
pub fn getregset(pid: Pid) -> Result<user_regs_struct> {
495+
ptrace_get_iovec_data::<user_regs_struct>(
496+
Request::PTRACE_GETREGSET,
497+
pid,
498+
libc::NT_PRSTATUS,
499+
)
500+
}
501+
502+
/// Modify the tracee's registers.
503+
///
504+
/// as with `ptrace(PTRACE_SETREGSET, ...)`
505+
///
506+
/// # Arguments
507+
///
508+
/// * `pid` - tracee's `nix::unistd::Pid`
509+
///
510+
/// * `regs` - `libc::user_regs_struct` to set
511+
///
512+
#[cfg(any(all(target_os = "linux", target_env = "gnu", target_arch = "aarch64")))]
513+
pub fn setregset(pid: Pid, regs: user_regs_struct) -> Result<()> {
514+
ptrace_set_iovec_data(
515+
Request::PTRACE_SETREGSET,
516+
pid,
517+
libc::NT_PRSTATUS,
518+
regs,
519+
)
520+
}
521+
522+
/// As with `ptrace_get_data` but with an `iovec`
523+
#[cfg(any(all(target_os = "linux", target_env = "gnu", target_arch = "aarch64")))]
524+
fn ptrace_get_iovec_data<T>(
525+
request: Request,
526+
pid: Pid,
527+
nt_req: libc::c_int,
528+
) -> Result<T> {
529+
let mut data = mem::MaybeUninit::<T>::uninit();
530+
let mut iov = libc::iovec {
531+
iov_base: data.as_mut_ptr() as *mut c_void,
532+
iov_len: mem::size_of::<T>(),
533+
};
534+
535+
let res = unsafe {
536+
libc::ptrace(
537+
request as RequestType,
538+
pid,
539+
nt_req as AddressType,
540+
&mut iov as *mut _ as *mut c_void,
541+
)
542+
};
543+
544+
Errno::result(res)?;
545+
Ok(unsafe { data.assume_init() })
546+
}
547+
548+
#[cfg(any(all(target_os = "linux", target_env = "gnu", target_arch = "aarch64")))]
549+
fn ptrace_set_iovec_data<T>(
550+
request: Request,
551+
pid: Pid,
552+
nt_req: libc::c_int,
553+
data: T,
554+
) -> Result<()> {
555+
let iov = libc::iovec {
556+
iov_base: &data as *const _ as *mut c_void,
557+
iov_len: mem::size_of::<T>(),
558+
};
559+
560+
unsafe {
561+
ptrace_other(
562+
request,
563+
pid,
564+
nt_req as AddressType,
565+
&iov as *const _ as *mut c_void,
566+
)
567+
.map(drop)
568+
}
569+
}

test/sys/test_ptrace.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,3 +217,44 @@ fn test_ptrace_syscall() {
217217
},
218218
}
219219
}
220+
221+
#[cfg(any(all(target_os = "linux",
222+
target_arch = "aarch64", target_env = "gnu")))]
223+
#[test]
224+
fn test_ptrace_regsets() {
225+
use nix::sys::signal::*;
226+
use nix::sys::wait::{waitpid, WaitStatus};
227+
use nix::unistd::fork;
228+
use nix::sys::ptrace::{self, getregset, setregset};
229+
use nix::unistd::ForkResult::*;
230+
231+
require_capability!("test_ptrace_regsets", CAP_SYS_PTRACE);
232+
233+
let _m = crate::FORK_MTX.lock();
234+
235+
match unsafe{fork()}.expect("Error: Fork Failed") {
236+
Child => {
237+
ptrace::traceme().unwrap();
238+
// As recommended by ptrace(2), raise SIGTRAP to pause the child
239+
// until the parent is ready to continue
240+
loop {
241+
raise(Signal::SIGTRAP).unwrap();
242+
}
243+
}
244+
245+
Parent { child } => {
246+
assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, Signal::SIGTRAP)));
247+
let mut regstruct = getregset(child).unwrap();
248+
regstruct.regs[16] = 0xdeadbeef;
249+
let _ = setregset(child, regstruct);
250+
assert_eq!(0xdeadbeef, getregset(child).unwrap().regs[16]);
251+
ptrace::cont(child, Some(Signal::SIGKILL)).unwrap();
252+
match waitpid(child, None) {
253+
Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _)) if pid == child => {
254+
255+
}
256+
_ => panic!("The process should have been killed"),
257+
}
258+
},
259+
}
260+
}

0 commit comments

Comments
 (0)