Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

eBPF: add support for tracing 32bit exec #41

Merged
merged 6 commits into from
Oct 12, 2024
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
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ serde = { version = "1.0.204", features = ["derive"] }
toml = "0.8.14"
paste = "1.0.15"
serde_json = "1.0.120"
libbpf-rs = { version = "0.24.1", optional = true, default-features = false }
libbpf-rs = { version = "0.24.6", optional = true, default-features = false }
# libbpf-sys exists here because we want to control its features
libbpf-sys = { version = "1", optional = true, default-features = false }
# tui-prompts = { version = "0.3.11", path = "../../contrib/tui-prompts" }
Expand All @@ -89,7 +89,7 @@ rstest = "0.22.0"
tracing-test = "0.2.4"

[build-dependencies]
libbpf-cargo = { version = "0.24.1", default-features = false }
libbpf-cargo = { version = "0.24.6", default-features = false }


[features]
Expand Down
6 changes: 3 additions & 3 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ fn main() {
.join("tracexec_system.skel.rs");
let arch = env::var("CARGO_CFG_TARGET_ARCH").expect("CARGO_CFG_TARGET_ARCH not set");
let arch_define = OsStr::new(match arch.as_str() {
"x86_64" => "__x86_64__",
"riscv64" => "__riscv64__",
"aarch64" => "__aarch64__",
"x86_64" => "TRACEXEC_TARGET_X86_64",
"riscv64" => "TRACEXEC_TARGET_RISCV64",
"aarch64" => "TRACEXEC_TARGET_AARCH64",
_ => panic!("Arch {arch} is not supported for now"),
});
let max_cpus = 64;
Expand Down
6 changes: 3 additions & 3 deletions include/vmlinux.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 3 additions & 5 deletions src/bpf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use libbpf_rs::{
use nix::{
errno::Errno,
fcntl::OFlag,
libc::{self, c_int, dup2, SYS_execve, SYS_execveat, AT_FDCWD},
libc::{self, c_int, dup2, AT_FDCWD},
sys::{
signal::{kill, raise, Signal},
wait::{waitpid, WaitPidFlag, WaitStatus},
Expand Down Expand Up @@ -197,9 +197,9 @@ impl EbpfTracer {
} else {
cached_cow(utf8_lossy_cow_from_bytes_with_nul(&event.base_filename)).into()
};
let filename = if event.syscall_nr == SYS_execve as i32 {
let filename = if !unsafe { event.is_execveat.assume_init() } {
base_filename
} else if event.syscall_nr == SYS_execveat as i32 {
} else {
if base_filename.is_ok_and(|s| s.starts_with('/')) {
base_filename
} else {
Expand All @@ -217,8 +217,6 @@ impl EbpfTracer {
}
}
}
} else {
unreachable!()
};
let exec_data = ExecData::new(
filename,
Expand Down
53 changes: 22 additions & 31 deletions src/bpf/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,44 +32,35 @@ extern void bpf_rcu_read_unlock(void) __ksym;
(((~_UL(0)) - (_UL(1) << (l)) + 1) & \
(~_UL(0) >> (BITS_PER_LONG - 1 - (h))))

/* BPF cannot access this struct */
struct forbidden_common_args {
u16 type;
u8 flags;
u8 preempt_count;
s32 pid;
};
// Architecture Specific Definitions

struct sys_enter_execve_args {
struct forbidden_common_args common;
s32 __syscall_nr;
u32 pad;
const u8 *filename;
const u8 *const *argv;
const u8 *const *envp;
};
#ifdef TRACEXEC_TARGET_X86_64
#define SYSCALL_PREFIX "x64"
#define SYSCALL_COMPAT_PREFIX "ia32_compat"
#elif TRACEXEC_TARGET_AARCH64
#define SYSCALL_PREFIX "arm64"
#elif TRACEXEC_TARGET_RISCV64
#define SYSCALL_PREFIX "riscv"
#endif

struct sys_enter_execveat_args {
struct forbidden_common_args common;
s32 __syscall_nr;
u64 fd;
const u8 *filename;
const u8 *const *argv;
const u8 *const *envp;
u64 flags;
};
#ifdef TRACEXEC_TARGET_X86_64

#define COMPAT_PT_REGS_PARM1_CORE(x) ((u32)(BPF_CORE_READ(__PT_REGS_CAST(x), bx)))
#define COMPAT_PT_REGS_PARM2_CORE(x) ((u32)(BPF_CORE_READ(__PT_REGS_CAST(x), cx)))
#define COMPAT_PT_REGS_PARM3_CORE(x) ((u32)(BPF_CORE_READ(__PT_REGS_CAST(x), dx)))
#define COMPAT_PT_REGS_PARM4_CORE(x) ((u32)(BPF_CORE_READ(__PT_REGS_CAST(x), si)))
#define COMPAT_PT_REGS_PARM5_CORE(x) ((u32)(BPF_CORE_READ(__PT_REGS_CAST(x), di)))

#endif

// Internal structs

struct sys_enter_exec_args {
s32 syscall_nr;
bool is_execveat;
bool is_compat;
const u8 *base_filename;
const u8 *const *argv;
const u8 *const *envp;
};

struct sys_exit_exec_args {
struct forbidden_common_args common;
s32 __syscall_nr;
s64 ret;
};

#endif
3 changes: 2 additions & 1 deletion src/bpf/interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ struct exec_event {
pid_t tgid;
uid_t uid;
uid_t gid;
s32 syscall_nr;
bool is_execveat;
bool is_compat;
s64 ret;
// argc and env count
u32 count[2];
Expand Down
121 changes: 95 additions & 26 deletions src/bpf/tracexec_system.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ struct reader_context {
// index:
// 0: arg
// 1: envp
u32 index;
u8 index;
bool is_compat;
// ptr is a userspace pointer to an array of cstring pointers
const u8 *const *ptr;
};
Expand Down Expand Up @@ -254,7 +255,8 @@ int trace_exec_common(struct sys_enter_exec_args *ctx) {
event->header.type = SYSEXIT_EVENT;
event->header.eid = __sync_fetch_and_add(&event_counter, 1);
event->count[0] = event->count[1] = event->fd_count = event->path_count = 0;
event->syscall_nr = ctx->syscall_nr;
event->is_compat = ctx->is_compat;
event->is_execveat = ctx->is_execveat;
// Read comm
if (0 != bpf_get_current_comm(event->comm, sizeof(event->comm))) {
// Failed to read comm
Expand Down Expand Up @@ -282,6 +284,7 @@ int trace_exec_common(struct sys_enter_exec_args *ctx) {
reader_ctx.event = event;
reader_ctx.ptr = ctx->argv;
reader_ctx.index = 0;
reader_ctx.is_compat = ctx->is_compat;
// bpf_loop allows 1 << 23 (~8 million) loops, otherwise we cannot achieve it
bpf_loop(ARGC_MAX, read_strings, &reader_ctx, 0);
// Read envp
Expand Down Expand Up @@ -419,49 +422,51 @@ int handle_exit(struct trace_event_raw_sched_process_template *ctx) {
return 0;
}

SEC("tracepoint/syscalls/sys_enter_execve")
int tp_sys_enter_execve(struct sys_enter_execve_args *ctx) {
SEC("fentry/__" SYSCALL_PREFIX "_sys_execve")
int BPF_PROG(sys_execve, struct pt_regs *regs) {
int key = 0;
struct sys_enter_exec_args *common_ctx =
bpf_map_lookup_elem(&exec_args_alloc, &key);
if (common_ctx == NULL)
return 0;
*common_ctx = (struct sys_enter_exec_args){
.syscall_nr = ctx->__syscall_nr,
.argv = ctx->argv,
.envp = ctx->envp,
.base_filename = ctx->filename,
.is_execveat = false,
.is_compat = false,
.argv = (u8 const *const *)PT_REGS_PARM2_CORE(regs),
.envp = (u8 const *const *)PT_REGS_PARM3_CORE(regs),
.base_filename = (u8 *)PT_REGS_PARM1_CORE(regs),
};
trace_exec_common(common_ctx);
return 0;
}

SEC("tracepoint/syscalls/sys_enter_execveat")
int tp_sys_enter_execveat(struct sys_enter_execveat_args *ctx) {
SEC("fentry/__" SYSCALL_PREFIX "_sys_execveat")
int BPF_PROG(sys_execveat, struct pt_regs *regs, int ret) {
int key = 0;
struct sys_enter_exec_args *common_ctx =
bpf_map_lookup_elem(&exec_args_alloc, &key);
if (common_ctx == NULL)
return 0;

*common_ctx = (struct sys_enter_exec_args){
.syscall_nr = ctx->__syscall_nr,
.argv = ctx->argv,
.envp = ctx->envp,
.base_filename = ctx->filename,
.is_execveat = true,
.is_compat = false,
.argv = (u8 const *const *)PT_REGS_PARM3_CORE(regs),
.envp = (u8 const *const *)PT_REGS_PARM4_CORE(regs),
.base_filename = (u8 *)PT_REGS_PARM2_CORE(regs),
};
trace_exec_common(common_ctx);
pid_t pid = (pid_t)bpf_get_current_pid_tgid();
struct exec_event *event = bpf_map_lookup_elem(&execs, &pid);
if (!event || !ctx)
return 0;

event->fd = ctx->fd;
event->flags = ctx->flags;
event->fd = PT_REGS_PARM1_CORE(regs);
event->flags = PT_REGS_PARM5_CORE(regs);
return 0;
}

int __always_inline tp_sys_exit_exec(struct sys_exit_exec_args *ctx) {
int __always_inline tp_sys_exit_exec(int sysret) {
pid_t pid, tgid;
u64 tmp = bpf_get_current_pid_tgid();
pid = (pid_t)tmp;
Expand All @@ -481,9 +486,9 @@ int __always_inline tp_sys_exit_exec(struct sys_exit_exec_args *ctx) {
}
return 0;
}
event->ret = ctx->ret;
event->ret = sysret;
event->header.type = SYSEXIT_EVENT;
debug("execve result: %d PID %d\n", ctx->ret, pid);
debug("execve result: %d PID %d\n", sysret, pid);
long ret = bpf_ringbuf_output(&events, event, sizeof(struct exec_event), 0);
if (ret != 0) {
#ifdef EBPF_DEBUG
Expand All @@ -497,16 +502,75 @@ int __always_inline tp_sys_exit_exec(struct sys_exit_exec_args *ctx) {
return 0;
}

SEC("tracepoint/syscalls/sys_exit_execve")
int tp_sys_exit_execve(struct sys_exit_exec_args *ctx) {
return tp_sys_exit_exec(ctx);
SEC("fexit/__" SYSCALL_PREFIX "_sys_execve")
int BPF_PROG(sys_exit_execve, struct pt_regs *regs, int ret) {
return tp_sys_exit_exec(ret);
}

SEC("tracepoint/syscalls/sys_exit_execveat")
int tp_sys_exit_execveat(struct sys_exit_exec_args *ctx) {
return tp_sys_exit_exec(ctx);
SEC("fexit/__" SYSCALL_PREFIX "_sys_execveat")
int BPF_PROG(sys_exit_execveat, struct pt_regs *regs, int ret) {
return tp_sys_exit_exec(ret);
}

#ifdef SYSCALL_COMPAT_PREFIX

SEC("fexit/__" SYSCALL_COMPAT_PREFIX "_sys_execveat")
int BPF_PROG(compat_sys_exit_execveat, struct pt_regs *regs, int ret) {
return tp_sys_exit_exec(ret);
}

SEC("fentry/__" SYSCALL_COMPAT_PREFIX "_sys_execveat")
int BPF_PROG(compat_sys_execveat, struct pt_regs *regs, int ret) {
int key = 0;
struct sys_enter_exec_args *common_ctx =
bpf_map_lookup_elem(&exec_args_alloc, &key);
if (common_ctx == NULL)
return 0;

*common_ctx = (struct sys_enter_exec_args){
.is_execveat = true,
.is_compat = true,
.argv = (u8 const *const *)(u64)COMPAT_PT_REGS_PARM3_CORE(regs),
.envp = (u8 const *const *)(u64)COMPAT_PT_REGS_PARM4_CORE(regs),
.base_filename = (u8 *)(u64)COMPAT_PT_REGS_PARM2_CORE(regs),
};
trace_exec_common(common_ctx);
pid_t pid = (pid_t)bpf_get_current_pid_tgid();
struct exec_event *event = bpf_map_lookup_elem(&execs, &pid);
if (!event || !ctx)
return 0;

event->fd = COMPAT_PT_REGS_PARM1_CORE(regs);
event->flags = COMPAT_PT_REGS_PARM5_CORE(regs);
return 0;
}

SEC("fexit/__" SYSCALL_COMPAT_PREFIX "_sys_execve")
int BPF_PROG(compat_sys_exit_execve, struct pt_regs *regs, int ret) {
return tp_sys_exit_exec(ret);
}

SEC("fentry/__" SYSCALL_COMPAT_PREFIX "_sys_execve")
int BPF_PROG(compat_sys_execve, struct pt_regs *regs) {
// int tp_sys_enter_execve(struct sys_enter_execve_args *ctx)
int key = 0;
struct sys_enter_exec_args *common_ctx =
bpf_map_lookup_elem(&exec_args_alloc, &key);
if (common_ctx == NULL)
return 0;
*common_ctx = (struct sys_enter_exec_args){
.is_execveat = false,
.is_compat = true,
.argv = (u8 const *const *)(u64)COMPAT_PT_REGS_PARM2_CORE(regs),
.envp = (u8 const *const *)(u64)COMPAT_PT_REGS_PARM3_CORE(regs),
.base_filename = (u8 *)(u64)COMPAT_PT_REGS_PARM1_CORE(regs),
};
trace_exec_common(common_ctx);
return 0;
}

#endif

// Collect information about file descriptors of the process on sysenter of exec
static int read_fds(struct exec_event *event) {
if (event == NULL)
Expand Down Expand Up @@ -747,7 +811,12 @@ static int _read_fd(unsigned int fd_num, struct file **fd_array,
static int read_strings(u32 index, struct reader_context *ctx) {
struct exec_event *event = ctx->event;
const u8 *argp = NULL;
int ret = bpf_probe_read_user(&argp, sizeof(argp), &ctx->ptr[index]);
int ret;
if (!ctx->is_compat)
ret = bpf_probe_read_user(&argp, sizeof(argp), &ctx->ptr[index]);
else
ret =
bpf_probe_read_user(&argp, sizeof(u32), (void*)ctx->ptr + index * sizeof(u32));
if (ret < 0) {
event->header.flags |= PTR_READ_FAILURE;
debug("Failed to read pointer to arg");
Expand Down
1 change: 1 addition & 0 deletions typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ extend-ignore-re = ["\\(L\\)ine", "\\(E\\)nvironment"]
[default.extend-words]
"Sur" = "Sur"
"inh" = "inh" # abbr for inherited
"PARM" = "PARM"

[files]
extend-exclude = [
Expand Down