Skip to content

Commit

Permalink
cleanup(driver): made socketcall support fully dynamic.
Browse files Browse the repository at this point in the history
We don't need either CAPTURE_SOCKETCALL nor _HAS_SOCKETCALL anymore.
Moreover, added support for x86 ia32 socketcall for modern bpf.

Signed-off-by: Federico Di Pierro <[email protected]>
  • Loading branch information
FedeDP committed Jul 12, 2023
1 parent 8cb3ad5 commit 79cfe5c
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 145 deletions.
16 changes: 2 additions & 14 deletions driver/bpf/plumbing_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@ or GPL2.txt for full copies of the license.

#include "types.h"
#include "builtins.h"

#if defined(CAPTURE_SOCKETCALL) || (defined(CONFIG_X86_64) && defined(CONFIG_IA32_EMULATION))
#include <linux/net.h>
#endif
#include "../socketcall_to_syscall.h"

#define _READ(P) ({ typeof(P) _val; \
bpf_probe_read_kernel(&_val, sizeof(_val), &P); \
Expand Down Expand Up @@ -321,7 +318,6 @@ static __always_inline unsigned long bpf_syscall_get_argument_from_ctx(void *ctx
return arg;
}

#if defined(CAPTURE_SOCKETCALL) || (defined(CONFIG_X86_64) && defined(CONFIG_IA32_EMULATION))
static __always_inline unsigned long bpf_syscall_get_socketcall_arg(void *ctx, int idx)
{
unsigned long arg = 0;
Expand All @@ -337,20 +333,16 @@ static __always_inline unsigned long bpf_syscall_get_socketcall_arg(void *ctx, i

return arg;
}
#endif /* CAPTURE_SOCKETCALL */

static __always_inline unsigned long bpf_syscall_get_argument(struct filler_data *data,
int idx)
{
#ifdef BPF_SUPPORTS_RAW_TRACEPOINTS

/* We define it here because we support socket calls only on kernels with BPF_SUPPORTS_RAW_TRACEPOINTS */
#if defined(CAPTURE_SOCKETCALL) || (defined(CONFIG_X86_64) && defined(CONFIG_IA32_EMULATION))
/* We define it here because we support socket calls only on kernels with BPF_SUPPORTS_RAW_TRACEPOINTS */
if(bpf_syscall_get_nr(data->ctx) == data->state->tail_ctx.socketcall_syscall_id)
{
return bpf_syscall_get_socketcall_arg(data->ctx, idx);
}
#endif /* CAPTURE_SOCKETCALL */
return bpf_syscall_get_argument_from_ctx(data->ctx, idx);
#else
return bpf_syscall_get_argument_from_args(data->args, idx);
Expand Down Expand Up @@ -740,14 +732,10 @@ static __always_inline void call_filler(void *ctx,
release_local_state(state);
}

#if (defined(CAPTURE_SOCKETCALL) || (defined(CONFIG_X86_64) && defined(CONFIG_IA32_EMULATION))) && defined(BPF_SUPPORTS_RAW_TRACEPOINTS)
#include "../socketcall_to_syscall.h"

static __always_inline long convert_network_syscalls(void *ctx, bool *is_syscall)
{
int socketcall_id = (int)bpf_syscall_get_argument_from_ctx(ctx, 0);
return socketcall_code_to_syscall_code(socketcall_id, is_syscall);
}
#endif

#endif
4 changes: 0 additions & 4 deletions driver/bpf/probe.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ BPF_PROBE("raw_syscalls/", sys_enter, sys_enter_args)
}
}

#if (defined(CAPTURE_SOCKETCALL) || (defined(CONFIG_X86_64) && defined(CONFIG_IA32_EMULATION))) && defined(BPF_SUPPORTS_RAW_TRACEPOINTS)
if(id == socketcall_syscall_id)
{
bool is_syscall_return;
Expand All @@ -92,7 +91,6 @@ BPF_PROBE("raw_syscalls/", sys_enter, sys_enter_args)
id = return_code;
}
}
#endif

