1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
pub mod backtrace;
mod child;
pub mod magic;
mod syscall_decode;
mod tracer;

use anyhow::{anyhow, Context};
pub use child::{Payload, SpawnOptions};
pub use magic::ty::Value;
use magic::Magic;
use serde::{Deserialize, Serialize};
use std::{mem, os::unix::io::RawFd};
use tiny_nix_ipc::Socket;

pub struct Settings {
    pub capture_backtrace: bool,
}

#[repr(C)]
#[derive(Debug, Serialize, Deserialize)]
pub struct RawSyscall {
    pub syscall_id: u64,
    pub args: [u64; 6],
    pub ret: u64,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Syscall {
    pub name: String,
    pub args: Vec<Value>,
    // sysenter: Some
    // sysexit: None
    pub ret: Option<Value>,
    // only provided on sysenter events, if backtrace capture was requested
    pub backtrace: Option<backtrace::Backtrace>,
}

#[repr(C)]
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "kind", content = "data")]
#[serde(rename_all = "kebab-case")]
pub enum EventPayload {
    Attach,
    Sysenter {
        /// First field - raw syscall args as is in registers
        raw: RawSyscall,
        /// Second field - parsed data
        decoded: Option<Syscall>,
    },
    Sysexit {
        raw: RawSyscall,
        decoded: Option<Syscall>,
    },
    Signal {
        raw: i32,
        decoded: String,
    },
    Exit(i32),
    /// Internal
    /// for this event pid=0
    /// tracer is about to exit because all tracees have finished
    Eos,
    #[doc(hidden)]
    __NonExhaustive,
}

#[repr(C)]
#[derive(Debug, Serialize, Deserialize)]
pub struct Event {
    pub payload: EventPayload,
    pub pid: u32,
}

unsafe fn split(payload: Payload, settings: Settings, out: Socket, magic: &Magic) -> ! {
    let res = libc::fork();
    if res == -1 {
        libc::exit(1);
    }
    if res != 0 {
        tracer::parent(out, settings, magic).ok();
    } else {
        mem::forget(out);
        child::execute_child_payload(payload);
    }
    libc::exit(0);
}

pub unsafe fn run(
    payload: Payload,
    settings: Settings,
    out: crossbeam::channel::Sender<Event>,
) -> anyhow::Result<()> {
    let (mut rcv, snd) = tiny_nix_ipc::Socket::new_socketpair()
        .map_err(|err| anyhow!("{}", err))
        .context("failed to create socket pair")?;

    let magic = magic_init();

    let res = libc::fork();
    if res == -1 {
        return Err(anyhow::Error::new(std::io::Error::last_os_error()).context("fork failed"));
    }

    if res != 0 {
        mem::forget(snd);
        loop {
            let msg = rcv
                .recv_json::<Event, [RawFd; 0]>(16384)
                .map_err(|err| anyhow!("{}", err))
                .context("failed to receive event")?
                .0;
            match msg.payload {
                EventPayload::Eos => {
                    break Ok(());
                }
                _ => {
                    out.send(msg)
                        .map_err(|err| anyhow!("{}", err))
                        .context("failed to send event")?;
                }
            }
        }
    } else {
        mem::forget(rcv);
        split(payload, settings, snd, &magic)
    }
}

static MAGIC: &str = include_str!("../magic.ktrace");

pub fn magic_init() -> magic::Magic {
    magic::init(MAGIC)
}