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

fix(modern_ebpf): address verifier issues on kernel versions >=6.12.0 #2172

Merged
merged 5 commits into from
Nov 26, 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
6 changes: 5 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,11 @@ jobs:
steps:
- name: Install deps ⛓️
run: |
apk add g++ gcc cmake make git bash perl linux-headers autoconf automake m4 libtool elfutils-dev libelf-static patch binutils bpftool clang
apk add g++ gcc cmake make git bash perl linux-headers autoconf automake m4 libtool elfutils-dev libelf-static patch binutils clang llvm
git clone https://github.com/libbpf/bpftool.git --branch v7.3.0 --single-branch
cd bpftool
git submodule update --init
cd src && make install

- name: Checkout Libs ⤵️
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
Expand Down
60 changes: 0 additions & 60 deletions driver/modern_bpf/helpers/interfaces/attached_programs.h

This file was deleted.

13 changes: 5 additions & 8 deletions driver/modern_bpf/maps/maps.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,20 +117,17 @@ struct {
} syscall_exit_tail_table __weak SEC(".maps");

/**
* @brief This tail table is used when a bpf program needs another program
* to complete its execution flow. This table could be used both by
* programs directly attached in the kernel (like page_faults,
* context_switch, ...) and by syscall_events (like
* ppme_syscall_execveat_x, ...).
* Given a predefined tail-code (`extra_syscall_codes`), it calls
* @brief This tail table is used when a sys exit bpf program needs another program
* to complete its execution flow.
* Given a predefined tail-code (`sys_exit_extra_code`), it calls
* the right bpf program.
*/
struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(max_entries, TAIL_EXTRA_EVENT_PROG_MAX);
__uint(max_entries, SYS_EXIT_EXTRA_CODE_MAX);
__type(key, uint32_t);
__type(value, uint32_t);
} extra_syscall_calls __weak SEC(".maps");
} syscall_exit_extra_tail_table __weak SEC(".maps");

/*=============================== BPF_MAP_TYPE_PROG_ARRAY ===============================*/

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,42 @@
*/

#include <helpers/interfaces/syscalls_dispatcher.h>
#include <helpers/interfaces/attached_programs.h>

// We don't want to send DROP_E/DROP_X events from the enter tracepoint because it would requires us
// to create a dedicated tail table for the enter. It is enough to send DROP_E/DROP_X events from
// the exit tracepoint.
static __always_inline bool sampling_logic_enter(void* ctx, uint32_t id) {
/* If dropping mode is not enabled we don't perform any sampling
* false: means don't drop the syscall
* true: means drop the syscall
*/
if(!maps__get_dropping_mode()) {
return false;
}

uint8_t sampling_flag = maps__64bit_sampling_syscall_table(id);

if(sampling_flag == UF_NEVER_DROP) {
return false;
}

if(sampling_flag == UF_ALWAYS_DROP) {
return true;
}

// If we are in the sampling period we drop the event
if((bpf_ktime_get_boot_ns() % SECOND_TO_NS) >= (SECOND_TO_NS / maps__get_sampling_ratio())) {
return true;
}

return false;
}