enabled = is_syscall_interesting(id);
if(!enabled)
Expand Down Expand Up @@ -188,7 +186,6 @@ BPF_PROBE("raw_syscalls/", sys_exit, sys_exit_args)
}
}

#if (defined(CAPTURE_SOCKETCALL) || (defined(CONFIG_X86_64) && defined(CONFIG_IA32_EMULATION))) && defined(BPF_SUPPORTS_RAW_TRACEPOINTS)
if(id == socketcall_syscall_id)
{
bool is_syscall_return;
Expand All @@ -204,7 +201,6 @@ BPF_PROBE("raw_syscalls/", sys_exit, sys_exit_args)
id = return_code;
}
}
#endif

enabled = is_syscall_interesting(id);
if (!enabled)
Expand Down
31 changes: 0 additions & 31 deletions driver/feature_gates.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,21 +64,6 @@ or GPL2.txt for full copies of the license.
#define CAPTURE_SCHED_PROC_FORK
#endif

///////////////////////////////
// CAPTURE_SOCKETCALL
///////////////////////////////

/* There are architectures that used history socketcall to multiplex
* the network system calls. Even if architectures, like s390x, has
* direct support for those network system calls, kernel version header
* dependencies in libc prevent using them.
*
* For details, see also https://sourceware.org/pipermail/libc-alpha/2022-September/142108.html
*/
#if defined(CONFIG_S390)
#define CAPTURE_SOCKETCALL
#endif

///////////////////////////////
// CAPTURE_SCHED_PROC_EXEC
///////////////////////////////
Expand Down Expand Up @@ -182,14 +167,6 @@ or GPL2.txt for full copies of the license.
#define CAPTURE_PAGE_FAULTS
#endif

///////////////////////////////
// CAPTURE_SOCKETCALL
///////////////////////////////

#if defined(__TARGET_ARCH_s390)
#define CAPTURE_SOCKETCALL
#endif

#else /* Userspace */

/* Please note: the userspace loads the filler table for the bpf probe
Expand Down Expand Up @@ -241,14 +218,6 @@ or GPL2.txt for full copies of the license.
#define CAPTURE_SCHED_PROC_EXEC
#endif

///////////////////////////////
// CAPTURE_SOCKETCALL
///////////////////////////////

#if defined(__s390x__)
#define CAPTURE_SOCKETCALL
#endif

#endif /* UDIG */

#endif /* __KERNEL__ */
Expand Down
78 changes: 35 additions & 43 deletions driver/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ or GPL2.txt for full copies of the license.
#include "ppm.h"
#include "ppm_tp.h"

#ifdef _HAS_SOCKETCALL
#include "socketcall_to_syscall.h"
#endif

#define __NR_ia32_socketcall 102

