summary refs log tree commit diff
diff options
context:
space:
mode:
authorV <v@unfathomable.blue>2022-07-27 03:15:30 +0000
committeredef <edef@unfathomable.blue>2022-07-27 03:18:18 +0000
commit8fbe6ee6febf3991f95a8e0c9c27ae4f024d27af (patch)
tree944611ab027540534d2b343c0a7ff1b3dbebb9ff
parent01f798f2b2139f28c5a28de51c6ba90cd2a7c2f6 (diff)
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 <edef@unfathomable.blue>
Change-Id: I7e334d3e128e7b1461bfd6fae7c8ec5dd6ada0a8
-rw-r--r--ripple/minitrace/src/main.rs301
1 files changed, 184 insertions, 117 deletions
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<Self>;
+}
+
+impl SyscallArg for u16 {
+	fn try_from_reg(reg: u64) -> Option<Self> {
+		reg.try_into().ok()
+	}
+}
+
+impl SyscallArg for u32 {
+	fn try_from_reg(reg: u64) -> Option<Self> {
+		reg.try_into().ok()
+	}
+}
+
+impl SyscallArg for u64 {
+	fn try_from_reg(reg: u64) -> Option<Self> {
+		Some(reg)
+	}
+}
+
+impl SyscallArg for i32 {
+	fn try_from_reg(reg: u64) -> Option<Self> {
+		Some(u32::try_from(reg).ok()? as i32)
+	}
+}
+
+impl SyscallArg for *mut i32 {
+	fn try_from_reg(reg: u64) -> Option<Self> {
+		Some(usize::try_from_reg(reg)? as *mut i32)
+	}
+}
+
+impl SyscallArg for usize {
+	fn try_from_reg(reg: u64) -> Option<Self> {
+		reg.try_into().ok()
+	}
+}
+
+impl SyscallArg for *const u8 {
+	fn try_from_reg(reg: u64) -> Option<Self> {
+		Some(usize::try_from_reg(reg)? as *const u8)
+	}
+}
+
+impl SyscallArg for *mut u8 {
+	fn try_from_reg(reg: u64) -> Option<Self> {
+		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<Self> {
+		Some(usize::try_from_reg(reg)? as *mut ())
+	}
+}
+
+impl SyscallArg for *const () {
+	fn try_from_reg(reg: u64) -> Option<Self> {
+		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
 }