ptrace used to single-step child program

This commit is contained in:
Elnath 2025-04-18 19:01:26 +02:00
parent 0528a6af22
commit 775d909585
2 changed files with 52 additions and 22 deletions

15
src/child.rs Normal file
View File

@ -0,0 +1,15 @@
use nix::libc;
use nix::sys::ptrace::traceme;
use nix::unistd::execv;
use std::ffi::CString;
pub fn starti(child_exec_path: CString) -> ! {
// ⚠️ There is a limited amount of things that can be done here, see fork's safety
match traceme() {
Ok(_) => {
execv(&child_exec_path, &[&child_exec_path]).unwrap();
unreachable!();
}
Err(errno) => unsafe { libc::exit(errno as i32) },
}
}

View File

@ -1,33 +1,48 @@
use nix::unistd::{execv, fork, ForkResult}; mod child;
use nix::sys::ptrace::*;
use nix::sys::wait::waitpid;
use nix::unistd::{ForkResult, fork};
use std::ffi::CString; use std::ffi::CString;
use std::process::ExitCode; use std::process::ExitCode;
use nix::sys::wait::{waitid, WaitPidFlag, WaitStatus};
fn main() -> ExitCode { fn main() -> ExitCode {
let child_exec_path = CString::new(env!("TEST_PROG_PATH")).unwrap(); let child_exec_path = CString::new(env!("TEST_PROG_PATH")).unwrap();
match unsafe { fork() } { match unsafe { fork() } {
Ok(ForkResult::Child) => { Ok(ForkResult::Child) => child::starti(child_exec_path),
// ⚠️ There is a limited amount of things that can be done here, see fork's safety
execv(&child_exec_path, &[&child_exec_path]).unwrap();
unreachable!();
}
Ok(ForkResult::Parent { child: child_pid }) => { Ok(ForkResult::Parent { child: child_pid }) => {
println!("✔️ Started child {child_pid}"); println!("✔️ Started child {child_pid}");
println!("⏳️ Waiting for child to exit..."); println!("⚙️ Waiting for child signals...");
use nix::sys::wait::Id::Pid;
match waitid(Pid(child_pid), WaitPidFlag::WEXITED) { loop {
use nix::sys::signal::Signal::*;
use nix::sys::wait::WaitStatus::*;
match waitpid(child_pid, None) {
Err(e) => { Err(e) => {
println!("❌ Wait failed: {e}"); println!("❌ Wait failed: {e}");
ExitCode::FAILURE return ExitCode::FAILURE;
} }
Ok(WaitStatus::Exited(_pid, exit_code)) => { Ok(status) => match status {
println!("👋 child exited with code {exit_code}"); Exited(_pid, exit_code) => {
ExitCode::SUCCESS println!("👋 Child exited with code {exit_code}");
return ExitCode::SUCCESS;
} }
Ok(status) => { Signaled(_, signal, _) => match signal {
println!("⚠️ Unexpected status from wait! {status:#?}"); SIGKILL => {
ExitCode::FAILURE println!("💀 Child killed by SIGKILL");
return ExitCode::SUCCESS;
}
s => println!("💡 Child received signal {s:?}"),
},
Stopped(_pid, _signal) => {
println!("⚙️ Single-stepping child");
step(child_pid, None).unwrap()
}
status => {
println!("⚠️ Other (unexpected) wait status: {status:?}");
}
},
} }
} }
} }