Expand Down Expand Up @@ -1363,8 +1361,7 @@ static const unsigned char compat_nas[21] = {
#endif


#ifdef _HAS_SOCKETCALL
/* This method is just a pass-through to avoid exporting
/* This method is just a pass-through to avoid exporting
* `ppm_syscall_get_arguments` outside of `main.c`
*/
static long convert_network_syscalls(struct pt_regs *regs, bool* is_syscall_return)
Expand Down Expand Up @@ -1410,51 +1407,44 @@ static int load_socketcall_params(struct event_filler_arguments *filler_args)

static inline struct event_data_t *manage_socketcall(struct event_data_t *event_data, int socketcall_syscall_id, bool is_exit)
{
if(event_data->event_info.syscall_data.id == socketcall_syscall_id)
{
bool is_syscall_return;
int return_code = convert_network_syscalls(event_data->event_info.syscall_data.regs, &is_syscall_return);
bool is_syscall_return;
int return_code = convert_network_syscalls(event_data->event_info.syscall_data.regs, &is_syscall_return);

/* If the return code is not the generic event we will need to extract parameters
* with the socket call mechanism.
*/
event_data->extract_socketcall_params = true;
/* If the return code is not the generic event we will need to extract parameters
* with the socket call mechanism.
*/
event_data->extract_socketcall_params = true;

/* If we return an event code, it means we need to call directly `record_event_all_consumers` */
if(!is_syscall_return)
/* If we return an event code, it means we need to call directly `record_event_all_consumers` */
if(!is_syscall_return)
{
/* The user provided a wrong code, we will send a generic event,
* no need for socket call arguments extraction logic.
*/
if(return_code == PPME_GENERIC_E)
{
/* The user provided a wrong code, we will send a generic event,
* no need for socket call arguments extraction logic.
*/
if(return_code == PPME_GENERIC_E)
{
event_data->extract_socketcall_params = false;
}
/* we need to use `return_code + 1` because return_code
* is the enter event.
*/
record_event_all_consumers(return_code + is_exit,
return_code == PPME_GENERIC_E ? UF_ALWAYS_DROP : UF_USED,
event_data, is_exit ? KMOD_PROG_SYS_EXIT : KMOD_PROG_SYS_ENTER);
return NULL; // managed
event_data->extract_socketcall_params = false;
}

/* If we return a syscall id we just set it */
event_data->event_info.syscall_data.id = return_code;
/* we need to use `return_code + 1` because return_code
* is the enter event.
*/
record_event_all_consumers(return_code + is_exit,
return_code == PPME_GENERIC_E ? UF_ALWAYS_DROP : UF_USED,
event_data, is_exit ? KMOD_PROG_SYS_EXIT : KMOD_PROG_SYS_ENTER);
return NULL; // managed
}

/* If we return a syscall id we just set it */
event_data->event_info.syscall_data.id = return_code;
return event_data;
}

#endif /* _HAS_SOCKETCALL */

static int preload_params(struct event_filler_arguments *filler_args, bool extract_socketcall_params)
{
#ifdef _HAS_SOCKETCALL
if (extract_socketcall_params)
{
return load_socketcall_params(filler_args);
}
#endif
ppm_syscall_get_arguments(current, filler_args->regs, filler_args->args);
return 0;
}
Expand Down Expand Up @@ -2214,12 +2204,13 @@ TRACEPOINT_PROBE(syscall_enter_probe, struct pt_regs *regs, long id)

g_n_tracepoint_hit_inc();

#ifdef _HAS_SOCKETCALL
if (manage_socketcall(&event_data, socketcall_syscall_id, false) == NULL)
if(event_data.event_info.syscall_data.id == socketcall_syscall_id)
{
return;
if(manage_socketcall(&event_data, socketcall_syscall_id, false) == NULL)
{
return;
}
}
#endif

/* We need to set here the `syscall_id` because it could change in case of socketcalls */
table_index = event_data.event_info.syscall_data.id - SYSCALL_TABLE_ID0;
Expand Down Expand Up @@ -2337,12 +2328,13 @@ TRACEPOINT_PROBE(syscall_exit_probe, struct pt_regs *regs, long ret)

g_n_tracepoint_hit_inc();

#ifdef _HAS_SOCKETCALL
if (manage_socketcall(&event_data, socketcall_syscall_id, true) == NULL)
if(event_data.event_info.syscall_data.id == socketcall_syscall_id)
{
return;
if (manage_socketcall(&event_data, socketcall_syscall_id, true) == NULL)
{
return;
}
}
#endif

table_index = event_data.event_info.syscall_data.id - SYSCALL_TABLE_ID0;
if (unlikely(table_index < 0 || table_index >= SYSCALL_TABLE_SIZE))
Expand Down
45 changes: 35 additions & 10 deletions driver/modern_bpf/helpers/extract/extract_from_kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
#include <helpers/base/read_from_task.h>
#include <driver/ppm_flag_helpers.h>

#ifdef CAPTURE_SOCKETCALL
#include <syscall.h>
#endif

#define __NR_ia32_socketcall 102

/* Used to convert from page number to KB. */
#define DO_PAGE_SHIFT(x) (x) << (IOC_PAGE_SHIFT - 10)
Expand Down Expand Up @@ -53,6 +53,25 @@ static __always_inline u32 extract__syscall_id(struct pt_regs *regs)
#endif
}

static __always_inline bool extract__32bit_syscall()
{
uint32_t status;
struct task_struct *task = get_current_task();

#if defined(__TARGET_ARCH_x86)
READ_TASK_FIELD_INTO(&status, task, thread_info.status);
return status & TS_COMPAT;
#elif defined(__TARGET_ARCH_arm64)
READ_TASK_FIELD_INTO(&status, task, thread_info.flags);
return status & _TIF_32BIT;
#elif defined(__TARGET_ARCH_s390)
READ_TASK_FIELD_INTO(&status, task, thread_info.flags);
return status & _TIF_31BIT;
#else
return false;
#endif
}

/**
* @brief Extract a specific syscall argument
*
Expand All @@ -65,11 +84,7 @@ static __always_inline unsigned long extract__syscall_argument(struct pt_regs *r
{
unsigned long arg;
#if defined(__TARGET_ARCH_x86)
// TODO: somehow use syscalls_dispatcher__check_32bit_syscalls()
uint32_t status;
struct task_struct *task = get_current_task();
READ_TASK_FIELD_INTO(&status, task, thread_info.status);
if (status & TS_COMPAT)
if (extract__32bit_syscall())
{
switch(idx)
{
Expand Down Expand Up @@ -139,15 +154,25 @@ static __always_inline unsigned long extract__syscall_argument(struct pt_regs *r
*/
static __always_inline void extract__network_args(void *argv, int num, struct pt_regs *regs)
{
#ifdef CAPTURE_SOCKETCALL
int id = extract__syscall_id(regs);
bool is_32bit_syscall = extract__32bit_syscall();
#ifdef __NR_socketcall
if(id == __NR_socketcall)
#elif defined(__TARGET_ARCH_x86)
if(is_32bit_syscall && id == __NR_ia32_socketcall)
#else
if (false)
#endif
{
size_t size = sizeof(unsigned long);
if (extract__32bit_syscall())
{
size = sizeof(u32);
}
unsigned long args_pointer = extract__syscall_argument(regs, 1);
bpf_probe_read_user(argv, num * sizeof(unsigned long), (void*)args_pointer);
bpf_probe_read_user(argv, num * size, (void*)args_pointer);
return;
}
#endif
for (int i = 0; i < num; i++)
{
unsigned long *dst = (unsigned long *)argv;
Expand Down
22 changes: 5 additions & 17 deletions driver/modern_bpf/helpers/interfaces/syscalls_dispatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,7 @@

static __always_inline bool syscalls_dispatcher__check_32bit_syscalls()
{
uint32_t status;
struct task_struct *task = get_current_task();

#if defined(__TARGET_ARCH_x86)
READ_TASK_FIELD_INTO(&status, task, thread_info.status);
return status & TS_COMPAT;
#elif defined(__TARGET_ARCH_arm64)
READ_TASK_FIELD_INTO(&status, task, thread_info.flags);
return status & _TIF_32BIT;
#elif defined(__TARGET_ARCH_s390)
READ_TASK_FIELD_INTO(&status, task, thread_info.flags);
return status & _TIF_31BIT;
#else
return false;
#endif
return extract__32bit_syscall();
}

static __always_inline bool syscalls_dispatcher__64bit_interesting_syscall(u32 syscall_id)
Expand All @@ -41,7 +27,6 @@ static __always_inline u32 syscalls_dispatcher__convert_ia32_to_64(u32 syscall_i
return maps__ia32_to_64(syscall_id);
}

#ifdef CAPTURE_SOCKETCALL
static __always_inline long convert_network_syscalls(struct pt_regs *regs)
{
int socketcall_id = (int)extract__syscall_argument(regs, 0);
Expand Down Expand Up @@ -155,6 +140,9 @@ static __always_inline long convert_network_syscalls(struct pt_regs *regs)
}

// Reset NR_socketcall to send a generic even with correct id
#ifdef __NR_socketcall
return __NR_socketcall;
}
#else
return -1;
#endif
}
Loading

0 comments on commit 79cfe5c

Please sign in to comment.