Skip to content

Commit

Permalink
progress on strace-rs
Browse files Browse the repository at this point in the history
  • Loading branch information
thundergolfer committed May 20, 2024
1 parent 733ab41 commit 89905c9
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 23 deletions.
23 changes: 21 additions & 2 deletions operating_systems/linux/strace_rs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions operating_systems/linux/strace_rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ edition = "2021"
anyhow = "1.0.86"
bitflags = "2.5.0"
libc = "0.2.139"
nix = {version = "0.28.0", features = ["process", "ptrace"] }
num = "0.4.3"
num-derive = "0.4.2"
num-traits = "0.2.19"
Expand Down
73 changes: 66 additions & 7 deletions operating_systems/linux/strace_rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ extern crate num_derive;
#[macro_use]
extern crate bitflags;

use anyhow::{anyhow, bail, Result};
use libc;
use nix::{sys::wait::{WaitPidFlag, WaitStatus}, unistd::Pid};
use std::ffi::CString;
use std::io::Error;
use std::ptr;

use tracing::{debug, error};
pub mod ptrace;

unsafe fn do_child<T>(args: T) -> i32
unsafe fn do_child<T>(args: T) -> Result<()>
where
T: IntoIterator<Item = String>,
{
Expand All @@ -37,22 +39,79 @@ where
.collect();
envp.push(ptr::null());
let envp: *const *const libc::c_char = envp.as_ptr();

// If a child knows that it wants to be traced, it can make the PTRACE_TRACEME ptrace request,
// which starts tracing. In addition, it means that the next signal sent to this process will stop
// it and notify the parent (via wait), so that the parent knows to start tracing.
ptrace::traceme().map_err(|errno| anyhow!("failed TRACEME. errno {}", errno))?;
// After doing a TRACEME, we SIGSTOP ourselves so that the parent can continue this
// child's execution. This assures that the tracer does not miss the early syscalls made by
// the child.
let result = libc::raise(libc::SIGSTOP);
if result == -1 {
bail!("child failed to SIGSTOP itself. errno {}", Error::last_os_error());
}

libc::execve(child_prog, argv, envp);

// If execution continued to here there was an error.
let errno: i32 = Error::last_os_error().raw_os_error().unwrap();
error!("errno = {}", errno);
errno
bail!("errno = {}", errno)
}

fn wait_for_status(child: i32) -> Result<bool> {
loop {
let status = nix::sys::wait::waitpid(
Pid::from_raw(child),
Some(WaitPidFlag::WUNTRACED)
)?;
match status {
WaitStatus::Exited(_, code) => {
debug!("{} signalled exited. exit code: {:?}", child, code);
return Ok(true);
},
WaitStatus::Stopped(_, signal) => {
debug!("{} signalled stopped: {:?}", child, signal);
return Ok(false);
},
_ => {},
}
}
}

