mirror of
https://github.com/eunomia-bpf/bpf-developer-tutorial.git
synced 2026-10-29 03:34:44 +08:00
* Rebase lesson 54 as a standalone monitor * docs: preserve lesson 54 introduction * docs: remove design aside from lesson 54 * Simplify lesson 54 to the monitor path --------- Co-authored-by: LinuxDev9002 <i@littlefisher.me>
152 lines
3.9 KiB
C
152 lines
3.9 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#include "vmlinux.h"
|
|
#include <bpf/bpf_helpers.h>
|
|
#include <bpf/bpf_tracing.h>
|
|
#include "bpf_experimental.h"
|
|
#include "exec_image_inspector.h"
|
|
|
|
char LICENSE[] SEC("license") = "GPL";
|
|
|
|
#define ENOENT 2
|
|
|
|
#define EI_CLASS 4
|
|
#define EI_DATA 5
|
|
#define ELFCLASS32 1
|
|
#define ELFCLASS64 2
|
|
#define ELFDATA2LSB 1
|
|
#define ELFDATA2MSB 2
|
|
|
|
struct inspector_stats stats;
|
|
|
|
struct {
|
|
__uint(type, BPF_MAP_TYPE_RINGBUF);
|
|
__uint(max_entries, 256 * 1024);
|
|
} events SEC(".maps");
|
|
|
|
struct exec_work {
|
|
__u64 scheduled_ns;
|
|
struct bpf_task_work work;
|
|
};
|
|
|
|
struct {
|
|
__uint(type, BPF_MAP_TYPE_HASH);
|
|
__uint(map_flags, BPF_F_NO_PREALLOC);
|
|
__uint(max_entries, 4096);
|
|
__type(key, __u64);
|
|
__type(value, struct exec_work);
|
|
} pending SEC(".maps");
|
|
|
|
static __u16 read_elf_u16(const unsigned char *header, int offset, __u8 data)
|
|
{
|
|
if (data == ELFDATA2MSB)
|
|
return ((__u16)header[offset] << 8) | header[offset + 1];
|
|
return header[offset] | ((__u16)header[offset + 1] << 8);
|
|
}
|
|
|
|
static int inspect_executable(struct bpf_map *map, void *key, void *value)
|
|
{
|
|
unsigned char header[64] = {};
|
|
struct exec_work *work = value;
|
|
struct exec_event event = {};
|
|
struct task_struct *task;
|
|
struct bpf_dynptr dynptr;
|
|
struct file *file;
|
|
__u64 pid_tgid;
|
|
int err;
|
|
|
|
__sync_fetch_and_add(&stats.callbacks, 1);
|
|
pid_tgid = bpf_get_current_pid_tgid();
|
|
event.pid = (__u32)pid_tgid;
|
|
event.tgid = pid_tgid >> 32;
|
|
event.latency_ns = bpf_ktime_get_ns() - work->scheduled_ns;
|
|
bpf_get_current_comm(event.comm, sizeof(event.comm));
|
|
|
|
task = bpf_get_current_task_btf();
|
|
file = bpf_get_task_exe_file(task);
|
|
if (!file) {
|
|
event.header_error = -ENOENT;
|
|
__sync_fetch_and_add(&stats.header_errors, 1);
|
|
goto emit;
|
|
}
|
|
|
|
err = bpf_path_d_path(&file->f_path, event.path, sizeof(event.path));
|
|
if (err < 0) {
|
|
event.path_error = err;
|
|
__sync_fetch_and_add(&stats.path_errors, 1);
|
|
}
|
|
|
|
err = bpf_dynptr_from_file(file, 0, &dynptr);
|
|
if (err) {
|
|
bpf_dynptr_file_discard(&dynptr);
|
|
event.header_error = err;
|
|
__sync_fetch_and_add(&stats.header_errors, 1);
|
|
goto put_file;
|
|
}
|
|
|
|
err = bpf_dynptr_read(header, sizeof(header), &dynptr, 0, 0);
|
|
if (err) {
|
|
event.header_error = err;
|
|
__sync_fetch_and_add(&stats.header_errors, 1);
|
|
}
|
|
bpf_dynptr_file_discard(&dynptr);
|
|
|
|
if (!event.header_error && header[0] == 0x7f && header[1] == 'E' &&
|
|
header[2] == 'L' && header[3] == 'F') {
|
|
event.is_elf = 1;
|
|
event.elf_class = header[EI_CLASS];
|
|
event.elf_data = header[EI_DATA];
|
|
event.elf_type = read_elf_u16(header, 16, event.elf_data);
|
|
event.elf_machine = read_elf_u16(header, 18, event.elf_data);
|
|
}
|
|
|
|
put_file:
|
|
bpf_put_file(file);
|
|
emit:
|
|
if (bpf_ringbuf_output(&events, &event, sizeof(event), 0))
|
|
__sync_fetch_and_add(&stats.dropped, 1);
|
|
if (bpf_map_delete_elem(map, key))
|
|
__sync_fetch_and_add(&stats.cleanup_errors, 1);
|
|
__sync_fetch_and_add(&stats.completed, 1);
|
|
return 0;
|
|
}
|
|
|
|
SEC("lsm/bprm_committed_creds")
|
|
void BPF_PROG(schedule_exec_inspection, struct linux_binprm *bprm)
|
|
{
|
|
struct task_struct *task;
|
|
struct exec_work empty_work = {};
|
|
struct exec_work *work;
|
|
__u64 pid_tgid;
|
|
__u64 key;
|
|
int err;
|
|
|
|
(void)bprm;
|
|
pid_tgid = bpf_get_current_pid_tgid();
|
|
key = pid_tgid;
|
|
__sync_fetch_and_add(&stats.matched, 1);
|
|
err = bpf_map_update_elem(&pending, &key, &empty_work, BPF_NOEXIST);
|
|
if (err) {
|
|
__sync_fetch_and_add(&stats.schedule_errors, 1);
|
|
return;
|
|
}
|
|
work = bpf_map_lookup_elem(&pending, &key);
|
|
if (!work) {
|
|
__sync_fetch_and_add(&stats.schedule_errors, 1);
|
|
if (bpf_map_delete_elem(&pending, &key))
|
|
__sync_fetch_and_add(&stats.cleanup_errors, 1);
|
|
return;
|
|
}
|
|
|
|
work->scheduled_ns = bpf_ktime_get_ns();
|
|
task = bpf_get_current_task_btf();
|
|
err = bpf_task_work_schedule_signal(task, &work->work, &pending,
|
|
inspect_executable);
|
|
if (err) {
|
|
__sync_fetch_and_add(&stats.schedule_errors, 1);
|
|
if (bpf_map_delete_elem(&pending, &key))
|
|
__sync_fetch_and_add(&stats.cleanup_errors, 1);
|
|
return;
|
|
}
|
|
__sync_fetch_and_add(&stats.scheduled, 1);
|
|
}
|