Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified ghostscope-process/ebpf/obj/sysmon-bpf.bpfeb.o
Binary file not shown.
Binary file modified ghostscope-process/ebpf/obj/sysmon-bpf.bpfel.o
Binary file not shown.
147 changes: 90 additions & 57 deletions ghostscope-process/ebpf/sysmon-bpf/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
#![no_main]

use aya_ebpf::{
macros::{map, tracepoint},
macros::{btf_tracepoint, map, raw_tracepoint, tracepoint},
maps::{Array, HashMap, PerfEventArray, RingBuf},
programs::TracePointContext,
programs::{BtfTracePointContext, RawTracePointContext, TracePointContext},
EbpfContext,
};

// ABI note:
Expand All @@ -20,35 +21,59 @@ pub struct SysEvent {
}

#[map(name = "sysmon_events")]
static mut SYS_EVENTS: RingBuf = RingBuf::with_byte_size(1 << 20, 0); // 1MB
static SYS_EVENTS: RingBuf = RingBuf::with_byte_size(1 << 20, 0); // 1MB

#[map(name = "sysmon_events_perf")]
static mut SYS_EVENTS_PERF: PerfEventArray<SysEvent> = PerfEventArray::new(0);
static SYS_EVENTS_PERF: PerfEventArray<SysEvent> = PerfEventArray::new(0);

// Allowed pids for -t mode: fork/exit events are only emitted when pid is present.
#[map(name = "allowed_pids")]
static mut ALLOWED_PIDS: HashMap<u32, u8> = HashMap::pinned(16384, 0);
static ALLOWED_PIDS: HashMap<u32, u8> = HashMap::pinned(16384, 0);

// Optional comm filter for exec events (truncated basename, null-terminated).
#[map(name = "target_exec_comm")]
static mut TARGET_EXEC_COMM: Array<[u8; 16]> = Array::pinned(1, 0);

