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
715 changes: 58 additions & 657 deletions src/54-exec-image-inspector/README.md

Large diffs are not rendered by default.

715 changes: 58 additions & 657 deletions src/54-exec-image-inspector/README.zh.md

Large diffs are not rendered by default.

46 changes: 15 additions & 31 deletions src/54-exec-image-inspector/exec-image-flow.dot
Original file line number Diff line number Diff line change
Expand Up @@ -29,41 +29,28 @@ digraph exec_image_flow {
arrowsize=0.75
];

fork [
label="User space\nfork child\nchild waits on pipe",
start [
label="User space\nstart system-wide monitor",
fillcolor="#EBF8FF",
color="#3182CE"
];
prepare [
label="target_tgid + probe_offset\nload LSM + ring buffer",
label="load LSM + ring buffer",
fillcolor="#EBF8FF",
color="#3182CE"
];
exec [
label="release pipe\nchild calls execvp",
label="any process calls exec",
fillcolor="#EBF8FF",
color="#3182CE"
];
hook [
label="bprm_committed_creds\nmatch target_tgid",
fillcolor="#FFFAF0",
color="#DD6B20"
];
probe [
label="probe_offset set?",
shape=diamond,
style="filled",
fillcolor="#FFFAF0",
color="#DD6B20",
margin="0.10,0.05"
];
direct [
label="non-sleepable direct probe\nstore result",
label="bprm_committed_creds\nobserve every committed exec",
fillcolor="#FFFAF0",
color="#DD6B20"
];
pending [
label="pending[0]\nscheduled_ns + direct result",
label="pending[pid_tgid]\nscheduled_ns + task work",
shape=cylinder,
fillcolor="#FFF5F5",
color="#C05621"
Expand All @@ -84,38 +71,35 @@ digraph exec_image_flow {
color="#805AD5"
];
read [
label="file-backed dynptr\nread ELF header + marker\ndiscard dynptr + put file",
label="file-backed dynptr\nread ELF header\ndiscard dynptr + put file",
fillcolor="#FAF5FF",
color="#805AD5"
];
event [
label="ring buffer event\npath + ELF + probe + latency",
label="ring buffer event\npath + ELF + latency\ndelete pending entry",
fillcolor="#F0FFF4",
color="#38A169"
];
collect [
label="User space\npoll + waitpid\nfinal drain + stats",
label="User space\nstream events until SIGINT/SIGTERM\nfinal drain + stats",
fillcolor="#EBF8FF",
color="#3182CE"
];

{ rank=same; fork; prepare; exec; }
{ rank=same; hook; probe; direct; }
{ rank=same; pending; schedule; }
{ rank=same; start; prepare; exec; }
{ rank=same; hook; pending; schedule; }
{ rank=same; callback; file; read; }
{ rank=same; event; collect; }

fork -> prepare [label="parent knows child TGID"];
prepare -> exec [label="attach completes"];
start -> prepare;
prepare -> exec [label="READY"];
exec -> hook [label="each committed exec"];
hook -> probe;
probe -> direct [label="yes"];
probe -> pending [label="no"];
direct -> pending;
hook -> pending;
pending -> schedule;
schedule -> callback [label="task work runs"];
callback -> file;
file -> read;
read -> event;
event -> collect;
collect -> exec [label="keep monitoring", style=dashed];
}
Binary file modified src/54-exec-image-inspector/exec-image-flow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
87 changes: 20 additions & 67 deletions src/54-exec-image-inspector/exec_image_inspector.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ char LICENSE[] SEC("license") = "GPL";
#define ELFDATA2LSB 1
#define ELFDATA2MSB 2

const volatile __u32 target_tgid;
const volatile __u32 probe_offset;

struct inspector_stats stats;

struct {
Expand All @@ -28,14 +25,14 @@ struct {

struct exec_work {
__u64 scheduled_ns;
int direct_probe_error;
struct bpf_task_work work;
};

struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__type(key, __u32);
__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");

Expand All @@ -46,36 +43,6 @@ static __u16 read_elf_u16(const unsigned char *header, int offset, __u8 data)
return header[offset] | ((__u16)header[offset + 1] << 8);
}

static int probe_file_without_sleep(struct file *file)
{
unsigned char sample[EXEC_PROBE_LEN];
struct bpf_dynptr dynptr;
int err;

if (!probe_offset)
return 0;

__sync_fetch_and_add(&stats.direct_probes, 1);
if (!file) {
err = -ENOENT;
goto record;
}

err = bpf_dynptr_from_file(file, 0, &dynptr);
if (err) {
bpf_dynptr_file_discard(&dynptr);
goto record;
}

err = bpf_dynptr_read(sample, sizeof(sample), &dynptr, probe_offset, 0);
bpf_dynptr_file_discard(&dynptr);

record:
if (err)
__sync_fetch_and_add(&stats.direct_probe_errors, 1);
return err;
}

static int inspect_executable(struct bpf_map *map, void *key, void *value)
{
unsigned char header[64] = {};
Expand All @@ -87,28 +54,18 @@ static int inspect_executable(struct bpf_map *map, void *key, void *value)
__u64 pid_tgid;
int err;

(void)map;
(void)key;
__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;
event.direct_probe_error = work->direct_probe_error;
event.probe_offset = probe_offset;
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);
if (probe_offset) {
event.deferred_probe_error = -ENOENT;
__sync_fetch_and_add(&stats.deferred_probes, 1);
__sync_fetch_and_add(&stats.deferred_probe_errors, 1);
}
goto emit;
}

Expand All @@ -123,11 +80,6 @@ static int inspect_executable(struct bpf_map *map, void *key, void *value)
bpf_dynptr_file_discard(&dynptr);
event.header_error = err;
__sync_fetch_and_add(&stats.header_errors, 1);
if (probe_offset) {
event.deferred_probe_error = err;
__sync_fetch_and_add(&stats.deferred_probes, 1);
__sync_fetch_and_add(&stats.deferred_probe_errors, 1);
}
goto put_file;
}

Expand All @@ -136,15 +88,6 @@ static int inspect_executable(struct bpf_map *map, void *key, void *value)
event.header_error = err;
__sync_fetch_and_add(&stats.header_errors, 1);
}

if (probe_offset) {
__sync_fetch_and_add(&stats.deferred_probes, 1);
err = bpf_dynptr_read(event.probe_bytes, sizeof(event.probe_bytes),
&dynptr, probe_offset, 0);
event.deferred_probe_error = err;
if (err)
__sync_fetch_and_add(&stats.deferred_probe_errors, 1);
}
bpf_dynptr_file_discard(&dynptr);

if (!event.header_error && header[0] == 0x7f && header[1] == 'E' &&
Expand All @@ -161,37 +104,47 @@ static int inspect_executable(struct bpf_map *map, void *key, void *value)
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;
__u32 key = 0, tgid;
__u64 key;
int err;

(void)bprm;
pid_tgid = bpf_get_current_pid_tgid();
tgid = pid_tgid >> 32;
if (target_tgid && tgid != target_tgid)
return;

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();
work->direct_probe_error = probe_file_without_sleep(bprm->file);
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);
Expand Down
Loading