use num_traits::FromPrimitive;
use std::collections::HashMap;
use crate::enums::PtraceOp;
use crate::syscall::Direction;
use crate::value::Value;
use crate::Ident;
use crate::SyscallParameters;
use crate::{from_c, TraceError, TraceOptions, TraceOutput, Tracer};
pub struct TraceThread<T>
where
T: Tracer,
{
data: from_c::ptrace_syscall_info,
ptrace: T,
sender: crossbeam_channel::Sender<TraceOutput>,
options: TraceOptions,
syscall_val_by_pid: HashMap<usize, TraceOutput>,
}
impl<T: Tracer> TraceThread<T> {
pub fn new(
ptrace: T,
sender: crossbeam_channel::Sender<TraceOutput>,
options: TraceOptions,
) -> Self {
TraceThread {
data: unsafe { std::mem::zeroed() },
ptrace,
sender,
options,
syscall_val_by_pid: HashMap::new(),
}
}
pub fn iterate(&mut self) -> Result<(bool, Option<usize>), TraceError> {
log::debug!("iterate(), pid: {}", self.ptrace.get_pid());
self.ptrace.before_data()?;
let ret = self.ptrace.get_ptrace(&mut self.data)?;
let mut has_more = ret.has_more;
if !has_more {
return Ok((false, None));
}
log::debug!("iteration op: {}", self.data.op);
let mut child_pid = None;
match FromPrimitive::from_u8(self.data.op)
.ok_or(TraceError::UnknownOp(self.data.op as usize))?
{
PtraceOp::Entry => {
self.process_entry(ret.pid)?;
has_more = true;
}
PtraceOp::Exit => {
let ret = self.process_exit(ret.pid)?;
has_more = ret.0;
child_pid = ret.1;
}
_ => (),
}
if !self.ptrace.prepare_next()? {
has_more = false;
}
Ok((has_more, child_pid))
}
pub fn finalize(&mut self) -> Result<(), TraceError> {
for (_, sv) in self.syscall_val_by_pid.drain() {
if let Err(e) = filtered_send(&self.sender, &self.options, sv) {
log::error!("Error when sending TraceOutput from finalize: {}", e);
}
}
self.ptrace.finalize()?;
Ok(())
}
fn process_entry(&mut self, pid: usize) -> Result<(), TraceError> {
let info = SyscallParameters::from(Direction::In, pid, &self.data);
if self.syscall_val_by_pid.get(&info.pid).is_some() {
return Err(TraceError::DuplicateEntry);
}
let ptrace_item = match info.get_definition() {
Some(definition) => TraceOutput::from_definition(&mut self.ptrace, definition, &info),
None => match info.get_ident() {
None => TraceOutput::new(
Ident::Unknown,
info.nr,
vec![Value::ValueNotImplemented],
info.pid,
),
Some(ident) => {
TraceOutput::new(ident, info.nr, vec![Value::ValueNotImplemented], info.pid)
}
},
};
self.syscall_val_by_pid.insert(info.pid, ptrace_item);
log::debug!("ENTRY: {:?}", self.syscall_val_by_pid.get(&info.pid));
Ok(())
}
fn process_exit(&mut self, pid: usize) -> Result<(bool, Option<usize>), TraceError> {
let mut info = SyscallParameters::from(Direction::Out, pid, &self.data);
let mut trace_output = match self.syscall_val_by_pid.remove(&info.pid) {
Some(sv) => sv,
None => {
return Ok((true, None));
}
};
info.nr = trace_output.nr;
let definition = match info.get_definition() {
None => {
if let Err(_) = filtered_send(&self.sender, &self.options, trace_output) {
log::debug!("Send error from trace, reading stopped?");
return Ok((false, None));
};
return Ok((true, None));
}
Some(sc) => sc,
};
log::debug!("\t process sc: {:?}", definition.call_name);
trace_output.update_exit(&mut self.ptrace, &info);
log::debug!("EXIT: {:?}", trace_output);
let child_pid = if trace_output.ident == Ident::Clone {
if info.exit.rval > 0 {
Some(info.exit.rval as usize)
} else {
None
}
} else {
None
};
if let Err(_) = filtered_send(&self.sender, &self.options, trace_output) {
log::debug!("Send error from trace, reading stopped?");
return Ok((false, child_pid));
};
Ok((true, child_pid))
}
}
fn filtered_send(
sender: &crossbeam_channel::Sender<TraceOutput>,
_options: &TraceOptions,
sv: TraceOutput,
) -> Result<(), crossbeam_channel::SendError<TraceOutput>> {
sender.send(sv)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ptrace::MockPtrace;
use crate::Errno;
fn init() {
let _ = env_logger::builder().is_test(true).try_init();
}
#[test]
pub fn test_unknown_op() {
init();
let output = test_with_data(vec![
0x01, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0xc0, 0xfb, 0x9d, 0x44, 0x00, 0x00, 0x00,
0x00, 0x3e, 0x00, 0xa4, 0x30, 0x55, 0xfe, 0x7f, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x20, 0xa4, 0x30, 0x52, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00,
0x01, 0x20, 0xa4, 0x30, 0x55, 0x00, 0x61, 0x63, 0x65, 0x2f, 0x73, 0x77, 0x61, 0x70,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xa3, 0x30, 0x55, 0xfe, 0x7f,
0x00, 0x00, 0x20, 0x50, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xa4, 0x30, 0x55, 0xfe,
0x7f, 0x00, 0x00, 0x73, 0x77, 0x61, 0x70, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xa0, 0xa3, 0x30, 0x55,
]);
assert_eq!(output.unwrap_err(), TraceError::UnknownOp(255));
}
#[test]
pub fn test_duplicate_entry() {
init();
let output = test_with_data(vec![
0x01, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0xc0, 0xfb, 0x9d, 0x44, 0x00, 0x00, 0x00,
0x00, 0x00, 0x18, 0xa4, 0x30, 0x55, 0xfe, 0x7f, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x20, 0xa4, 0x30, 0x55, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x63, 0x65, 0x2f, 0x73, 0x77, 0x61, 0x70,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xa3, 0x30, 0x55, 0xfe, 0x7f,
0x00, 0x00, 0x20, 0x50, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0xde, 0x00, 0x00, 0x3e, 0x00, 0x00, 0xc0,
]);
assert_eq!(output.unwrap_err(), TraceError::DuplicateEntry);
}
#[test]
pub fn test_too_large_memory_request() {
init();
let output = test_with_data(vec![
0x01, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0xc0, 0xfb, 0x9d, 0x44, 0x00, 0x00, 0x00,
0x00, 0x00, 0x18, 0xa4, 0x30, 0x55, 0xfe, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x20, 0xa4, 0x30, 0x55, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x63, 0x65, 0x2f, 0x73, 0x77, 0x61, 0x70,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xa3, 0x30, 0x55, 0xfe, 0x7f,
0x00, 0x00, 0x20, 0x50, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0xc0, 0xfb, 0x9d,
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xa4, 0x30, 0x55, 0xfe, 0x7f, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x01, 0xa4, 0x30, 0x55, 0xfe, 0x7f,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x63, 0x65, 0x2f,
0xfe, 0x7f, 0x00, 0x00,
]);
assert!(output.is_ok()); }
#[test]
pub fn test_memory_capacity_error() {
init();
let _output = test_with_data(vec![
0x01, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0xc0, 0xfb, 0x9d, 0x44, 0x00, 0x00, 0x00,
0x00, 0x00, 0x18, 0xa4, 0x30, 0x55, 0xfe, 0x7f, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x20, 0xa4, 0x30, 0x55, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x63, 0x65, 0x2f, 0x73, 0x77, 0x61, 0x70,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xa3, 0x30, 0x55, 0xfe, 0x7f,
0x00, 0x00, 0x20, 0x50, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0xc0,
]);
}
#[test]
pub fn test_memory_capacity_error_2() {
init();
let _output = test_with_data(vec![
0x01, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0xc0, 0xfb, 0x9d, 0x44, 0x00, 0x00, 0x00,
0x00, 0x00, 0x18, 0xa4, 0x30, 0x55, 0xfe, 0x7f, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
]);
}
#[test]
pub fn test_crash_1() {
init();
let output = test_with_data(vec![
0x01, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0xc0, 0x9b, 0x6b, 0x19, 0xf2, 0x8f, 0x7f,
0x00, 0x00, 0xe8, 0xb8, 0xdf, 0x4a, 0xfd, 0x7f, 0x00, 0xf3, 0x3f, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x1a, 0xf2, 0x87, 0x7f, 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x92, 0x17, 0xf2, 0x87, 0x7f, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x3b, 0x25,
0x6e, 0xe3, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3b, 0x25, 0x6e, 0xe3, 0x7f, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])
.unwrap();
assert_eq!(
output.out.unwrap().unwrap_err().to_errno(),
Errno::UnknownErrno
);
}
#[test]
pub fn test_crash_2() {
init();
let output = test_with_data(vec![
0x01, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x05, 0xc0, 0x9b, 0x6b, 0x40, 0xf2, 0x87, 0x7f,
0xe0, 0xff, 0xe8, 0xa4, 0xdf, 0x4a, 0xfd, 0x7f, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x50, 0x07, 0x1a, 0xf2, 0x87, 0x7f, 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x92, 0x17, 0xf2, 0x87, 0x7f, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0xc0, 0x9b, 0x6b,
0x19, 0xf2, 0x87, 0x7f, 0x00, 0x00, 0xe8, 0xb8, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xdb, 0x3c, 0x51, 0x4e, 0x56, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x97, 0xb0, 0x50, 0x4e,
0x56, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x00,
0x00, 0x1a, 0xf2, 0x87, 0x7f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0xc0, 0x56, 0xa1, 0x17, 0x6e, 0xe3,
]);
output.unwrap();
}
fn test_with_data(data: Vec<u8>) -> Result<TraceOutput, TraceError> {
let (seed_sender, seed_receiver) = crossbeam_channel::bounded(5);
let mut ptrace = MockPtrace::new(seed_receiver);
ptrace.initialize()?;
let (sender, r) = crossbeam_channel::bounded(5);
let mut tracer_thread = TraceThread::new(ptrace, sender, TraceOptions::default());
seed_sender.try_send(data.to_vec()).unwrap();
tracer_thread.iterate()?;
tracer_thread.iterate()?;
let output = r.try_recv().map_err(|_| TraceError::NoOutput)?;
tracer_thread.finalize()?;
Ok(output)
}
}