fn write_event(ctx: &TracePointContext, mut ev: SysEvent) {
// Prefer direct helper to avoid large memcpy/memmove codegen
let size = core::mem::size_of::<SysEvent>() as u64;
// SAFETY: eBPF map statics are accessed only through BPF helper-compatible
// pointers while the verifier controls program concurrency.
let map_ptr = unsafe { core::ptr::addr_of_mut!(SYS_EVENTS) } as *mut _;
let data_ptr = &mut ev as *mut _ as *mut _;
// SAFETY: map_ptr points to SYS_EVENTS and data_ptr/size describe the local
// SysEvent value for the duration of the helper call.
let ret = unsafe { aya_ebpf::helpers::bpf_ringbuf_output(map_ptr, data_ptr, size, 0) };
if ret < 0 {
// Fallback to perf event if ringbuf output fails
// SAFETY: SYS_EVENTS_PERF is the statically declared BPF perf map and ev
// remains valid for the helper call.
let _ = unsafe { SYS_EVENTS_PERF.output(ctx, &mut ev, 0) };
static TARGET_EXEC_COMM: Array<[u8; 16]> = Array::pinned(1, 0);

fn write_event<C: EbpfContext>(ctx: &C, ev: SysEvent) {
if SYS_EVENTS.output::<SysEvent>(&ev, 0).is_err() {
SYS_EVENTS_PERF.output(ctx, &ev, 0);
}
}

#[inline(always)]
fn emit_exec<C: EbpfContext>(ctx: &C) -> u32 {
if !exec_comm_matches() {
return 0;
}
// Emit exec only when filter (if present) passes; userspace handles mapping and allowlist.
let ev = SysEvent {
tgid: current_tgid(),
kind: 1,
};
write_event(ctx, ev);
0
}

#[inline(always)]
fn emit_fork<C: EbpfContext>(ctx: &C) -> u32 {
let pid = current_tgid();
if ALLOWED_PIDS.get_ptr(&pid).is_none() {
return 0;
}
let ev = SysEvent { tgid: pid, kind: 2 };
write_event(ctx, ev);
0
}

#[inline(always)]
fn emit_exit<C: EbpfContext>(ctx: &C) -> u32 {
let pid = current_tgid();
if ALLOWED_PIDS.get_ptr(&pid).is_none() {
return 0;
}
let ev = SysEvent { tgid: pid, kind: 3 };
write_event(ctx, ev);
0
}

#[inline(always)]
Expand All @@ -60,9 +85,7 @@ fn current_tgid() -> u32 {

#[inline(always)]
fn exec_comm_matches() -> bool {
// SAFETY: TARGET_EXEC_COMM is a single-entry BPF array; get_ptr returns None
// when index 0 is unavailable.
let filter = match unsafe { TARGET_EXEC_COMM.get_ptr(0) } {
let filter = match TARGET_EXEC_COMM.get_ptr(0) {
Some(ptr) => {
// SAFETY: get_ptr returned a valid pointer to the array value for this
// helper invocation; [u8; 16] is Copy.
Expand All @@ -73,10 +96,16 @@ fn exec_comm_matches() -> bool {
if filter[0] == 0 {
return true;
}
let comm = match aya_ebpf::helpers::bpf_get_current_comm() {
Ok(val) => val,
Err(_) => return false,
let mut comm = [0u8; 16];
let ret = unsafe {
aya_ebpf::helpers::generated::bpf_get_current_comm(
comm.as_mut_ptr().cast(),
core::mem::size_of_val(&comm) as u32,
)
};
if ret != 0 {
return false;
}
let mut matched = true;
for i in 0..16 {
let expected = filter[i];
Expand All @@ -93,43 +122,47 @@ fn exec_comm_matches() -> bool {

#[tracepoint(name = "sched_process_exec", category = "sched")]
pub fn sched_process_exec(ctx: TracePointContext) -> u32 {
if !exec_comm_matches() {
return 0;
}
// Emit exec only when filter (if present) passes; userspace handles mapping and allowlist
let ev = SysEvent { tgid: current_tgid(), kind: 1 };
write_event(&ctx, ev);
0
emit_exec(&ctx)
}

#[tracepoint(name = "sched_process_fork", category = "sched")]
pub fn sched_process_fork(ctx: TracePointContext) -> u32 {
let pid = current_tgid();
// SAFETY: ALLOWED_PIDS is the statically declared BPF hash map; aya's map API
// performs verifier-compatible map lookup for the stack key.
unsafe {
if ALLOWED_PIDS.get(&pid).is_none() {
return 0;
}
}
let ev = SysEvent { tgid: pid, kind: 2 };
write_event(&ctx, ev);
0
emit_fork(&ctx)
}

#[tracepoint(name = "sched_process_exit", category = "sched")]
pub fn sched_process_exit(ctx: TracePointContext) -> u32 {
let pid = current_tgid();
// SAFETY: ALLOWED_PIDS is the statically declared BPF hash map; aya's map API
// performs verifier-compatible map lookup for the stack key.
unsafe {
if ALLOWED_PIDS.get(&pid).is_none() {
return 0;
}
}
let ev = SysEvent { tgid: pid, kind: 3 };
write_event(&ctx, ev);
0
emit_exit(&ctx)
}

#[raw_tracepoint(tracepoint = "sched_process_exec")]
pub fn raw_sched_process_exec(ctx: RawTracePointContext) -> u32 {
emit_exec(&ctx)
}

#[raw_tracepoint(tracepoint = "sched_process_fork")]
pub fn raw_sched_process_fork(ctx: RawTracePointContext) -> u32 {
emit_fork(&ctx)
}

#[raw_tracepoint(tracepoint = "sched_process_exit")]
pub fn raw_sched_process_exit(ctx: RawTracePointContext) -> u32 {
emit_exit(&ctx)
}

#[btf_tracepoint(function = "sched_process_exec")]
pub fn btf_sched_process_exec(ctx: BtfTracePointContext) -> u32 {
emit_exec(&ctx)
}

#[btf_tracepoint(function = "sched_process_fork")]
pub fn btf_sched_process_fork(ctx: BtfTracePointContext) -> u32 {
emit_fork(&ctx)
}

#[btf_tracepoint(function = "sched_process_exit")]
pub fn btf_sched_process_exit(ctx: BtfTracePointContext) -> u32 {
emit_exit(&ctx)
}

// Required by aya-bpf for panic handling in no_std
Expand Down
Loading
Loading