From 79cfe5c9ab2bf50dde080f92b55f52dafee93fca Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Wed, 12 Jul 2023 17:30:50 +0200 Subject: [PATCH] cleanup(driver): made socketcall support fully dynamic. 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 --- driver/bpf/plumbing_helpers.h | 16 +--- driver/bpf/probe.c | 4 - driver/feature_gates.h | 31 -------- driver/main.c | 78 +++++++++---------- .../helpers/extract/extract_from_kernel.h | 45 ++++++++--- .../helpers/interfaces/syscalls_dispatcher.h | 22 ++---- .../attached/dispatchers/syscall_enter.bpf.c | 33 +++++--- .../attached/dispatchers/syscall_exit.bpf.c | 42 ++++++---- driver/ppm_events.h | 4 - 9 files changed, 130 insertions(+), 145 deletions(-) diff --git a/driver/bpf/plumbing_helpers.h b/driver/bpf/plumbing_helpers.h index 48001c484b7..c91f150e6d3 100644 --- a/driver/bpf/plumbing_helpers.h +++ b/driver/bpf/plumbing_helpers.h @@ -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 -#endif +#include "../socketcall_to_syscall.h" #define _READ(P) ({ typeof(P) _val; \ bpf_probe_read_kernel(&_val, sizeof(_val), &P); \ @@ -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; @@ -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); @@ -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 diff --git a/driver/bpf/probe.c b/driver/bpf/probe.c index 2e3210476f3..28a5c9e604c 100644 --- a/driver/bpf/probe.c +++ b/driver/bpf/probe.c @@ -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; @@ -92,7 +91,6 @@ BPF_PROBE("raw_syscalls/", sys_enter, sys_enter_args) id = return_code; } } -#endif enabled = is_syscall_interesting(id); if(!enabled) @@ -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; @@ -204,7 +201,6 @@ BPF_PROBE("raw_syscalls/", sys_exit, sys_exit_args) id = return_code; } } -#endif enabled = is_syscall_interesting(id); if (!enabled) diff --git a/driver/feature_gates.h b/driver/feature_gates.h index 21e08e647e5..e4808539b2b 100644 --- a/driver/feature_gates.h +++ b/driver/feature_gates.h @@ -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 /////////////////////////////// @@ -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 @@ -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__ */ diff --git a/driver/main.c b/driver/main.c index 4653a97f788..de1f4dcebc1 100644 --- a/driver/main.c +++ b/driver/main.c @@ -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 @@ -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) @@ -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; } @@ -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; @@ -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)) diff --git a/driver/modern_bpf/helpers/extract/extract_from_kernel.h b/driver/modern_bpf/helpers/extract/extract_from_kernel.h index 3134c9e00ef..cda3ccb0e40 100644 --- a/driver/modern_bpf/helpers/extract/extract_from_kernel.h +++ b/driver/modern_bpf/helpers/extract/extract_from_kernel.h @@ -11,9 +11,9 @@ #include #include -#ifdef CAPTURE_SOCKETCALL #include -#endif + +#define __NR_ia32_socketcall 102 /* Used to convert from page number to KB. */ #define DO_PAGE_SHIFT(x) (x) << (IOC_PAGE_SHIFT - 10) @@ -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 * @@ -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) { @@ -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; diff --git a/driver/modern_bpf/helpers/interfaces/syscalls_dispatcher.h b/driver/modern_bpf/helpers/interfaces/syscalls_dispatcher.h index 0ea64d2be08..8c625749b85 100644 --- a/driver/modern_bpf/helpers/interfaces/syscalls_dispatcher.h +++ b/driver/modern_bpf/helpers/interfaces/syscalls_dispatcher.h @@ -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) @@ -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); @@ -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 +} diff --git a/driver/modern_bpf/programs/attached/dispatchers/syscall_enter.bpf.c b/driver/modern_bpf/programs/attached/dispatchers/syscall_enter.bpf.c index 0a0800362e8..6902806610c 100644 --- a/driver/modern_bpf/programs/attached/dispatchers/syscall_enter.bpf.c +++ b/driver/modern_bpf/programs/attached/dispatchers/syscall_enter.bpf.c @@ -16,28 +16,43 @@ int BPF_PROG(sys_enter, struct pt_regs *regs, long syscall_id) { + int socketcall_syscall_id = -1; + +#ifdef __NR_socketcall + socketcall_syscall_id = __NR_socketcall; +#endif if(syscalls_dispatcher__check_32bit_syscalls()) { -#if defined(__TARGET_ARCH_x86) - syscall_id = syscalls_dispatcher__convert_ia32_to_64(syscall_id); - if(syscall_id == (u32)-1) + if (syscall_id == __NR_ia32_socketcall) { - return 0; + socketcall_syscall_id = __NR_ia32_socketcall; } + else + { +#if defined(__TARGET_ARCH_x86) + syscall_id = syscalls_dispatcher__convert_ia32_to_64(syscall_id); + if(syscall_id == (u32)-1) + { + return 0; + } #else - // TODO: unsupported - return 0; + // TODO: unsupported + return 0; #endif + } } -#ifdef CAPTURE_SOCKETCALL /* we convert it here in this way the syscall will be treated exactly as the original one */ - if(syscall_id == __NR_socketcall) + if(syscall_id == socketcall_syscall_id) { syscall_id = convert_network_syscalls(regs); + if (syscall_id == -1) + { + // We can't do anything since modern bpf filler jump table is syscall indexed + return 0; + } } -#endif if(!syscalls_dispatcher__64bit_interesting_syscall(syscall_id)) { diff --git a/driver/modern_bpf/programs/attached/dispatchers/syscall_exit.bpf.c b/driver/modern_bpf/programs/attached/dispatchers/syscall_exit.bpf.c index 287e65c9515..5307649c0a9 100644 --- a/driver/modern_bpf/programs/attached/dispatchers/syscall_exit.bpf.c +++ b/driver/modern_bpf/programs/attached/dispatchers/syscall_exit.bpf.c @@ -20,38 +20,54 @@ int BPF_PROG(sys_exit, struct pt_regs *regs, long ret) { + int socketcall_syscall_id = -1; + +#ifdef __NR_socketcall + socketcall_syscall_id = __NR_socketcall; +#endif + u32 syscall_id = extract__syscall_id(regs); if(syscalls_dispatcher__check_32bit_syscalls()) { + if (syscall_id == __NR_ia32_socketcall) + { + socketcall_syscall_id = __NR_ia32_socketcall; + } + else + { #if defined(__TARGET_ARCH_x86) - /* + /* * When a process does execve from 64bit to 32bit, TS_COMPAT is marked true * but the id of the syscall is __NR_execve, so to correctly parse it we need to * use 64bit syscall table. On 32bit __NR_execve is equal to __NR_ia32_oldolduname * which is a very old syscall, not used anymore by most applications - */ - if (syscall_id != X86_64_NR_EXECVE && syscall_id != X86_64_NR_EXECVEAT) - { - syscall_id = syscalls_dispatcher__convert_ia32_to_64(syscall_id); - if(syscall_id == (u32)-1) + */ + if(syscall_id != X86_64_NR_EXECVE && syscall_id != X86_64_NR_EXECVEAT) { - return 0; + syscall_id = syscalls_dispatcher__convert_ia32_to_64(syscall_id); + if(syscall_id == (u32)-1) + { + return 0; + } } - } #else - // TODO: unsupported - return 0; + // TODO: unsupported + return 0; #endif + } } -#ifdef CAPTURE_SOCKETCALL /* we convert it here in this way the syscall will be treated exactly as the original one */ - if(syscall_id == __NR_socketcall) + if(syscall_id == socketcall_syscall_id) { syscall_id = convert_network_syscalls(regs); + if (syscall_id == -1) + { + // We can't do anything since modern bpf filler jump table is syscall indexed + return 0; + } } -#endif if(!syscalls_dispatcher__64bit_interesting_syscall(syscall_id)) { diff --git a/driver/ppm_events.h b/driver/ppm_events.h index 592da183fae..113d4ad8713 100644 --- a/driver/ppm_events.h +++ b/driver/ppm_events.h @@ -19,10 +19,6 @@ or GPL2.txt for full copies of the license. #include #endif -#if defined(__NR_socketcall) || (defined(CONFIG_X86_64) && defined(CONFIG_IA32_EMULATION)) - #define _HAS_SOCKETCALL -#endif - #include "ppm_events_public.h" /*