/* From linux tree: /include/trace/events/syscall.h
* TP_PROTO(struct pt_regs *regs, long id),
*/
SEC("tp_btf/sys_enter")
int BPF_PROG(sys_enter, struct pt_regs *regs, long syscall_id) {
int BPF_PROG(sys_enter, struct pt_regs* regs, long syscall_id) {
int socketcall_syscall_id = -1;

if(bpf_in_ia32_syscall()) {
Expand Down Expand Up @@ -49,7 +78,7 @@ int BPF_PROG(sys_enter, struct pt_regs *regs, long syscall_id) {
return 0;
}

if(sampling_logic(ctx, syscall_id)) {
if(sampling_logic_enter(ctx, syscall_id)) {
return 0;
}

Expand Down
163 changes: 160 additions & 3 deletions driver/modern_bpf/programs/attached/dispatchers/syscall_exit.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,165 @@
*/

#include <helpers/interfaces/syscalls_dispatcher.h>
#include <helpers/interfaces/attached_programs.h>
#include <bpf/bpf_helpers.h>
#include <helpers/interfaces/fixed_size_event.h>

SEC("tp_btf/sys_exit")
int BPF_PROG(t_hotplug) {
/* We assume that the ring buffer for CPU 0 is always there so we send the
* HOT-PLUG event through this buffer.
*/
uint32_t cpu_0 = 0;
struct ringbuf_map *rb = bpf_map_lookup_elem(&ringbuf_maps, &cpu_0);
if(!rb) {
bpf_printk("unable to obtain the ring buffer for CPU 0");
return 0;
}

struct counter_map *counter = bpf_map_lookup_elem(&counter_maps, &cpu_0);
if(!counter) {
bpf_printk("unable to obtain the counter map for CPU 0");
return 0;
}

/* This counts the event seen by the drivers even if they are dropped because the buffer is
* full. */
counter->n_evts++;

/* If we are not able to reserve space we stop here
* the event collection.
*/
struct ringbuf_struct ringbuf;
ringbuf.reserved_event_size = HOTPLUG_E_SIZE;
ringbuf.event_type = PPME_CPU_HOTPLUG_E;
ringbuf.data = bpf_ringbuf_reserve(rb, HOTPLUG_E_SIZE, 0);
if(!ringbuf.data) {
counter->n_drops_buffer++;
return 0;
}

ringbuf__store_event_header(&ringbuf);

/*=============================== COLLECT PARAMETERS ===========================*/

/* Parameter 1: cpu (type: PT_UINT32) */
uint32_t current_cpu_id = (uint32_t)bpf_get_smp_processor_id();
ringbuf__store_u32(&ringbuf, current_cpu_id);

/* Parameter 2: action (type: PT_UINT32) */
/* Right now we don't have actions we always send 0 */
ringbuf__store_u32(&ringbuf, 0);

/*=============================== COLLECT PARAMETERS ===========================*/

ringbuf__submit_event(&ringbuf);
return 0;
}

SEC("tp_btf/sys_exit")
int BPF_PROG(t_drop_e) {
struct ringbuf_struct ringbuf;
if(!ringbuf__reserve_space(&ringbuf, DROP_E_SIZE, PPME_DROP_E)) {
return 0;
}

ringbuf__store_event_header(&ringbuf);

/*=============================== COLLECT PARAMETERS ===========================*/

ringbuf__store_u32(&ringbuf, maps__get_sampling_ratio());

/*=============================== COLLECT PARAMETERS ===========================*/

ringbuf__submit_event(&ringbuf);
return 0;
}

SEC("tp_btf/sys_exit")
int BPF_PROG(t_drop_x) {
struct ringbuf_struct ringbuf;
if(!ringbuf__reserve_space(&ringbuf, DROP_X_SIZE, PPME_DROP_X)) {
return 0;
}

ringbuf__store_event_header(&ringbuf);

/*=============================== COLLECT PARAMETERS ===========================*/

ringbuf__store_u32(&ringbuf, maps__get_sampling_ratio());

/*=============================== COLLECT PARAMETERS ===========================*/

ringbuf__submit_event(&ringbuf);
return 0;
}

enum custom_sys_exit_logic_codes {
T_HOTPLUG,
T_DROP_E,
T_DROP_X,
// add more codes here.
T_CUSTOM_MAX,
};

struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(max_entries, T_CUSTOM_MAX);
__uint(key_size, sizeof(__u32));
__array(values, int(void *));
} custom_sys_exit_calls SEC(".maps") = {
.values =
{
[T_HOTPLUG] = (void *)&t_hotplug,
[T_DROP_E] = (void *)&t_drop_e,
[T_DROP_X] = (void *)&t_drop_x,
},
};

static __always_inline bool sampling_logic_exit(void *ctx, uint32_t id) {
/* If dropping mode is not enabled we don't perform any sampling
* false: means don't drop the syscall
* true: means drop the syscall
*/
if(!maps__get_dropping_mode()) {
return false;
}

uint8_t sampling_flag = maps__64bit_sampling_syscall_table(id);

if(sampling_flag == UF_NEVER_DROP) {
return false;
}

if(sampling_flag == UF_ALWAYS_DROP) {
return true;
}

if((bpf_ktime_get_boot_ns() % SECOND_TO_NS) >= (SECOND_TO_NS / maps__get_sampling_ratio())) {
/* If we are starting the dropping phase we need to notify the userspace, otherwise, we
* simply drop our event.
* PLEASE NOTE: this logic is not per-CPU so it is best effort!
*/
if(!maps__get_is_dropping()) {
/* Here we are not sure we can send the drop_e event to userspace
* if the buffer is full, but this is not essential even if we lose
* an iteration we will synchronize again the next time the logic is enabled.
*/
maps__set_is_dropping(true);
bpf_tail_call(ctx, &custom_sys_exit_calls, T_DROP_E);
bpf_printk("unable to tail call into 'drop_e' prog");
}
return true;
}

if(maps__get_is_dropping()) {
maps__set_is_dropping(false);
bpf_tail_call(ctx, &custom_sys_exit_calls, T_DROP_X);
bpf_printk("unable to tail call into 'drop_x' prog");
}

return false;
}

#define X86_64_NR_EXECVE 59
#define X86_64_NR_EXECVEAT 322
Expand Down Expand Up @@ -63,7 +220,7 @@ int BPF_PROG(sys_exit, struct pt_regs *regs, long ret) {
return 0;
}

if(sampling_logic(ctx, syscall_id)) {
if(sampling_logic_exit(ctx, syscall_id)) {
return 0;
}

Expand All @@ -76,7 +233,7 @@ int BPF_PROG(sys_exit, struct pt_regs *regs, long ret) {
// we change our architecture we may need to update this logic.
struct ringbuf_map *rb = maps__get_ringbuf_map();
if(!rb) {
bpf_tail_call(ctx, &extra_syscall_calls, T1_HOTPLUG_E);
bpf_tail_call(ctx, &custom_sys_exit_calls, T_HOTPLUG);
bpf_printk("failed to tail call into the 'hotplug' prog");
return 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
*/

#include <helpers/interfaces/fixed_size_event.h>
#include <helpers/interfaces/attached_programs.h>

/* From linux tree: `/arch/x86/include/asm/trace/exceptions.h`
* TP_PROTO(unsigned long address, struct pt_regs *regs,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
*/

#include <helpers/interfaces/fixed_size_event.h>
#include <helpers/interfaces/attached_programs.h>

/* From linux tree: `/arch/x86/include/asm/trace/exceptions.h`
* TP_PROTO(unsigned long address, struct pt_regs *regs,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

#include <helpers/interfaces/variable_size_event.h>
#include <driver/systype_compat.h>
#include <helpers/interfaces/attached_programs.h>

/* The instruction limit is 1000000, so we shouldn't have issues */
#define MAX_THREADS_GROUPS 30
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
*/

#include <helpers/interfaces/fixed_size_event.h>
#include <helpers/interfaces/attached_programs.h>

/* From linux tree: /include/linux/events/sched.h
* TP_PROTO(bool preempt, struct task_struct *prev,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
*/

#include <helpers/interfaces/fixed_size_event.h>
#include <helpers/interfaces/attached_programs.h>

/* From linux tree: `/include/trace/events/signal.h`
* TP_PROTO(int sig, struct kernel_siginfo *info, struct k_sigaction *ka)
Expand Down
Loading
Loading