use std::fs;
use std::io;
fn list_siblings(caller_tid: i32) -> io::Result<Vec<i32>> {
let dir = fs::read_dir(format!("/proc/{}/task", caller_tid))?;
let mut tids = Vec::new();
for entry in dir {
let entry = match entry {
Ok(e) => e,
Err(_) => continue,
};
let name = entry.file_name();
let name_str = match name.to_str() {
Some(s) => s,
None => continue,
};
let tid: i32 = match name_str.parse() {
Ok(t) => t,
Err(_) => continue,
};
if tid != caller_tid {
tids.push(tid);
}
}
Ok(tids)
}
fn seize_and_interrupt(tid: i32) -> io::Result<bool> {
let ret = unsafe {
libc::ptrace(libc::PTRACE_SEIZE as libc::c_uint, tid, 0, 0)
};
if ret < 0 {
let err = io::Error::last_os_error();
if err.raw_os_error() == Some(libc::ESRCH) {
return Ok(false); }
return Err(err);
}
let ret = unsafe {
libc::ptrace(libc::PTRACE_INTERRUPT as libc::c_uint, tid, 0, 0)
};
if ret < 0 {
let err = io::Error::last_os_error();
let _ = unsafe { libc::ptrace(libc::PTRACE_DETACH, tid, 0, 0) };
if err.raw_os_error() == Some(libc::ESRCH) {
return Ok(false);
}
return Err(err);
}
let mut status: i32 = 0;
let _ = unsafe { libc::waitpid(tid, &mut status, libc::__WALL) };
Ok(true)
}
fn detach(tid: i32) {
let _ = unsafe { libc::ptrace(libc::PTRACE_DETACH, tid, 0, 0) };
}
pub(crate) fn freeze_siblings_for_execve(caller_tid: i32) -> io::Result<usize> {
let siblings = list_siblings(caller_tid)?;
let mut frozen: Vec<i32> = Vec::with_capacity(siblings.len());
for tid in siblings {
match seize_and_interrupt(tid) {
Ok(true) => frozen.push(tid),
Ok(false) => continue, Err(e) => {
for ftid in &frozen {
detach(*ftid);
}
return Err(e);
}
}
}
Ok(frozen.len())
}
pub(crate) fn requires_freeze_on_continue(syscall_nr: i64) -> bool {
syscall_nr == libc::SYS_execve || syscall_nr == libc::SYS_execveat
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn list_siblings_excludes_self() {
let our_tid = unsafe { libc::syscall(libc::SYS_gettid) } as i32;
let siblings = list_siblings(our_tid).unwrap();
assert!(!siblings.contains(&our_tid));
}
#[test]
fn requires_freeze_only_for_exec() {
assert!(requires_freeze_on_continue(libc::SYS_execve));
assert!(requires_freeze_on_continue(libc::SYS_execveat));
assert!(!requires_freeze_on_continue(libc::SYS_openat));
assert!(!requires_freeze_on_continue(libc::SYS_connect));
}
}