From 8fbe6ee6febf3991f95a8e0c9c27ae4f024d27af Mon Sep 17 00:00:00 2001 From: V Date: Wed, 27 Jul 2022 03:15:30 +0000 Subject: ripple/minitrace: refactor syscall interpretation We separate type-based parsing of syscall arguments from more detailed validation based on multiple parameters or on specific values. The former is implemented with a macro that takes syscall signatures and generates the relevant enum variant and parsing code. Co-authored-by: edef Change-Id: I7e334d3e128e7b1461bfd6fae7c8ec5dd6ada0a8 --- ripple/minitrace/src/main.rs | 301 ++++++++++++++++++++++++++----------------- 1 file changed, 184 insertions(+), 117 deletions(-) (limited to 'ripple/minitrace/src') diff --git a/ripple/minitrace/src/main.rs b/ripple/minitrace/src/main.rs index cd69094..3f27b16 100644 --- a/ripple/minitrace/src/main.rs +++ b/ripple/minitrace/src/main.rs @@ -93,22 +93,131 @@ impl Process { } } -#[derive(Debug, Copy, Clone)] -struct SyscallEntry { - number: u64, - // rdi, rsi, rdx, rcx, r8, r9 - args: [u64; 6], -} +macro_rules! define_syscalls { + (enum $enum:ident { + $(fn $id:ident ( $($arg_id:ident : $arg_ty:ty),* ) -> $ret:ty = $nr:literal ;)* + }) => { + #[derive(Debug, Copy, Clone)] + #[allow(non_camel_case_types)] + enum $enum { + $($id { + $($arg_id : $arg_ty),* + }),* + } -impl SyscallEntry { - fn from_regs(regs: libc::user_regs_struct) -> SyscallEntry { - SyscallEntry { - number: regs.orig_rax, - args: [regs.rdi, regs.rsi, regs.rdx, regs.r10, regs.r8, regs.r9], + impl $enum { + fn from_regs(regs: libc::user_regs_struct) -> Option<$enum> { + Some(match (regs.orig_rax, [regs.rdi, regs.rsi, regs.rdx, regs.r10, regs.r8, regs.r9]) { + $( + ($nr, [$($arg_id),*, ..]) => $enum::$id { + $($arg_id: SyscallArg::try_from_reg($arg_id)?),* + }, + )* + _ => return None + }) + } } } } +trait SyscallArg: Sized { + fn try_from_reg(reg: u64) -> Option; +} + +impl SyscallArg for u16 { + fn try_from_reg(reg: u64) -> Option { + reg.try_into().ok() + } +} + +impl SyscallArg for u32 { + fn try_from_reg(reg: u64) -> Option { + reg.try_into().ok() + } +} + +impl SyscallArg for u64 { + fn try_from_reg(reg: u64) -> Option { + Some(reg) + } +} + +impl SyscallArg for i32 { + fn try_from_reg(reg: u64) -> Option { + Some(u32::try_from(reg).ok()? as i32) + } +} + +impl SyscallArg for *mut i32 { + fn try_from_reg(reg: u64) -> Option { + Some(usize::try_from_reg(reg)? as *mut i32) + } +} + +impl SyscallArg for usize { + fn try_from_reg(reg: u64) -> Option { + reg.try_into().ok() + } +} + +impl SyscallArg for *const u8 { + fn try_from_reg(reg: u64) -> Option { + Some(usize::try_from_reg(reg)? as *const u8) + } +} + +impl SyscallArg for *mut u8 { + fn try_from_reg(reg: u64) -> Option { + Some(usize::try_from_reg(reg)? as *mut u8) + } +} + +type SigAction = (); +type SysInfo = (); +type Tms = (); +type Stat = (); +type RobustListHead = (); +type RLimit64 = (); + +impl SyscallArg for *mut () { + fn try_from_reg(reg: u64) -> Option { + Some(usize::try_from_reg(reg)? as *mut ()) + } +} + +impl SyscallArg for *const () { + fn try_from_reg(reg: u64) -> Option { + Some(usize::try_from_reg(reg)? as *const ()) + } +} + +define_syscalls! { + enum SyscallEntry { + fn read(fd: i32, buf: *mut u8, count: usize) -> i64 = 0; + fn write(fd: i32, buf: *const u8, count: usize) -> i64 = 1; + fn close(fd: i32) -> i64 = 3; + fn mmap(addr: u64, len: u64, prot: u64, flags: u64, fd: u64, off: u64) -> i64 = 9; + fn mprotect(addr: u64, len: usize, prot: u64) -> i64 = 10; + fn brk(brk: u64) -> i64 = 12; + fn rt_sigaction(sig: i32, act: *const SigAction, oact: *mut SigAction, sigsetsize: usize) -> i64 = 13; + fn ioctl(fd: u32, cmd: u32, arg: u64) -> i64 = 16; + fn pread64(fd: u32, buf: *mut u8, count: usize, pos: u64) -> i64 = 17; + fn access(filename: *const u8, mode: i32) -> i64 = 21; + fn getcwd(buf: *mut u8, size: u64) -> i64 = 79; + fn readlink(path: *const u8, buf: *mut u8, bufsiz: i32) -> i64 = 89; + fn sysinfo(info: *mut SysInfo) -> i64 = 99; + fn times(tbuf: *mut Tms) -> i64 = 100; + fn arch_prctl(option: i32, arg2: u64) -> i64 = 158; + fn set_tid_address(tidptr: *mut i32) -> i64 = 218; + fn exit_group(error_code: i32) -> i64 = 231; + fn openat(dfd: i32, filename: *const u8, flags: i32, mode: u16) -> i64 = 257; + fn newfstatat(dfd: i32, filename: *const u8, statbuf: *mut Stat, flag: i32) -> i64 = 262; + fn set_robust_list(head: *mut RobustListHead, len: usize) -> i64 = 273; + fn prlimit64(pid: i32, resource: u32, new_rlim: *const RLimit64, old_rlim: *mut RLimit64) -> i64 = 302; + fn getrandom(ubuf: *mut u8, len: usize, flags: u32) -> i64 = 318; + } +} + #[derive(Debug, Copy, Clone)] enum EntryExit { /// Process is about to enter a syscall @@ -159,13 +268,19 @@ fn main() -> Result<()> { assert_eq!(tid, event_tid); let regs = ptrace::getregs(event_tid.as_pid())?; - let entry = SyscallEntry::from_regs(regs); + let entry = match SyscallEntry::from_regs(regs) { + Some(entry) => entry, + None => { + ptrace::kill(event_tid.as_pid())?; + panic!("unsupported syscall {:?}", regs.orig_rax); + } + }; syscall_state = Some(EntryExit::Entry(entry)); if !check_syscall(&process, entry) { ptrace::kill(event_tid.as_pid())?; - panic!("unsupported syscall {:?}", entry); + panic!("invalid syscall {:?}", entry); } } (Some(EntryExit::Entry(entry)), WaitStatus::PtraceSyscall(event_tid)) => { @@ -196,39 +311,23 @@ fn main() -> Result<()> { const AT_FDCWD: i32 = -100; fn check_syscall(process: &Process, entry: SyscallEntry) -> bool { - match entry.number { - // read - 0 => {} - - // write - 1 => {} - - // close - 3 => {} - - // mmap - 9 => { - let [_addr, _len, _prot, flags, fd, _off] = entry.args; + match entry { + SyscallEntry::mmap { + addr: _, + len: _, + prot: _, + flags, + fd, + off: _, + } => { if fd != !0 { return flags & (libc::MAP_PRIVATE as u64) != 0; } else { return flags & (libc::MAP_ANON as u64) != 0; } } - - // mprotect - 10 => {} - - // brk - 12 => {} - - // rt_sigaction - 13 => {} - - // ioctl - 16 => { - let [_fd, command, ..] = entry.args; - match command { + SyscallEntry::ioctl { fd: _, cmd, arg: _ } => { + match cmd { // TCGETS 0x5401 => {} // TIOCGWINSZ @@ -236,99 +335,70 @@ fn check_syscall(process: &Process, entry: SyscallEntry) -> bool { _ => return false, } } - - // pread64 - 17 => {} - - // access - 21 => { - let [pathname, _mode, ..] = entry.args; - let pathname = process.read_mem_cstr(pathname).unwrap(); - println!("access({:?}, ..)", pathname); + SyscallEntry::access { filename, mode: _ } => { + let filename = process.read_mem_cstr(filename as u64).unwrap(); + println!("access({:?}, ..)", filename); } - - // getcwd - 79 => {} - - // readlink - 89 => { - let [pathname, _buf, _bufsiz, ..] = entry.args; - let pathname = process.read_mem_cstr(pathname).unwrap(); - println!("readlink({:?}, ..)", pathname); + SyscallEntry::readlink { + path, + buf: _, + bufsiz: _, + } => { + let path = process.read_mem_cstr(path as u64).unwrap(); + println!("readlink({:?}, ..)", path); } - - // sysinfo - 99 => {} - - // times - 100 => {} - - // arch_prctl - 158 => { - let [command, _addr, ..] = entry.args; - match command { + SyscallEntry::arch_prctl { option, arg2: _ } => { + match option { // ARCH_SET_FS 0x1002 => {} _ => return false, } } - - // set_tid_address - 218 => { - let [_tidptr, ..] = entry.args; + SyscallEntry::set_tid_address { tidptr: _ } => { println!("set_tid_address(..)"); } - - // exit_group - 231 => {} - - // openat - 257 => { - let [dirfd, pathname, flags, _mode, ..] = entry.args; - - let dirfd = u32::try_from(dirfd).map(|x| x as i32); - - if dirfd != Ok(AT_FDCWD) { + SyscallEntry::openat { + dfd, + filename, + flags, + mode: _, + } => { + if dfd != AT_FDCWD { return false; } - let pathname = process.read_mem_cstr(pathname).unwrap(); - - let flags: i32 = flags.try_into().expect("openat(2) flags don't fit in i32"); + let pathname = process.read_mem_cstr(filename as u64).unwrap(); let flags = OpenFlags::from_bits(flags).expect("unknown openat flags"); println!("openat(AT_FDCWD, {:?}, {:?}, ..)", pathname, flags); } - - // newfstatat - 262 => { - let [dirfd, pathname, _statbuf, _flags, ..] = entry.args; - - let dirfd = u32::try_from(dirfd).map(|x| x as i32); - let pathname = process.read_mem_cstr(pathname).unwrap(); - - if dirfd == Ok(AT_FDCWD) { - println!("newfstatat(AT_FDCWD, {:?}, ..)", pathname); + SyscallEntry::newfstatat { + dfd, + filename, + statbuf: _, + flag: _, + } => { + let pathname = process.read_mem_cstr(filename as u64).unwrap(); + if dfd == AT_FDCWD { + println!("newfstatat(AT_FDCWD, {pathname:?}, ..)"); } else if pathname.as_bytes() == b"" { - println!("newfstatat({dirfd}, {:?}, ..)", pathname); + println!("newfstatat({dfd}, {pathname:?})"); } else { return false; } } - - // set_robust_list - 273 => { - let [_head, len, ..] = entry.args; + SyscallEntry::set_robust_list { head: _, len } => { if len != 24 { panic!("set_robust_list(2) len should be sizeof (struct robust_list_head), actually {}", len); } println!("set_robust_list(..)"); } - - // prlimit64 - 302 => { - let [pid, resource, _new_limit, _old_limit, ..] = entry.args; - + SyscallEntry::prlimit64 { + pid, + resource, + new_rlim: _, + old_rlim: _, + } => { if pid != 0 { return false; } @@ -338,18 +408,15 @@ fn check_syscall(process: &Process, entry: SyscallEntry) -> bool { _ => return false, } } - - // getrandom - 318 => { - let [_buf, buflen, flags, ..] = entry.args; - let flags = flags - .try_into() - .expect("getrandom(2) flags don't fit in u32"); + SyscallEntry::getrandom { + ubuf: _, + len, + flags, + } => { let flags = GrndFlags::from_bits(flags).expect("unknown getrandom(2) flags"); - println!("getrandom(.., {}, {:?})", buflen, flags); + println!("getrandom(.., {}, {:?})", len, flags); } - - _ => return false, + _ => {} } true } -- cgit 1.4.1