diff --git a/driver/bpf/fillers.h b/driver/bpf/fillers.h index 5f10ab7d15d..f208870b237 100644 --- a/driver/bpf/fillers.h +++ b/driver/bpf/fillers.h @@ -3295,94 +3295,72 @@ FILLER(sys_open_by_handle_at_x, true) FILLER(sys_io_uring_setup_x, true) { - long retval; - int res; - unsigned long val; - unsigned long sq_entries; - unsigned long cq_entries; - unsigned long flags; - unsigned long sq_thread_cpu; - unsigned long sq_thread_idle; - unsigned long features = 0; - -#ifdef __NR_io_uring_setup - struct io_uring_params params; -#endif - retval = bpf_syscall_get_retval(data->ctx); - res = bpf_val_to_ring(data, retval); - if (res != PPM_SUCCESS) - return res; - /* - * entries + /* All these params are sent equal to `0` if `__NR_io_uring_setup` + * syscall is not defined. */ - val = bpf_syscall_get_argument(data, 0); - res = bpf_val_to_ring(data, val); - if (res != PPM_SUCCESS) - return res; + u32 sq_entries = 0; + u32 cq_entries = 0; + u32 flags = 0; + u32 sq_thread_cpu = 0; + u32 sq_thread_idle = 0; + u32 features = 0; + /* If the syscall is defined use the syscall data */ #ifdef __NR_io_uring_setup - /* - * io_uring_params: we get the data structure, and put its fields in the buffer one by one + struct io_uring_params params = {0}; + unsigned long params_pointer = bpf_syscall_get_argument(data, 1); + /* if the call fails we don't care since `bpf_probe_read` under the hood memsets + * the destination memory to `0` */ - val = bpf_syscall_get_argument(data, 1); - if (bpf_probe_read(¶ms, sizeof(struct io_uring_params), (void *)val)) { - return PPM_FAILURE_INVALID_USER_MEMORY; - } + bpf_probe_read(¶ms, sizeof(struct io_uring_params), (void *)params_pointer); sq_entries = params.sq_entries; cq_entries = params.cq_entries; flags = io_uring_setup_flags_to_scap(params.flags); sq_thread_cpu = params.sq_thread_cpu; sq_thread_idle = params.sq_thread_idle; + + /* We need this ifdef because `features` field is defined into the + * `struct io_uring_params` only if the `IORING_FEAT_SINGLE_MMAP` is + * defined. + */ #ifdef IORING_FEAT_SINGLE_MMAP features = io_uring_setup_feats_to_scap(params.features); -#endif -#else - sq_entries = 0; - cq_entries = 0; - flags = 0; - sq_thread_cpu = 0; - sq_thread_idle = 0; - features = 0; #endif +#endif /* __NR_io_uring_setup */ - /* - * sq_entries (extracted from io_uring_params structure) - */ + /* Parameter 1: res (type: PT_ERRNO) */ + long retval = bpf_syscall_get_retval(data->ctx); + int res = bpf_val_to_ring(data, retval); + CHECK_RES(res); + + /* Parameter 2: entries (type: PT_UINT32) */ + u32 entries = (u32)bpf_syscall_get_argument(data, 0); + res = bpf_val_to_ring(data, entries); + CHECK_RES(res); + + /* Parameter 3: sq_entries (type: PT_UINT32) */ res = bpf_val_to_ring(data, sq_entries); - if (res != PPM_SUCCESS) - return res; - /* - * cq_entries (extracted from io_uring_params structure) - */ + CHECK_RES(res); + + /* Parameter 4: cq_entries (type: PT_UINT32) */ res = bpf_val_to_ring(data, cq_entries); - if (res != PPM_SUCCESS) - return res; - /* - * flags (extracted from io_uring_params structure) - * Already converted in ppm portable representation - */ + CHECK_RES(res); + + /* Parameter 5: flags (type: PT_FLAGS32) */ res = bpf_val_to_ring(data, flags); - if (res != PPM_SUCCESS) - return res; - /* - * sq_thread_cpu (extracted from io_uring_params structure) - */ + CHECK_RES(res); + + /* Parameter 6: sq_thread_cpu (type: PT_UINT32) */ res = bpf_val_to_ring(data, sq_thread_cpu); - if (res != PPM_SUCCESS) - return res; - /* - * sq_thread_idle (extracted from io_uring_params structure) - */ + CHECK_RES(res); + + /* Parameter 7: sq_thread_idle (type: PT_UINT32) */ res = bpf_val_to_ring(data, sq_thread_idle); - if (res != PPM_SUCCESS) - return res; - /* - * features (extracted from io_uring_params structure) - * Already converted in ppm portable representation - */ - res = bpf_val_to_ring(data, features); - return res; + CHECK_RES(res); + + /* Parameter 8: features (type: PT_FLAGS32) */ + return bpf_val_to_ring(data, features); } FILLER(sys_io_uring_enter_x, true) diff --git a/driver/modern_bpf/definitions/events_dimensions.h b/driver/modern_bpf/definitions/events_dimensions.h index 704685f128d..82aa85fc5d3 100644 --- a/driver/modern_bpf/definitions/events_dimensions.h +++ b/driver/modern_bpf/definitions/events_dimensions.h @@ -156,6 +156,8 @@ #define IO_URING_ENTER_X_SIZE HEADER_LEN + sizeof(int64_t) * 2 + sizeof(uint32_t) * 4 + PARAM_LEN * 6 #define IO_URING_REGISTER_E_SIZE HEADER_LEN #define IO_URING_REGISTER_X_SIZE HEADER_LEN + sizeof(int64_t) * 2 + sizeof(uint16_t) + sizeof(uint64_t) + sizeof(uint32_t) + PARAM_LEN * 5 +#define IO_URING_SETUP_E_SIZE HEADER_LEN +#define IO_URING_SETUP_X_SIZE HEADER_LEN + sizeof(int64_t) + sizeof(uint32_t) * 7 + PARAM_LEN * 8 /* Generic tracepoints events. */ #define PROC_EXIT_SIZE HEADER_LEN + sizeof(int64_t) * 2 + sizeof(uint8_t) * 2 + PARAM_LEN * 4 diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/io_uring_setup.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/io_uring_setup.bpf.c new file mode 100644 index 00000000000..8254863ee33 --- /dev/null +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/io_uring_setup.bpf.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2023 The Falco Authors. + * + * This file is dual licensed under either the MIT or GPL 2. See MIT.txt + * or GPL2.txt for full copies of the license. + */ + +#include + +/*=============================== ENTER EVENT ===========================*/ + +SEC("tp_btf/sys_enter") +int BPF_PROG(io_uring_setup_e, + struct pt_regs *regs, + long id) +{ + struct ringbuf_struct ringbuf; + if(!ringbuf__reserve_space(&ringbuf, IO_URING_SETUP_E_SIZE)) + { + return 0; + } + + ringbuf__store_event_header(&ringbuf, PPME_SYSCALL_IO_URING_SETUP_E); + + /*=============================== COLLECT PARAMETERS ===========================*/ + + // Here we have no parameters to collect. + + /*=============================== COLLECT PARAMETERS ===========================*/ + + ringbuf__submit_event(&ringbuf); + + return 0; +} + +/*=============================== ENTER EVENT ===========================*/ + +/*=============================== EXIT EVENT ===========================*/ + +SEC("tp_btf/sys_exit") +int BPF_PROG(io_uring_setup_x, + struct pt_regs *regs, + long ret) +{ + struct ringbuf_struct ringbuf; + if(!ringbuf__reserve_space(&ringbuf, IO_URING_SETUP_X_SIZE)) + { + return 0; + } + + ringbuf__store_event_header(&ringbuf, PPME_SYSCALL_IO_URING_SETUP_X); + + /*=============================== COLLECT PARAMETERS ===========================*/ + + /* Parameter 1: res (type: PT_ERRNO) */ + ringbuf__store_s64(&ringbuf, ret); + + /* Parameter 2: entries (type: PT_UINT32) */ + u32 entries = (u32)extract__syscall_argument(regs, 0); + ringbuf__store_u32(&ringbuf, entries); + + /* Get the second syscall argument that is a `struct io_uring_params*` + * This struct is defined since kernel release 5.1 + */ + unsigned long params_pointer = extract__syscall_argument(regs, 1); + struct io_uring_params params = {0}; + bpf_probe_read_user((void *)¶ms, sizeof(struct io_uring_params), (void *)params_pointer); + + /* Parameter 3: sq_entries (type: PT_UINT32) */ + ringbuf__store_u32(&ringbuf, params.sq_entries); + + /* Parameter 4: cq_entries (type: PT_UINT32) */ + ringbuf__store_u32(&ringbuf, params.cq_entries); + + /* Parameter 5: flags (type: PT_FLAGS32) */ + ringbuf__store_u32(&ringbuf, (u32)io_uring_setup_flags_to_scap(params.flags)); + + /* Parameter 6: sq_thread_cpu (type: PT_UINT32) */ + ringbuf__store_u32(&ringbuf, params.sq_thread_cpu); + + /* Parameter 7: sq_thread_idle (type: PT_UINT32) */ + ringbuf__store_u32(&ringbuf, params.sq_thread_idle); + + /* Parameter 8: features (type: PT_FLAGS32) */ + ringbuf__store_u32(&ringbuf, (u32)io_uring_setup_feats_to_scap(params.features)); + + /*=============================== COLLECT PARAMETERS ===========================*/ + + ringbuf__submit_event(&ringbuf); + + return 0; +} + +/*=============================== EXIT EVENT ===========================*/ diff --git a/driver/ppm_fillers.c b/driver/ppm_fillers.c index 9f7e813343d..04cd7c63d41 100644 --- a/driver/ppm_fillers.c +++ b/driver/ppm_fillers.c @@ -5089,88 +5089,84 @@ int f_sys_open_by_handle_at_x(struct event_filler_arguments *args) return add_sentinel(args); } -int f_sys_io_uring_setup_x (struct event_filler_arguments *args) +int f_sys_io_uring_setup_x(struct event_filler_arguments *args) { - int res; - unsigned long val; - unsigned long sq_entries = 0; - unsigned long cq_entries = 0; - unsigned long flags = 0; - unsigned long sq_thread_cpu = 0; - unsigned long sq_thread_idle = 0; - unsigned long features = 0; - -#ifdef __NR_io_uring_setup - struct io_uring_params params; -#endif - - int64_t retval = (int64_t)syscall_get_return_value(current, args->regs); - res = val_to_ring(args, retval, 0, false, 0); - if (unlikely(res != PPM_SUCCESS)) - return res; + int res = 0; + long retval = 0; + unsigned long val = 0; - /* entries */ - syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); - res = val_to_ring(args, val, 0, true, 0); - if (unlikely(res != PPM_SUCCESS)) - return res; + /* All these params are sent equal to `0` if `__NR_io_uring_setup` + * syscall is not defined. + */ + u32 sq_entries = 0; + u32 cq_entries = 0; + u32 flags = 0; + u32 sq_thread_cpu = 0; + u32 sq_thread_idle = 0; + u32 features = 0; #ifdef __NR_io_uring_setup - /* - * io_uring_params: we get the data structure, and put its fields in the buffer one by one - */ + struct io_uring_params params = {0}; syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); res = ppm_copy_from_user(¶ms, (void *)val, sizeof(struct io_uring_params)); - if (unlikely(res != 0)) - return PPM_FAILURE_INVALID_USER_MEMORY; + if(unlikely(res != 0)) + { + sq_entries = 0; + cq_entries = 0; + flags = 0; + sq_thread_cpu = 0; + sq_thread_idle = 0; + features = 0; + } sq_entries = params.sq_entries; cq_entries = params.cq_entries; flags = io_uring_setup_flags_to_scap(params.flags); sq_thread_cpu = params.sq_thread_cpu; sq_thread_idle = params.sq_thread_idle; + + /* We need this ifdef because `features` field is defined into the + * `struct io_uring_params` only if the `IORING_FEAT_SINGLE_MMAP` is + * defined. + */ #ifdef IORING_FEAT_SINGLE_MMAP features = io_uring_setup_feats_to_scap(params.features); #endif -#endif // __NR_io_uring_setup - /* - * sq_entries (extracted from io_uring_params structure) - */ +#endif /* __NR_io_uring_setup */ + + /* Parameter 1: res (type: PT_ERRNO) */ + retval = (long)syscall_get_return_value(current, args->regs); + res = val_to_ring(args, retval, 0, false, 0); + CHECK_RES(res); + + /* Parameter 2: entries (type: PT_UINT32) */ + syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); + res = val_to_ring(args, val, 0, true, 0); + CHECK_RES(res); + + /* Parameter 3: sq_entries (type: PT_UINT32) */ res = val_to_ring(args, sq_entries, 0, true, 0); - if (unlikely(res != PPM_SUCCESS)) - return res; - /* - * cq_entries (extracted from io_uring_params structure) - */ + CHECK_RES(res); + + /* Parameter 4: cq_entries (type: PT_UINT32) */ res = val_to_ring(args, cq_entries, 0, true, 0); - if (unlikely(res != PPM_SUCCESS)) - return res; - /* - * flags (extracted from io_uring_params structure) - * Already converted in ppm portable representation - */ + CHECK_RES(res); + + /* Parameter 5: flags (type: PT_FLAGS32) */ res = val_to_ring(args, flags, 0, true, 0); - if (unlikely(res != PPM_SUCCESS)) - return res; - /* - * sq_thread_cpu (extracted from io_uring_params structure) - */ + CHECK_RES(res); + + /* Parameter 6: sq_thread_cpu (type: PT_UINT32) */ res = val_to_ring(args, sq_thread_cpu, 0, true, 0); - if (unlikely(res != PPM_SUCCESS)) - return res; - /* - * sq_thread_idle (extracted from io_uring_params structure) - */ + CHECK_RES(res); + + /* Parameter 7: sq_thread_idle (type: PT_UINT32) */ res = val_to_ring(args, sq_thread_idle, 0, true, 0); - if (unlikely(res != PPM_SUCCESS)) - return res; - /* - * features (extracted from io_uring_params structure) - * Already converted in ppm portable representation - */ + CHECK_RES(res); + + /* Parameter 8: features (type: PT_FLAGS32) */ res = val_to_ring(args, features, 0, true, 0); - if (unlikely(res != PPM_SUCCESS)) - return res; + CHECK_RES(res); return add_sentinel(args); } diff --git a/test/drivers/test_suites/syscall_enter_suite/io_uring_setup_e.cpp b/test/drivers/test_suites/syscall_enter_suite/io_uring_setup_e.cpp new file mode 100644 index 00000000000..d3aca19179e --- /dev/null +++ b/test/drivers/test_suites/syscall_enter_suite/io_uring_setup_e.cpp @@ -0,0 +1,42 @@ +#include "../../event_class/event_class.h" + +#if defined(__NR_io_uring_setup) + +#include + +TEST(SyscallEnter, io_uring_setupE) +{ + auto evt_test = get_syscall_event_test(__NR_io_uring_setup, ENTER_EVENT); + + evt_test->enable_capture(); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + uint32_t entries = 4; + struct io_uring_params* params_pointer = NULL; + assert_syscall_state(SYSCALL_FAILURE, "io_uring_setup", syscall(__NR_io_uring_setup, entries, params_pointer)); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + evt_test->disable_capture(); + + evt_test->assert_event_presence(); + + if(HasFatalFailure()) + { + return; + } + + evt_test->parse_event(); + + evt_test->assert_header(); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + // Here we have no parameters to assert. + + /*=============================== ASSERT PARAMETERS ===========================*/ + + evt_test->assert_num_params_pushed(0); +} +#endif diff --git a/test/drivers/test_suites/syscall_exit_suite/io_uring_setup_x.cpp b/test/drivers/test_suites/syscall_exit_suite/io_uring_setup_x.cpp new file mode 100644 index 00000000000..e3976a2dab1 --- /dev/null +++ b/test/drivers/test_suites/syscall_exit_suite/io_uring_setup_x.cpp @@ -0,0 +1,141 @@ +#include "../../event_class/event_class.h" + +#if defined(__NR_io_uring_setup) + +#include + +TEST(SyscallExit, io_uring_setupX) +{ + auto evt_test = get_syscall_event_test(__NR_io_uring_setup, EXIT_EVENT); + + evt_test->enable_capture(); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + /* There could be cases in which the structure `io_uring_params` + * doesn't have the filed `feature`, so define it to `0` as default. + */ + uint32_t expected_features = 0; + uint32_t entries = 4; + struct io_uring_params params = {0}; + params.sq_entries = 5; + params.cq_entries = 6; + /* The call should fail since we specified only `IORING_SETUP_SQ_AFF` + * but not `IORING_SETUP_SQPOLL` + */ + params.flags = IORING_SETUP_SQ_AFF; + params.sq_thread_cpu = 7; + params.sq_thread_idle = 8; +#ifdef IORING_FEAT_SINGLE_MMAP + params.features = IORING_FEAT_NODROP; + expected_features = PPM_IORING_FEAT_NODROP; +#endif + assert_syscall_state(SYSCALL_FAILURE, "io_uring_setup", syscall(__NR_io_uring_setup, entries, ¶ms)); + int64_t errno_value = -errno; + + /*=============================== TRIGGER SYSCALL ===========================*/ + + evt_test->disable_capture(); + + evt_test->assert_event_presence(); + + if(HasFatalFailure()) + { + return; + } + + evt_test->parse_event(); + + evt_test->assert_header(); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + /* Parameter 1: res (type: PT_ERRNO) */ + evt_test->assert_numeric_param(1, (int64_t)errno_value); + + /* Parameter 2: entries (type: PT_UINT32) */ + evt_test->assert_numeric_param(2, (uint32_t)entries); + + /* Parameter 3: sq_entries (type: PT_UINT32) */ + evt_test->assert_numeric_param(3, (uint32_t)params.sq_entries); + + /* Parameter 4: cq_entries (type: PT_UINT32) */ + evt_test->assert_numeric_param(4, (uint32_t)params.cq_entries); + + /* Parameter 5: flags (type: PT_FLAGS32) */ + evt_test->assert_numeric_param(5, (uint32_t)PPM_IORING_SETUP_SQ_AFF); + + /* Parameter 6: sq_thread_cpu (type: PT_UINT32) */ + evt_test->assert_numeric_param(6, (uint32_t)params.sq_thread_cpu); + + /* Parameter 7: sq_thread_idle (type: PT_UINT32) */ + evt_test->assert_numeric_param(7, (uint32_t)params.sq_thread_idle); + + /* Parameter 8: features (type: PT_FLAGS32) */ + evt_test->assert_numeric_param(8, (uint32_t)expected_features); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + evt_test->assert_num_params_pushed(8); +} + +TEST(SyscallExit, io_uring_setupX_with_NULL_pointer) +{ + auto evt_test = get_syscall_event_test(__NR_io_uring_setup, EXIT_EVENT); + + evt_test->enable_capture(); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + uint32_t entries = 4; + struct io_uring_params* params_pointer = NULL; + assert_syscall_state(SYSCALL_FAILURE, "io_uring_setup", syscall(__NR_io_uring_setup, entries, params_pointer)); + int64_t errno_value = -errno; + + /*=============================== TRIGGER SYSCALL ===========================*/ + + evt_test->disable_capture(); + + evt_test->assert_event_presence(); + + if(HasFatalFailure()) + { + return; + } + + evt_test->parse_event(); + + evt_test->assert_header(); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + /* Parameter 1: res (type: PT_ERRNO) */ + evt_test->assert_numeric_param(1, (int64_t)errno_value); + + /* Parameter 2: entries (type: PT_UINT32) */ + evt_test->assert_numeric_param(2, (uint32_t)entries); + + /* Parameter 3: sq_entries (type: PT_UINT32) */ + evt_test->assert_numeric_param(3, (uint32_t)0); + + /* Parameter 4: cq_entries (type: PT_UINT32) */ + evt_test->assert_numeric_param(4, (uint32_t)0); + + /* Parameter 5: flags (type: PT_FLAGS32) */ + evt_test->assert_numeric_param(5, (uint32_t)0); + + /* Parameter 6: sq_thread_cpu (type: PT_UINT32) */ + evt_test->assert_numeric_param(6, (uint32_t)0); + + /* Parameter 7: sq_thread_idle (type: PT_UINT32) */ + evt_test->assert_numeric_param(7, (uint32_t)0); + + /* Parameter 8: features (type: PT_FLAGS32) */ + evt_test->assert_numeric_param(8, (uint32_t)0); + + /*=============================== ASSERT PARAMETERS ===========================*/ + + evt_test->assert_num_params_pushed(8); +} + +#endif diff --git a/userspace/libpman/src/events_prog_names.h b/userspace/libpman/src/events_prog_names.h index 295effc1bb2..b5f35f0c844 100644 --- a/userspace/libpman/src/events_prog_names.h +++ b/userspace/libpman/src/events_prog_names.h @@ -211,6 +211,8 @@ static const char* event_prog_names[PPM_EVENT_MAX] = { [PPME_SYSCALL_IO_URING_ENTER_X] = "io_uring_enter_x", [PPME_SYSCALL_IO_URING_REGISTER_E] = "io_uring_register_e", [PPME_SYSCALL_IO_URING_REGISTER_X] = "io_uring_register_x", + [PPME_SYSCALL_IO_URING_SETUP_E] = "io_uring_setup_e", + [PPME_SYSCALL_IO_URING_SETUP_X] = "io_uring_setup_x", }; /* Some events can require more than one bpf program to collect all the data. */