fn do_trace(child: i32) -> i32 {
fn do_trace(child: i32) -> Result<()> {
debug!(%child, "starting trace of child");
let _ = child;
std::thread::sleep(std::time::Duration::from_secs(2));
0
// Wait until child has sent itself the SIGSTOP above, and is ready to be traced.
if wait_for_status(child)? {
bail!("child unexpected exit during trace setup")
}

if let Err(errno) = ptrace::setoptions(child, ptrace::Options::SysGood) {
bail!("failed to ptrace child with PTRACE_O_TRACESYSGOOD. errno={}", errno);
}

loop {
if wait_for_status(child)? {
break;
}
let registers = ptrace::getregs(child)
.map_err(|errno| anyhow!("ptrace failed errno {}", errno))?;
let syscall = registers.orig_rax;
print!("syscall({}) = ", syscall);
if wait_for_status(child)? {
break;
}
let registers = ptrace::getregs(child)
.map_err(|errno| anyhow!("ptrace failed errno {}", errno))?;
let retval = registers.rax;
println!("{}\n", retval);
}

Ok(())
}

pub unsafe fn trace_command<T>(args: T) -> i32
pub unsafe fn trace_command<T>(args: T) -> Result<()>
where
T: IntoIterator<Item = String>,
{
Expand Down
6 changes: 3 additions & 3 deletions operating_systems/linux/strace_rs/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use std::process;

use anyhow::Result;
use tracing::Level;
use tracing_subscriber;

use strace_rs::trace_command;

fn main() {
fn main() -> Result<()> {
let mut args = std::env::args();
if args.len() < 2 {
eprintln!(
Expand All @@ -25,6 +26,5 @@ fn main() {
// and then we fork() to create two processes –
// one to execute the program to be traced, and
// the other to trace it.
let exit_code = unsafe { trace_command(args.into_iter()) };
process::exit(exit_code);
unsafe { trace_command(args.into_iter()) }
}
22 changes: 11 additions & 11 deletions operating_systems/linux/strace_rs/src/ptrace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ bitflags! {

pub fn setoptions(pid: libc::pid_t, opts: Options) -> Result<libc::c_long, i32> {
unsafe {
raw(
_ptrace(
Request::SetOptions,
pid,
ptr::null_mut(),
Expand All @@ -150,7 +150,7 @@ pub fn getregs(pid: libc::pid_t) -> Result<Registers, i32> {
let buf_mut: *mut Registers = &mut buf;

match unsafe {
raw(
_ptrace(
Request::GetRegs,
pid,
ptr::null_mut(),
Expand All @@ -165,21 +165,21 @@ pub fn getregs(pid: libc::pid_t) -> Result<Registers, i32> {
pub fn setregs(pid: libc::pid_t, regs: &Registers) -> Result<libc::c_long, i32> {
unsafe {
let buf: *mut libc::c_void = mem::transmute(regs);
raw(Request::SetRegs, pid, ptr::null_mut(), buf)
_ptrace(Request::SetRegs, pid, ptr::null_mut(), buf)
}
}

pub fn seize(pid: libc::pid_t) -> Result<libc::c_long, i32> {
unsafe { raw(Request::Seize, pid, ptr::null_mut(), ptr::null_mut()) }
unsafe { _ptrace(Request::Seize, pid, ptr::null_mut(), ptr::null_mut()) }
}

pub fn attach(pid: libc::pid_t) -> Result<libc::c_long, i32> {
unsafe { raw(Request::Attach, pid, ptr::null_mut(), ptr::null_mut()) }
unsafe { _ptrace(Request::Attach, pid, ptr::null_mut(), ptr::null_mut()) }
}

pub fn release(pid: libc::pid_t, signal: Signal) -> Result<libc::c_long, i32> {
unsafe {
raw(
_ptrace(
Request::Detatch,
pid,
ptr::null_mut(),
Expand All @@ -190,7 +190,7 @@ pub fn release(pid: libc::pid_t, signal: Signal) -> Result<libc::c_long, i32> {

pub fn cont(pid: libc::pid_t, signal: Signal) -> Result<libc::c_long, i32> {
unsafe {
raw(
_ptrace(
Request::Continue,
pid,
ptr::null_mut(),
Expand All @@ -200,10 +200,10 @@ pub fn cont(pid: libc::pid_t, signal: Signal) -> Result<libc::c_long, i32> {
}

pub fn traceme() -> Result<libc::c_long, i32> {
unsafe { raw(Request::TraceMe, 0, ptr::null_mut(), ptr::null_mut()) }
unsafe { _ptrace(Request::TraceMe, 0, ptr::null_mut(), ptr::null_mut()) }
}

unsafe fn raw(
unsafe fn _ptrace(
request: Request,
pid: libc::pid_t,
addr: *mut libc::c_void,
Expand Down Expand Up @@ -281,7 +281,7 @@ impl Writer {

pub fn poke_data(&self, address: Address, data: Word) -> Result<Word, i32> {
match unsafe {
raw(
_ptrace(
Request::PokeData,
self.pid,
address as *mut libc::c_void,
Expand Down Expand Up @@ -352,7 +352,7 @@ impl Reader {
pub fn peek_data(&self, address: Address) -> Result<Word, i32> {
let l;
unsafe {
l = raw(
l = _ptrace(
Request::PeekData,
self.pid,
address as *mut libc::c_void,
Expand Down

0 comments on commit 89905c9

Please sign in to comment.