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

Hook exit only after descriptors are closed. Fixes #216 #218

Merged
merged 3 commits into from
Feb 5, 2025
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
40 changes: 28 additions & 12 deletions GPL/Events/Process/Probe.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,11 @@ int BPF_PROG(sched_process_exec,

// Process exit probe
//
// Note that we aren't using the sched_process_exit tracepoint here as it's
// prone to race conditions. We want to emit an exit event when every single
// thread in a thread group has exited. If we were to try to detect that by
// checking task->signal->live == 0 (i.e. check that there are now 0 running
// threads in the thread group), we would race with a thread exit on another
// CPU decrementing task->signal->live before the BPF program can check if it
// is equal to 0.
// We want to emit an exit event when every single thread in a thread group has
// exited. If we were to try to detect that by checking task->signal->live == 0
// (i.e. check that there are now 0 running threads in the thread group), we
// would race with a thread exit on another CPU decrementing task->signal->live
// before the BPF program can check if it is equal to 0.
//
// Checking group_dead on taskstats_exit to determine if every thread in a
// thread group has exited instead is free of race conditions. taskstats_exit
Expand All @@ -193,14 +191,33 @@ int BPF_PROG(sched_process_exec,
// group_dead is the result of an atomic decrement and test operation on
// task->signal->live, so is guaranteed to only be passed into taskstats_exit
// as true once, which signifies the last thread in a thread group exiting.
//
// The problem is taskstats_exit__enter happens before file descriptors are
// closed in exit_files(), so instead of emiting the event here, record that we
// saw group_dead and delay emiting the event until sched_process_exit().
static int taskstats_exit__enter(const struct task_struct *task, int group_dead)
{
struct ebpf_events_state state = {};

if (!group_dead || is_kernel_thread(task))
goto out;
return 0;

struct ebpf_process_exit_event *event = get_event_buffer();
if (!event)
goto out;
ebpf_events_state__set(EBPF_EVENTS_STATE_GROUP_DEAD, &state);

return 0;
}

SEC("tp_btf/sched_process_exit")
int BPF_PROG(sched_process_exit, const struct task_struct *task)
{
struct ebpf_process_exit_event *event;

if (ebpf_events_state__get(EBPF_EVENTS_STATE_GROUP_DEAD) == NULL)
return 0;

event = get_event_buffer();
if (event == NULL)
return 0;

event->hdr.type = EBPF_EVENT_PROCESS_EXIT;
event->hdr.ts = bpf_ktime_get_ns();
Expand All @@ -227,7 +244,6 @@ static int taskstats_exit__enter(const struct task_struct *task, int group_dead)

ebpf_ringbuf_write(&ringbuf, event, EVENT_SIZE(event), 0);

out:
return 0;
}

Expand Down
2 changes: 2 additions & 0 deletions GPL/Events/State.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ enum ebpf_events_state_op {
EBPF_EVENTS_STATE_WRITE = 7,
EBPF_EVENTS_STATE_WRITEV = 8,
EBPF_EVENTS_STATE_CHOWN = 9,
EBPF_EVENTS_STATE_GROUP_DEAD = 10,
};

struct ebpf_events_key {
Expand Down Expand Up @@ -91,6 +92,7 @@ struct ebpf_events_state {
struct ebpf_events_write_state write;
struct ebpf_events_writev_state writev;
struct ebpf_events_chown_state chown;
/* struct ebpf_events_group_dead group_dead; nada */
};
};

Expand Down
2 changes: 1 addition & 1 deletion non-GPL/Events/Lib/EbpfEvents.c
Original file line number Diff line number Diff line change
Expand Up @@ -943,4 +943,4 @@ int ebpf_set_process_trustlist(struct bpf_map *map, uint32_t *pids, int count)
}

return rv;
}
}