1//! For detailed description of the ptrace requests, consult `man ptrace`. 2 3use crate::errno::Errno; 4use crate::sys::signal::Signal; 5use crate::unistd::Pid; 6use crate::Result; 7use cfg_if::cfg_if; 8use libc::{self, c_long, c_void, siginfo_t}; 9use std::{mem, ptr}; 10 11pub type AddressType = *mut ::libc::c_void; 12 13#[cfg(all( 14 target_os = "linux", 15 any( 16 all( 17 target_arch = "x86_64", 18 any(target_env = "gnu", target_env = "musl", target_env = "ohos") 19 ), 20 all(target_arch = "x86", target_env = "gnu") 21 ) 22))] 23use libc::user_regs_struct; 24 25cfg_if! { 26 if #[cfg(any(all(target_os = "linux", target_arch = "s390x"), 27 all(target_os = "linux", target_env = "gnu"), 28 target_env = "uclibc"))] { 29 #[doc(hidden)] 30 pub type RequestType = ::libc::c_uint; 31 } else { 32 #[doc(hidden)] 33 pub type RequestType = ::libc::c_int; 34 } 35} 36 37libc_enum! { 38 #[cfg_attr(not(any(target_env = "musl", target_env = "ohos", target_env = "uclibc", target_os = "android")), repr(u32))] 39 #[cfg_attr(any(target_env = "musl", target_env = "ohos", target_env = "uclibc", target_os = "android"), repr(i32))] 40 /// Ptrace Request enum defining the action to be taken. 41 #[non_exhaustive] 42 pub enum Request { 43 PTRACE_TRACEME, 44 PTRACE_PEEKTEXT, 45 PTRACE_PEEKDATA, 46 PTRACE_PEEKUSER, 47 PTRACE_POKETEXT, 48 PTRACE_POKEDATA, 49 PTRACE_POKEUSER, 50 PTRACE_CONT, 51 PTRACE_KILL, 52 PTRACE_SINGLESTEP, 53 #[cfg(any(all(target_os = "android", target_pointer_width = "32"), 54 all(target_os = "linux", any(target_env = "musl", 55 target_env = "ohos", 56 target_arch = "mips", 57 target_arch = "mips64", 58 target_arch = "x86_64", 59 target_pointer_width = "32"))))] 60 PTRACE_GETREGS, 61 #[cfg(any(all(target_os = "android", target_pointer_width = "32"), 62 all(target_os = "linux", any(target_env = "musl", 63 target_env = "ohos", 64 target_arch = "mips", 65 target_arch = "mips64", 66 target_arch = "x86_64", 67 target_pointer_width = "32"))))] 68 PTRACE_SETREGS, 69 #[cfg(any(all(target_os = "android", target_pointer_width = "32"), 70 all(target_os = "linux", any(target_env = "musl", 71 target_env = "ohos", 72 target_arch = "mips", 73 target_arch = "mips64", 74 target_arch = "x86_64", 75 target_pointer_width = "32"))))] 76 PTRACE_GETFPREGS, 77 #[cfg(any(all(target_os = "android", target_pointer_width = "32"), 78 all(target_os = "linux", any(target_env = "musl", 79 target_env = "ohos", 80 target_arch = "mips", 81 target_arch = "mips64", 82 target_arch = "x86_64", 83 target_pointer_width = "32"))))] 84 PTRACE_SETFPREGS, 85 PTRACE_ATTACH, 86 PTRACE_DETACH, 87 #[cfg(all(target_os = "linux", any(target_env = "musl", 88 target_env = "ohos", 89 target_arch = "mips", 90 target_arch = "mips64", 91 target_arch = "x86", 92 target_arch = "x86_64")))] 93 PTRACE_GETFPXREGS, 94 #[cfg(all(target_os = "linux", any(target_env = "musl", 95 target_env = "ohos", 96 target_arch = "mips", 97 target_arch = "mips64", 98 target_arch = "x86", 99 target_arch = "x86_64")))] 100 PTRACE_SETFPXREGS, 101 PTRACE_SYSCALL, 102 PTRACE_SETOPTIONS, 103 PTRACE_GETEVENTMSG, 104 PTRACE_GETSIGINFO, 105 PTRACE_SETSIGINFO, 106 #[cfg(all(target_os = "linux", not(any(target_arch = "mips", 107 target_arch = "mips64"))))] 108 PTRACE_GETREGSET, 109 #[cfg(all(target_os = "linux", not(any(target_arch = "mips", 110 target_arch = "mips64"))))] 111 PTRACE_SETREGSET, 112 #[cfg(target_os = "linux")] 113 #[cfg_attr(docsrs, doc(cfg(all())))] 114 PTRACE_SEIZE, 115 #[cfg(target_os = "linux")] 116 #[cfg_attr(docsrs, doc(cfg(all())))] 117 PTRACE_INTERRUPT, 118 #[cfg(all(target_os = "linux", not(any(target_arch = "mips", 119 target_arch = "mips64"))))] 120 PTRACE_LISTEN, 121 #[cfg(all(target_os = "linux", not(any(target_arch = "mips", 122 target_arch = "mips64"))))] 123 PTRACE_PEEKSIGINFO, 124 #[cfg(all(target_os = "linux", target_env = "gnu", 125 any(target_arch = "x86", target_arch = "x86_64")))] 126 PTRACE_SYSEMU, 127 #[cfg(all(target_os = "linux", target_env = "gnu", 128 any(target_arch = "x86", target_arch = "x86_64")))] 129 PTRACE_SYSEMU_SINGLESTEP, 130 } 131} 132 133libc_enum! { 134 #[repr(i32)] 135 /// Using the ptrace options the tracer can configure the tracee to stop 136 /// at certain events. This enum is used to define those events as defined 137 /// in `man ptrace`. 138 #[non_exhaustive] 139 pub enum Event { 140 /// Event that stops before a return from fork or clone. 141 PTRACE_EVENT_FORK, 142 /// Event that stops before a return from vfork or clone. 143 PTRACE_EVENT_VFORK, 144 /// Event that stops before a return from clone. 145 PTRACE_EVENT_CLONE, 146 /// Event that stops before a return from execve. 147 PTRACE_EVENT_EXEC, 148 /// Event for a return from vfork. 149 PTRACE_EVENT_VFORK_DONE, 150 /// Event for a stop before an exit. Unlike the waitpid Exit status program. 151 /// registers can still be examined 152 PTRACE_EVENT_EXIT, 153 /// Stop triggered by a seccomp rule on a tracee. 154 PTRACE_EVENT_SECCOMP, 155 /// Stop triggered by the `INTERRUPT` syscall, or a group stop, 156 /// or when a new child is attached. 157 PTRACE_EVENT_STOP, 158 } 159} 160 161libc_bitflags! { 162 /// Ptrace options used in conjunction with the PTRACE_SETOPTIONS request. 163 /// See `man ptrace` for more details. 164 pub struct Options: libc::c_int { 165 /// When delivering system call traps set a bit to allow tracer to 166 /// distinguish between normal stops or syscall stops. May not work on 167 /// all systems. 168 PTRACE_O_TRACESYSGOOD; 169 /// Stop tracee at next fork and start tracing the forked process. 170 PTRACE_O_TRACEFORK; 171 /// Stop tracee at next vfork call and trace the vforked process. 172 PTRACE_O_TRACEVFORK; 173 /// Stop tracee at next clone call and trace the cloned process. 174 PTRACE_O_TRACECLONE; 175 /// Stop tracee at next execve call. 176 PTRACE_O_TRACEEXEC; 177 /// Stop tracee at vfork completion. 178 PTRACE_O_TRACEVFORKDONE; 179 /// Stop tracee at next exit call. Stops before exit commences allowing 180 /// tracer to see location of exit and register states. 181 PTRACE_O_TRACEEXIT; 182 /// Stop tracee when a SECCOMP_RET_TRACE rule is triggered. See `man seccomp` for more 183 /// details. 184 PTRACE_O_TRACESECCOMP; 185 /// Send a SIGKILL to the tracee if the tracer exits. This is useful 186 /// for ptrace jailers to prevent tracees from escaping their control. 187 PTRACE_O_EXITKILL; 188 } 189} 190 191fn ptrace_peek( 192 request: Request, 193 pid: Pid, 194 addr: AddressType, 195 data: *mut c_void, 196) -> Result<c_long> { 197 let ret = unsafe { 198 Errno::clear(); 199 libc::ptrace(request as RequestType, libc::pid_t::from(pid), addr, data) 200 }; 201 match Errno::result(ret) { 202 Ok(..) | Err(Errno::UnknownErrno) => Ok(ret), 203 err @ Err(..) => err, 204 } 205} 206 207/// Get user registers, as with `ptrace(PTRACE_GETREGS, ...)` 208#[cfg(all( 209 target_os = "linux", 210 any( 211 all( 212 target_arch = "x86_64", 213 any(target_env = "gnu", target_env = "musl", target_env = "ohos") 214 ), 215 all(target_arch = "x86", target_env = "gnu") 216 ) 217))] 218pub fn getregs(pid: Pid) -> Result<user_regs_struct> { 219 ptrace_get_data::<user_regs_struct>(Request::PTRACE_GETREGS, pid) 220} 221 222/// Set user registers, as with `ptrace(PTRACE_SETREGS, ...)` 223#[cfg(all( 224 target_os = "linux", 225 any( 226 all( 227 target_arch = "x86_64", 228 any(target_env = "gnu", target_env = "musl", target_env = "ohos") 229 ), 230 all(target_arch = "x86", target_env = "gnu") 231 ) 232))] 233pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> { 234 let res = unsafe { 235 libc::ptrace( 236 Request::PTRACE_SETREGS as RequestType, 237 libc::pid_t::from(pid), 238 ptr::null_mut::<c_void>(), 239 ®s as *const _ as *const c_void, 240 ) 241 }; 242 Errno::result(res).map(drop) 243} 244 245/// Function for ptrace requests that return values from the data field. 246/// Some ptrace get requests populate structs or larger elements than `c_long` 247/// and therefore use the data field to return values. This function handles these 248/// requests. 249fn ptrace_get_data<T>(request: Request, pid: Pid) -> Result<T> { 250 let mut data = mem::MaybeUninit::uninit(); 251 let res = unsafe { 252 libc::ptrace( 253 request as RequestType, 254 libc::pid_t::from(pid), 255 ptr::null_mut::<T>(), 256 data.as_mut_ptr() as *const _ as *const c_void, 257 ) 258 }; 259 Errno::result(res)?; 260 Ok(unsafe { data.assume_init() }) 261} 262 263unsafe fn ptrace_other( 264 request: Request, 265 pid: Pid, 266 addr: AddressType, 267 data: *mut c_void, 268) -> Result<c_long> { 269 Errno::result(libc::ptrace( 270 request as RequestType, 271 libc::pid_t::from(pid), 272 addr, 273 data, 274 )) 275 .map(|_| 0) 276} 277 278/// Set options, as with `ptrace(PTRACE_SETOPTIONS,...)`. 279pub fn setoptions(pid: Pid, options: Options) -> Result<()> { 280 let res = unsafe { 281 libc::ptrace( 282 Request::PTRACE_SETOPTIONS as RequestType, 283 libc::pid_t::from(pid), 284 ptr::null_mut::<c_void>(), 285 options.bits() as *mut c_void, 286 ) 287 }; 288 Errno::result(res).map(drop) 289} 290 291/// Gets a ptrace event as described by `ptrace(PTRACE_GETEVENTMSG,...)` 292pub fn getevent(pid: Pid) -> Result<c_long> { 293 ptrace_get_data::<c_long>(Request::PTRACE_GETEVENTMSG, pid) 294} 295 296/// Get siginfo as with `ptrace(PTRACE_GETSIGINFO,...)` 297pub fn getsiginfo(pid: Pid) -> Result<siginfo_t> { 298 ptrace_get_data::<siginfo_t>(Request::PTRACE_GETSIGINFO, pid) 299} 300 301/// Set siginfo as with `ptrace(PTRACE_SETSIGINFO,...)` 302pub fn setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> { 303 let ret = unsafe { 304 Errno::clear(); 305 libc::ptrace( 306 Request::PTRACE_SETSIGINFO as RequestType, 307 libc::pid_t::from(pid), 308 ptr::null_mut::<c_void>(), 309 sig as *const _ as *const c_void, 310 ) 311 }; 312 match Errno::result(ret) { 313 Ok(_) => Ok(()), 314 Err(e) => Err(e), 315 } 316} 317 318/// Sets the process as traceable, as with `ptrace(PTRACE_TRACEME, ...)` 319/// 320/// Indicates that this process is to be traced by its parent. 321/// This is the only ptrace request to be issued by the tracee. 322pub fn traceme() -> Result<()> { 323 unsafe { 324 ptrace_other( 325 Request::PTRACE_TRACEME, 326 Pid::from_raw(0), 327 ptr::null_mut(), 328 ptr::null_mut(), 329 ) 330 .map(drop) // ignore the useless return value 331 } 332} 333 334/// Continue execution until the next syscall, as with `ptrace(PTRACE_SYSCALL, ...)` 335/// 336/// Arranges for the tracee to be stopped at the next entry to or exit from a system call, 337/// optionally delivering a signal specified by `sig`. 338pub fn syscall<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> { 339 let data = match sig.into() { 340 Some(s) => s as i32 as *mut c_void, 341 None => ptr::null_mut(), 342 }; 343 unsafe { 344 ptrace_other(Request::PTRACE_SYSCALL, pid, ptr::null_mut(), data) 345 .map(drop) // ignore the useless return value 346 } 347} 348 349/// Continue execution until the next syscall, as with `ptrace(PTRACE_SYSEMU, ...)` 350/// 351/// In contrast to the `syscall` function, the syscall stopped at will not be executed. 352/// Thus the the tracee will only be stopped once per syscall, 353/// optionally delivering a signal specified by `sig`. 354#[cfg(all( 355 target_os = "linux", 356 target_env = "gnu", 357 any(target_arch = "x86", target_arch = "x86_64") 358))] 359pub fn sysemu<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> { 360 let data = match sig.into() { 361 Some(s) => s as i32 as *mut c_void, 362 None => ptr::null_mut(), 363 }; 364 unsafe { 365 ptrace_other(Request::PTRACE_SYSEMU, pid, ptr::null_mut(), data) 366 .map(drop) 367 // ignore the useless return value 368 } 369} 370 371/// Attach to a running process, as with `ptrace(PTRACE_ATTACH, ...)` 372/// 373/// Attaches to the process specified by `pid`, making it a tracee of the calling process. 374pub fn attach(pid: Pid) -> Result<()> { 375 unsafe { 376 ptrace_other( 377 Request::PTRACE_ATTACH, 378 pid, 379 ptr::null_mut(), 380 ptr::null_mut(), 381 ) 382 .map(drop) // ignore the useless return value 383 } 384} 385 386/// Attach to a running process, as with `ptrace(PTRACE_SEIZE, ...)` 387/// 388/// Attaches to the process specified in pid, making it a tracee of the calling process. 389#[cfg(target_os = "linux")] 390#[cfg_attr(docsrs, doc(cfg(all())))] 391pub fn seize(pid: Pid, options: Options) -> Result<()> { 392 unsafe { 393 ptrace_other( 394 Request::PTRACE_SEIZE, 395 pid, 396 ptr::null_mut(), 397 options.bits() as *mut c_void, 398 ) 399 .map(drop) // ignore the useless return value 400 } 401} 402 403/// Detaches the current running process, as with `ptrace(PTRACE_DETACH, ...)` 404/// 405/// Detaches from the process specified by `pid` allowing it to run freely, optionally delivering a 406/// signal specified by `sig`. 407pub fn detach<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> { 408 let data = match sig.into() { 409 Some(s) => s as i32 as *mut c_void, 410 None => ptr::null_mut(), 411 }; 412 unsafe { 413 ptrace_other(Request::PTRACE_DETACH, pid, ptr::null_mut(), data) 414 .map(drop) 415 } 416} 417 418/// Restart the stopped tracee process, as with `ptrace(PTRACE_CONT, ...)` 419/// 420/// Continues the execution of the process with PID `pid`, optionally 421/// delivering a signal specified by `sig`. 422pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> { 423 let data = match sig.into() { 424 Some(s) => s as i32 as *mut c_void, 425 None => ptr::null_mut(), 426 }; 427 unsafe { 428 ptrace_other(Request::PTRACE_CONT, pid, ptr::null_mut(), data).map(drop) 429 // ignore the useless return value 430 } 431} 432 433/// Stop a tracee, as with `ptrace(PTRACE_INTERRUPT, ...)` 434/// 435/// This request is equivalent to `ptrace(PTRACE_INTERRUPT, ...)` 436#[cfg(target_os = "linux")] 437#[cfg_attr(docsrs, doc(cfg(all())))] 438pub fn interrupt(pid: Pid) -> Result<()> { 439 unsafe { 440 ptrace_other( 441 Request::PTRACE_INTERRUPT, 442 pid, 443 ptr::null_mut(), 444 ptr::null_mut(), 445 ) 446 .map(drop) 447 } 448} 449 450/// Issues a kill request as with `ptrace(PTRACE_KILL, ...)` 451/// 452/// This request is equivalent to `ptrace(PTRACE_CONT, ..., SIGKILL);` 453pub fn kill(pid: Pid) -> Result<()> { 454 unsafe { 455 ptrace_other( 456 Request::PTRACE_KILL, 457 pid, 458 ptr::null_mut(), 459 ptr::null_mut(), 460 ) 461 .map(drop) 462 } 463} 464 465/// Move the stopped tracee process forward by a single step as with 466/// `ptrace(PTRACE_SINGLESTEP, ...)` 467/// 468/// Advances the execution of the process with PID `pid` by a single step optionally delivering a 469/// signal specified by `sig`. 470/// 471/// # Example 472/// ```rust 473/// use nix::sys::ptrace::step; 474/// use nix::unistd::Pid; 475/// use nix::sys::signal::Signal; 476/// use nix::sys::wait::*; 477/// 478/// // If a process changes state to the stopped state because of a SIGUSR1 479/// // signal, this will step the process forward and forward the user 480/// // signal to the stopped process 481/// match waitpid(Pid::from_raw(-1), None) { 482/// Ok(WaitStatus::Stopped(pid, Signal::SIGUSR1)) => { 483/// let _ = step(pid, Signal::SIGUSR1); 484/// } 485/// _ => {}, 486/// } 487/// ``` 488pub fn step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> { 489 let data = match sig.into() { 490 Some(s) => s as i32 as *mut c_void, 491 None => ptr::null_mut(), 492 }; 493 unsafe { 494 ptrace_other(Request::PTRACE_SINGLESTEP, pid, ptr::null_mut(), data) 495 .map(drop) 496 } 497} 498 499/// Move the stopped tracee process forward by a single step or stop at the next syscall 500/// as with `ptrace(PTRACE_SYSEMU_SINGLESTEP, ...)` 501/// 502/// Advances the execution by a single step or until the next syscall. 503/// In case the tracee is stopped at a syscall, the syscall will not be executed. 504/// Optionally, the signal specified by `sig` is delivered to the tracee upon continuation. 505#[cfg(all( 506 target_os = "linux", 507 target_env = "gnu", 508 any(target_arch = "x86", target_arch = "x86_64") 509))] 510pub fn sysemu_step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> { 511 let data = match sig.into() { 512 Some(s) => s as i32 as *mut c_void, 513 None => ptr::null_mut(), 514 }; 515 unsafe { 516 ptrace_other( 517 Request::PTRACE_SYSEMU_SINGLESTEP, 518 pid, 519 ptr::null_mut(), 520 data, 521 ) 522 .map(drop) // ignore the useless return value 523 } 524} 525 526/// Reads a word from a processes memory at the given address 527pub fn read(pid: Pid, addr: AddressType) -> Result<c_long> { 528 ptrace_peek(Request::PTRACE_PEEKDATA, pid, addr, ptr::null_mut()) 529} 530 531/// Writes a word into the processes memory at the given address 532/// 533/// # Safety 534/// 535/// The `data` argument is passed directly to `ptrace(2)`. Read that man page 536/// for guidance. 537pub unsafe fn write( 538 pid: Pid, 539 addr: AddressType, 540 data: *mut c_void, 541) -> Result<()> { 542 ptrace_other(Request::PTRACE_POKEDATA, pid, addr, data).map(drop) 543} 544 545/// Reads a word from a user area at `offset`. 546/// The user struct definition can be found in `/usr/include/sys/user.h`. 547pub fn read_user(pid: Pid, offset: AddressType) -> Result<c_long> { 548 ptrace_peek(Request::PTRACE_PEEKUSER, pid, offset, ptr::null_mut()) 549} 550 551/// Writes a word to a user area at `offset`. 552/// The user struct definition can be found in `/usr/include/sys/user.h`. 553/// 554/// # Safety 555/// 556/// The `data` argument is passed directly to `ptrace(2)`. Read that man page 557/// for guidance. 558pub unsafe fn write_user( 559 pid: Pid, 560 offset: AddressType, 561 data: *mut c_void, 562) -> Result<()> { 563 ptrace_other(Request::PTRACE_POKEUSER, pid, offset, data).map(drop) 564} 565