Skip to content

Commit 6408fc2

Browse files
committed
Add PTRACE_SET_SYSCALL_INFO and set_syscall_info wrapper
1 parent 0dc1dd8 commit 6408fc2

File tree

3 files changed

+88
-2
lines changed

3 files changed

+88
-2
lines changed

changelog/2739.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added set_syscall_info to ptrace on linux

src/sys/ptrace/linux.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ libc_enum! {
148148
PTRACE_SYSEMU_SINGLESTEP,
149149
#[cfg(all(target_os = "linux", target_env = "gnu"))]
150150
PTRACE_GET_SYSCALL_INFO,
151+
#[cfg(all(target_os = "linux", target_env = "gnu"))]
152+
PTRACE_SET_SYSCALL_INFO,
151153
}
152154
}
153155

@@ -576,6 +578,21 @@ pub fn syscall_info(pid: Pid) -> Result<libc::ptrace_syscall_info> {
576578
ptrace_get_data::<libc::ptrace_syscall_info>(Request::PTRACE_GET_SYSCALL_INFO, pid)
577579
}
578580

581+
/// Set the information of the syscall that caused the stop, as with
582+
/// `ptrace(PTRACE_SET_SYSCALL_INFO, ...`.
583+
#[cfg(all(target_os = "linux", target_env = "gnu"))]
584+
pub fn set_syscall_info(pid: Pid, syscall_info: &libc::ptrace_syscall_info) -> Result<()> {
585+
let res = unsafe {
586+
libc::ptrace(
587+
Request::PTRACE_SET_SYSCALL_INFO as RequestType,
588+
libc::pid_t::from(pid),
589+
mem::size_of::<libc::ptrace_syscall_info>(),
590+
syscall_info as *const _ as *const c_void,
591+
)
592+
};
593+
Errno::result(res).map(drop)
594+
}
595+
579596
/// Sets the process as traceable, as with `ptrace(PTRACE_TRACEME, ...)`
580597
///
581598
/// Indicates that this process is to be traced by its parent.

test/sys/test_ptrace.rs

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ use nix::unistd::getpid;
1313
#[cfg(linux_android)]
1414
use std::mem;
1515

16-
use crate::*;
17-
1816
#[test]
1917
fn test_ptrace() {
2018
// Just make sure ptrace can be called at all, for now.
@@ -410,3 +408,73 @@ fn test_ptrace_syscall_info() {
410408
},
411409
}
412410
}
411+
412+
#[cfg(all(target_os = "linux", target_env = "gnu"))]
413+
#[test]
414+
fn test_ptrace_set_syscall_info() {
415+
use nix::sys::mman::{mmap_anonymous, MapFlags, ProtFlags};
416+
use nix::sys::ptrace;
417+
use nix::sys::signal::{raise, Signal::*};
418+
use nix::sys::wait::{waitpid, WaitStatus};
419+
use nix::unistd::{fork, ForkResult::*};
420+
use std::num::NonZero;
421+
422+
require_capability!("test_ptrace_set_syscall_info", CAP_SYS_PTRACE);
423+
424+
let _m = crate::FORK_MTX.lock();
425+
match unsafe { fork() }.expect("Error: Fork Failed") {
426+
Child => {
427+
ptrace::traceme().unwrap();
428+
raise(SIGSTOP).unwrap();
429+
430+
unsafe {
431+
let my_memory = mmap_anonymous(
432+
None,
433+
NonZero::new(1).unwrap(),
434+
ProtFlags::PROT_WRITE,
435+
MapFlags::MAP_PRIVATE,
436+
)
437+
.unwrap_or_else(|_| ::libc::_exit(1))
438+
.cast::<u8>();
439+
440+
*my_memory.as_ptr() = 42;
441+
::libc::_exit(0);
442+
}
443+
}
444+
Parent { child } => {
445+
assert_eq!(
446+
waitpid(child, None),
447+
Ok(WaitStatus::Stopped(child, SIGSTOP))
448+
);
449+
ptrace::setoptions(child, Options::PTRACE_O_TRACESYSGOOD).unwrap();
450+
ptrace::syscall(child, None).unwrap();
451+
452+
// Hijack the syscall and remove PROT_WRITE to force a SEGFAULT in the child
453+
assert_eq!(
454+
waitpid(child, None),
455+
Ok(WaitStatus::PtraceSyscall(child))
456+
);
457+
let mut si = ptrace::syscall_info(child).unwrap();
458+
assert_eq!(si.op, libc::PTRACE_SYSCALL_INFO_ENTRY);
459+
unsafe {
460+
si.u.entry.args[2] = ProtFlags::PROT_NONE.bits() as u64;
461+
}
462+
let set_syscall_res = ptrace::set_syscall_info(child, &si);
463+
ptrace::cont(child, None).unwrap();
464+
465+
if set_syscall_res.is_err() {
466+
assert_eq!(
467+
waitpid(child, None),
468+
Ok(WaitStatus::Exited(child, 0))
469+
);
470+
crate::skip!("PTRACE_SET_SYSCALL_INFO failed: Linux >= 6.16 is required, skipping test.");
471+
}
472+
473+
assert_eq!(
474+
waitpid(child, None),
475+
Ok(WaitStatus::Stopped(child, SIGSEGV))
476+
);
477+
ptrace::detach(child, SIGSEGV).unwrap();
478+
}
479+
}
480+
}

0 commit comments

Comments
 (0)