From 54eaee06413c60434ffb77e65aee8b9d9a09d8ab Mon Sep 17 00:00:00 2001 From: edef Date: Mon, 27 Dec 2021 11:57:33 +0000 Subject: ripple/minitrace: init Minimal PTRACE_SYSCALL tracer for further narrowing down the syscalls we need to support. Change-Id: I562ee6e88e52d7deeee6de588ef00dfc1c38a71a --- ripple/Cargo.lock | 42 +++++++++++++ ripple/Cargo.toml | 2 +- ripple/minitrace/.gitignore | 4 ++ ripple/minitrace/Cargo.toml | 12 ++++ ripple/minitrace/src/main.rs | 147 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 ripple/minitrace/.gitignore create mode 100644 ripple/minitrace/Cargo.toml create mode 100644 ripple/minitrace/src/main.rs (limited to 'ripple') diff --git a/ripple/Cargo.lock b/ripple/Cargo.lock index fdab2f2..994b4bc 100644 --- a/ripple/Cargo.lock +++ b/ripple/Cargo.lock @@ -311,12 +311,45 @@ dependencies = [ "autocfg", ] +[[package]] +name = "minitrace" +version = "0.0.0" +dependencies = [ + "anyhow", + "nix 0.19.1", + "spawn-ptrace", +] + [[package]] name = "multimap" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +[[package]] +name = "nix" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055" +dependencies = [ + "bitflags", + "cc", + "cfg-if 0.1.10", + "libc", +] + +[[package]] +name = "nix" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ccba0cfe4fdf15982d1674c69b1fd80bad427d293849982668dfe454bd61f2" +dependencies = [ + "bitflags", + "cc", + "cfg-if 1.0.0", + "libc", +] + [[package]] name = "num_cpus" version = "1.13.0" @@ -552,6 +585,15 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" +[[package]] +name = "spawn-ptrace" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b572503c81aa40e1e61954bca89b863c344e0f310fd51f308d933255bf4f756" +dependencies = [ + "nix 0.18.0", +] + [[package]] name = "subtle" version = "2.4.1" diff --git a/ripple/Cargo.toml b/ripple/Cargo.toml index 5255ca8..ffc8527 100644 --- a/ripple/Cargo.toml +++ b/ripple/Cargo.toml @@ -9,4 +9,4 @@ edition = "2018" [dependencies] [workspace] -members = ["fossil"] +members = ["fossil", "minitrace"] diff --git a/ripple/minitrace/.gitignore b/ripple/minitrace/.gitignore new file mode 100644 index 0000000..be75022 --- /dev/null +++ b/ripple/minitrace/.gitignore @@ -0,0 +1,4 @@ +# SPDX-FileCopyrightText: edef +# SPDX-License-Identifier: OSL-3.0 + +/target diff --git a/ripple/minitrace/Cargo.toml b/ripple/minitrace/Cargo.toml new file mode 100644 index 0000000..4a64930 --- /dev/null +++ b/ripple/minitrace/Cargo.toml @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: edef +# SPDX-License-Identifier: OSL-3.0 + +[package] +name = "minitrace" +version = "0.0.0" +edition = "2018" + +[dependencies] +nix = "0.19.1" +spawn-ptrace = "0.1.2" +anyhow = "1.0.43" diff --git a/ripple/minitrace/src/main.rs b/ripple/minitrace/src/main.rs new file mode 100644 index 0000000..362be9c --- /dev/null +++ b/ripple/minitrace/src/main.rs @@ -0,0 +1,147 @@ +// SPDX-FileCopyrightText: edef +// SPDX-License-Identifier: OSL-3.0 + +use { + nix::{ + libc, + sys::{ + ptrace, + wait::{waitpid, WaitPidFlag, WaitStatus}, + }, + unistd::Pid, + }, + spawn_ptrace::CommandPtraceSpawn, + std::{env, io, process::Command}, +}; + +// TODO(edef): consider implementing this in terms of TID? +// tgids are a strict subset of tids +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct Tgid(pub libc::pid_t); + +impl Tgid { + fn as_pid(&self) -> Pid { + Pid::from_raw(self.0) + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct Tid(pub libc::pid_t); + +impl Tid { + fn as_pid(&self) -> Pid { + Pid::from_raw(self.0) + } +} + +#[derive(Debug)] +struct Process { + tgid: Tgid, +} + +impl Process { + fn spawn(cmd: &mut Command) -> io::Result { + let child = cmd.spawn_ptrace()?; + + // the thread group leader's TID is equal to the TGID + let tgid = Tgid(child.id() as _); + + Ok(Process { tgid }) + } +} + +#[derive(Debug, Copy, Clone)] +struct SyscallEntry { + number: u64, + // rdi, rsi, rdx, rcx, r8, r9 + args: [u64; 6], +} + +impl SyscallEntry { + fn from_regs(regs: libc::user_regs_struct) -> SyscallEntry { + SyscallEntry { + number: regs.orig_rax, + args: [regs.rdi, regs.rsi, regs.rdx, regs.rcx, regs.r8, regs.r9], + } + } +} + +#[derive(Debug, Copy, Clone)] +enum EntryExit { + /// Process is about to enter a syscall + Entry(SyscallEntry), + /// Process is about to exit a syscall + Exit(SyscallEntry, i64), +} + +fn main() -> anyhow::Result<()> { + let process = Process::spawn(&mut { + let mut args = env::args(); + + // drop argv[0] + args.next(); + + let mut cmd = Command::new(args.next().unwrap()); + for arg in args { + cmd.arg(arg); + } + + cmd + })?; + + let options = ptrace::Options::PTRACE_O_TRACESYSGOOD | ptrace::Options::PTRACE_O_TRACECLONE; + ptrace::setoptions(process.tgid.as_pid(), options)?; + + // this is always equal to tgid for now, + // but I'm keeping this separate so it's obvious what has to be tgid + let tid = Tid(process.tgid.0); + + let mut syscall_state: Option = None; + + loop { + ptrace::syscall(tid.as_pid(), None)?; + if let Some(EntryExit::Exit(..)) = syscall_state { + // syscall has completed now + syscall_state = None; + } + + let status = waitpid(tid.as_pid(), Some(WaitPidFlag::__WALL))?; + println!("{:?}", status); + + match (syscall_state, status) { + (None, WaitStatus::PtraceSyscall(event_tid)) => { + let event_tid = Tid(event_tid.as_raw()); + assert_eq!(tid, event_tid); + + let regs = ptrace::getregs(event_tid.as_pid())?; + let entry = SyscallEntry::from_regs(regs); + + syscall_state = Some(EntryExit::Entry(entry)); + println!("entry: {:?}", regs); + } + (Some(EntryExit::Entry(entry)), WaitStatus::PtraceSyscall(event_tid)) => { + let event_tid = Tid(event_tid.as_raw()); + assert_eq!(tid, event_tid); + + let regs = ptrace::getregs(event_tid.as_pid())?; + let ret = regs.rax as i64; + syscall_state = Some(EntryExit::Exit(entry, ret)); + + println!("syscall returned {:?} with {:?}", ret, regs); + } + (_, WaitStatus::Exited(event_tid, _)) => { + let event_tid = Tid(event_tid.as_raw()); + assert_eq!(tid, event_tid); + + // TODO(edef): this only works for main thread + break; + } + _ => panic!( + "unknown status {:?} with syscall_state = {:?}", + status, syscall_state + ), + } + } + + Ok(()) +} -- cgit 1.4.1