From 0a3ff98288e2905cd71695251013702072750a44 Mon Sep 17 00:00:00 2001 From: Andrea Terzolo Date: Thu, 5 Dec 2024 10:51:51 +0100 Subject: [PATCH] new: extend WRITE_X/PWRITE_X Signed-off-by: Andrea Terzolo --- driver/SCHEMA_VERSION | 2 +- driver/bpf/fillers.h | 48 +++++- driver/event_table.c | 23 ++- driver/fillers_table.c | 2 +- .../syscall_dispatched_events/pwrite64.bpf.c | 14 +- .../syscall_dispatched_events/write.bpf.c | 10 +- driver/ppm_fillers.c | 76 +++++++- driver/ppm_fillers.h | 1 + .../actions_suite/dynamic_snaplen.cpp | 4 +- .../syscall_exit_suite/pwrite64_x.cpp | 33 +++- .../syscall_exit_suite/write_x.cpp | 36 +++- .../engines/savefile/converter.cpp | 163 +++++++++++++++++- userspace/libscap/engine/gvisor/fillers.cpp | 22 ++- userspace/libscap/engine/gvisor/fillers.h | 9 +- userspace/libscap/engine/gvisor/parsers.cpp | 9 +- .../engine/savefile/converter/table.cpp | 14 ++ userspace/libscap/scap.h | 2 +- userspace/libscap/scap_event.c | 2 + userspace/libsinsp/test/events_param.ut.cpp | 32 ++-- .../libsinsp/test/parsers/parse_pwrite.cpp | 88 ++++++++++ .../libsinsp/test/parsers/parse_write.cpp | 84 +++++++++ .../test/scap_files/converter_tests.cpp | 46 +++++ 22 files changed, 664 insertions(+), 56 deletions(-) create mode 100644 userspace/libsinsp/test/parsers/parse_pwrite.cpp create mode 100644 userspace/libsinsp/test/parsers/parse_write.cpp diff --git a/driver/SCHEMA_VERSION b/driver/SCHEMA_VERSION index fd2a01863fd..fcdb2e109f6 100644 --- a/driver/SCHEMA_VERSION +++ b/driver/SCHEMA_VERSION @@ -1 +1 @@ -3.1.0 +4.0.0 diff --git a/driver/bpf/fillers.h b/driver/bpf/fillers.h index 64bf7480ff5..45ddc58ff53 100644 --- a/driver/bpf/fillers.h +++ b/driver/bpf/fillers.h @@ -454,10 +454,19 @@ FILLER(sys_write_x, true) { /* If the syscall doesn't fail we use the return value as `size` * otherwise we need to rely on the syscall parameter provided by the user. */ - unsigned long bytes_to_read = retval > 0 ? retval : bpf_syscall_get_argument(data, 2); + size_t size = bpf_syscall_get_argument(data, 2); + unsigned long bytes_to_read = retval > 0 ? retval : size; unsigned long sent_data_pointer = bpf_syscall_get_argument(data, 1); - data->fd = bpf_syscall_get_argument(data, 0); - return __bpf_val_to_ring(data, sent_data_pointer, bytes_to_read, PT_BYTEBUF, -1, true, USER); + data->fd = (int32_t)bpf_syscall_get_argument(data, 0); + res = __bpf_val_to_ring(data, sent_data_pointer, bytes_to_read, PT_BYTEBUF, -1, true, USER); + CHECK_RES(res); + + /* Parameter 3: fd (type: PT_FD) */ + res = bpf_push_s64_to_ring(data, (int64_t)data->fd); + CHECK_RES(res); + + /* Parameter 4: size (type: PT_UINT32) */ + return bpf_push_u32_to_ring(data, (uint32_t)size); } #define POLL_MAXFDS 16 @@ -4819,6 +4828,39 @@ FILLER(sys_pwrite64_e, true) { return bpf_push_u64_to_ring(data, pos); } +FILLER(sys_pwrite64_x, true) { +#ifndef CAPTURE_64BIT_ARGS_SINGLE_REGISTER +#error Implement this +#endif + /* Parameter 1: res (type: PT_ERRNO) */ + long retval = bpf_syscall_get_retval(data->ctx); + int res = bpf_push_s64_to_ring(data, retval); + CHECK_RES(res); + + /* Parameter 2: data (type: PT_BYTEBUF) */ + /* If the syscall doesn't fail we use the return value as `size` + * otherwise we need to rely on the syscall parameter provided by the user. + */ + size_t size = bpf_syscall_get_argument(data, 2); + unsigned long bytes_to_read = retval > 0 ? retval : size; + unsigned long sent_data_pointer = bpf_syscall_get_argument(data, 1); + data->fd = (int32_t)bpf_syscall_get_argument(data, 0); + res = __bpf_val_to_ring(data, sent_data_pointer, bytes_to_read, PT_BYTEBUF, -1, true, USER); + CHECK_RES(res); + + /* Parameter 3: fd (type: PT_FD) */ + res = bpf_push_s64_to_ring(data, (int64_t)data->fd); + CHECK_RES(res); + + /* Parameter 4: size (type: PT_UINT32) */ + res = bpf_push_u32_to_ring(data, (uint32_t)size); + CHECK_RES(res); + + /* Parameter 3: pos (type: PT_UINT64) */ + uint64_t pos = (uint64_t)bpf_syscall_get_argument(data, 3); + return bpf_push_u64_to_ring(data, pos); +} + FILLER(sys_renameat_x, true) { unsigned long val; long retval; diff --git a/driver/event_table.c b/driver/event_table.c index d8160f5369e..d6621497f8e 100644 --- a/driver/event_table.c +++ b/driver/event_table.c @@ -98,14 +98,17 @@ const struct ppm_event_info g_event_info[] = { {"size", PT_UINT32, PF_DEC}}}, [PPME_SYSCALL_WRITE_E] = {"write", EC_IO_WRITE | EC_SYSCALL, - EF_USES_FD | EF_WRITES_TO_FD, + EF_USES_FD | EF_WRITES_TO_FD | EF_TMP_CONVERTER_MANAGED, 2, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC}}}, [PPME_SYSCALL_WRITE_X] = {"write", EC_IO_WRITE | EC_SYSCALL, - EF_USES_FD | EF_WRITES_TO_FD, - 2, - {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA}}}, + EF_USES_FD | EF_WRITES_TO_FD | EF_TMP_CONVERTER_MANAGED, + 4, + {{"res", PT_ERRNO, PF_DEC}, + {"data", PT_BYTEBUF, PF_NA}, + {"fd", PT_FD, PF_DEC}, + {"size", PT_UINT32, PF_DEC}}}, [PPME_SYSCALL_BRK_1_E] = {"brk", EC_MEMORY | EC_SYSCALL, EF_OLD_VERSION, 1, {{"size", PT_UINT32, PF_DEC}}}, [PPME_SYSCALL_BRK_1_X] = @@ -568,16 +571,20 @@ const struct ppm_event_info g_event_info[] = { {"pos", PT_UINT64, PF_DEC}}}, [PPME_SYSCALL_PWRITE_E] = {"pwrite", EC_IO_WRITE | EC_SYSCALL, - EF_USES_FD | EF_WRITES_TO_FD, + EF_USES_FD | EF_WRITES_TO_FD | EF_TMP_CONVERTER_MANAGED, 3, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC}, {"pos", PT_UINT64, PF_DEC}}}, [PPME_SYSCALL_PWRITE_X] = {"pwrite", EC_IO_WRITE | EC_SYSCALL, - EF_USES_FD | EF_WRITES_TO_FD, - 2, - {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA}}}, + EF_USES_FD | EF_WRITES_TO_FD | EF_TMP_CONVERTER_MANAGED, + 5, + {{"res", PT_ERRNO, PF_DEC}, + {"data", PT_BYTEBUF, PF_NA}, + {"fd", PT_FD, PF_DEC}, + {"size", PT_UINT32, PF_DEC}, + {"pos", PT_UINT64, PF_DEC}}}, [PPME_SYSCALL_READV_E] = {"readv", EC_IO_READ | EC_SYSCALL, EF_USES_FD | EF_READS_FROM_FD, diff --git a/driver/fillers_table.c b/driver/fillers_table.c index c88a4b086cd..eb89e0e1e70 100644 --- a/driver/fillers_table.c +++ b/driver/fillers_table.c @@ -117,7 +117,7 @@ const struct ppm_event_entry g_ppm_events[PPM_EVENT_MAX] = { [PPME_SYSCALL_PREAD_E] = {FILLER_REF(sys_pread64_e)}, [PPME_SYSCALL_PREAD_X] = {FILLER_REF(sys_pread64_x)}, [PPME_SYSCALL_PWRITE_E] = {FILLER_REF(sys_pwrite64_e)}, - [PPME_SYSCALL_PWRITE_X] = {FILLER_REF(sys_write_x)}, + [PPME_SYSCALL_PWRITE_X] = {FILLER_REF(sys_pwrite64_x)}, [PPME_SYSCALL_READV_E] = {FILLER_REF(sys_readv_e)}, [PPME_SYSCALL_READV_X] = {FILLER_REF(sys_readv_preadv_x)}, [PPME_SYSCALL_WRITEV_E] = {FILLER_REF(sys_writev_e)}, diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/pwrite64.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/pwrite64.bpf.c index 643610b3d34..4b18140b8a4 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/pwrite64.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/pwrite64.bpf.c @@ -62,7 +62,8 @@ int BPF_PROG(pwrite64_x, struct pt_regs *regs, long ret) { /* If the syscall doesn't fail we use the return value as `size` * otherwise we need to rely on the syscall parameter provided by the user. */ - int64_t bytes_to_read = ret > 0 ? ret : extract__syscall_argument(regs, 2); + size_t size = extract__syscall_argument(regs, 2); + int64_t bytes_to_read = ret > 0 ? ret : size; uint16_t snaplen = maps__get_snaplen(); apply_dynamic_snaplen(regs, &snaplen, false, PPME_SYSCALL_PWRITE_X); if((int64_t)snaplen > bytes_to_read) { @@ -73,6 +74,17 @@ int BPF_PROG(pwrite64_x, struct pt_regs *regs, long ret) { unsigned long data_pointer = extract__syscall_argument(regs, 1); auxmap__store_bytebuf_param(auxmap, data_pointer, snaplen, USER); + /* Parameter 3: fd (type: PT_FD) */ + int32_t fd = (int32_t)extract__syscall_argument(regs, 0); + auxmap__store_s64_param(auxmap, (int64_t)fd); + + /* Parameter 4: size (type: PT_UINT32) */ + auxmap__store_u32_param(auxmap, size); + + /* Parameter 5: pos (type: PT_UINT64) */ + uint64_t pos = (uint64_t)extract__syscall_argument(regs, 3); + auxmap__store_u64_param(auxmap, pos); + /*=============================== COLLECT PARAMETERS ===========================*/ auxmap__finalize_event_header(auxmap); diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/write.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/write.bpf.c index 98ab3c4cdfb..2b04133e479 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/write.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/write.bpf.c @@ -58,7 +58,8 @@ int BPF_PROG(write_x, struct pt_regs *regs, long ret) { /* If the syscall doesn't fail we use the return value as `size` * otherwise we need to rely on the syscall parameter provided by the user. */ - int64_t bytes_to_read = ret > 0 ? ret : extract__syscall_argument(regs, 2); + size_t size = extract__syscall_argument(regs, 2); + int64_t bytes_to_read = ret > 0 ? ret : size; uint16_t snaplen = maps__get_snaplen(); apply_dynamic_snaplen(regs, &snaplen, false, PPME_SYSCALL_WRITE_X); if((int64_t)snaplen > bytes_to_read) { @@ -69,6 +70,13 @@ int BPF_PROG(write_x, struct pt_regs *regs, long ret) { unsigned long data_pointer = extract__syscall_argument(regs, 1); auxmap__store_bytebuf_param(auxmap, data_pointer, snaplen, USER); + /* Parameter 3: fd (type: PT_FD) */ + int32_t fd = (int32_t)extract__syscall_argument(regs, 0); + auxmap__store_s64_param(auxmap, (int64_t)fd); + + /* Parameter 4: size (type: PT_UINT32) */ + auxmap__store_u32_param(auxmap, size); + /*=============================== COLLECT PARAMETERS ===========================*/ auxmap__finalize_event_header(auxmap); diff --git a/driver/ppm_fillers.c b/driver/ppm_fillers.c index 1fe3366ac91..e2a58571bcb 100644 --- a/driver/ppm_fillers.c +++ b/driver/ppm_fillers.c @@ -460,6 +460,7 @@ int f_sys_write_x(struct event_filler_arguments *args) { int res; int64_t retval; unsigned long bufsize; + size_t size = 0; /* * Retrieve the FD. It will be used for dynamic snaplen calculation. @@ -474,14 +475,21 @@ int f_sys_write_x(struct event_filler_arguments *args) { /* Parameter 2: data (type: PT_BYTEBUF) */ /* Get the size from userspace paramater */ - syscall_get_arguments_deprecated(args, 2, 1, &val); - bufsize = retval > 0 ? retval : val; - + syscall_get_arguments_deprecated(args, 2, 1, &size); + bufsize = retval > 0 ? retval : size; syscall_get_arguments_deprecated(args, 1, 1, &val); args->enforce_snaplen = true; res = val_to_ring(args, val, bufsize, true, 0); CHECK_RES(res); + /* Parameter 3: fd (type: PT_FD) */ + res = val_to_ring(args, (int64_t)args->fd, 0, false, 0); + CHECK_RES(res); + + /* Parameter 4: size (type: PT_UINT32) */ + res = val_to_ring(args, (uint32_t)size, 0, false, 0); + CHECK_RES(res); + return add_sentinel(args); } @@ -3799,6 +3807,68 @@ int f_sys_pwrite64_e(struct event_filler_arguments *args) { return add_sentinel(args); } +int f_sys_pwrite64_x(struct event_filler_arguments *args) { + unsigned long val; + int res; + int64_t retval; + unsigned long bufsize; + size_t size = 0; + unsigned long pos64 = 0; + + /* + * Retrieve the FD. It will be used for dynamic snaplen calculation. + */ + syscall_get_arguments_deprecated(args, 0, 1, &val); + args->fd = (int)val; + + /* Parameter 1: res (type: PT_ERRNO) */ + retval = (int64_t)(long)syscall_get_return_value(current, args->regs); + res = val_to_ring(args, retval, 0, false, 0); + CHECK_RES(res); + + /* Parameter 2: data (type: PT_BYTEBUF) */ + /* Get the size from userspace paramater */ + syscall_get_arguments_deprecated(args, 2, 1, &size); + bufsize = retval > 0 ? retval : size; + syscall_get_arguments_deprecated(args, 1, 1, &val); + args->enforce_snaplen = true; + res = val_to_ring(args, val, bufsize, true, 0); + CHECK_RES(res); + + /* Parameter 3: fd (type: PT_FD) */ + res = val_to_ring(args, (int64_t)args->fd, 0, false, 0); + CHECK_RES(res); + + /* Parameter 4: size (type: PT_UINT32) */ + res = val_to_ring(args, (uint32_t)size, 0, false, 0); + CHECK_RES(res); + + /* Parameter 5: pos (type: PT_UINT64) */ +#ifndef CAPTURE_64BIT_ARGS_SINGLE_REGISTER + { + unsigned long pos0 = 0; + unsigned long pos1 = 0; +#if defined CONFIG_X86 + syscall_get_arguments_deprecated(args, 3, 1, &pos0); + syscall_get_arguments_deprecated(args, 4, 1, &pos1); +#elif defined CONFIG_ARM && CONFIG_AEABI + syscall_get_arguments_deprecated(args, 4, 1, &pos0); + syscall_get_arguments_deprecated(args, 5, 1, &pos1); +#else +#error This architecture/abi not yet supported +#endif + pos64 = merge_64(pos1, pos0); + } +#else + syscall_get_arguments_deprecated(args, 3, 1, &pos64); +#endif /* CAPTURE_64BIT_ARGS_SINGLE_REGISTER*/ + + res = val_to_ring(args, pos64, 0, false, 0); + CHECK_RES(res); + + return add_sentinel(args); +} + int f_sys_preadv_e(struct event_filler_arguments *args) { unsigned long val; int res; diff --git a/driver/ppm_fillers.h b/driver/ppm_fillers.h index fba89c8c485..e3e33c30eda 100644 --- a/driver/ppm_fillers.h +++ b/driver/ppm_fillers.h @@ -192,6 +192,7 @@ or GPL2.txt for full copies of the license. FN(sys_process_vm_writev_x) \ FN(sys_delete_module_x) \ FN(sys_pread64_x) \ + FN(sys_pwrite64_x) \ FN(terminate_filler) #define FILLER_ENUM_FN(x) PPM_FILLER_##x, diff --git a/test/drivers/test_suites/actions_suite/dynamic_snaplen.cpp b/test/drivers/test_suites/actions_suite/dynamic_snaplen.cpp index 7cac3d73e3a..e8cf15993d3 100644 --- a/test/drivers/test_suites/actions_suite/dynamic_snaplen.cpp +++ b/test/drivers/test_suites/actions_suite/dynamic_snaplen.cpp @@ -79,7 +79,7 @@ TEST(Actions, dynamic_snaplen_negative_fd) { /*=============================== ASSERT PARAMETERS ===========================*/ - evt_test->assert_num_params_pushed(2); + evt_test->assert_num_params_pushed(4); } TEST(Actions, dynamic_snaplen_no_socket) { @@ -158,7 +158,7 @@ TEST(Actions, dynamic_snaplen_no_socket) { /*=============================== ASSERT PARAMETERS ===========================*/ - evt_test->assert_num_params_pushed(2); + evt_test->assert_num_params_pushed(4); } #if defined(__NR_sendto) && defined(__NR_socket) && defined(__NR_shutdown) && \ diff --git a/test/drivers/test_suites/syscall_exit_suite/pwrite64_x.cpp b/test/drivers/test_suites/syscall_exit_suite/pwrite64_x.cpp index 288f8d37e68..d72296304e3 100644 --- a/test/drivers/test_suites/syscall_exit_suite/pwrite64_x.cpp +++ b/test/drivers/test_suites/syscall_exit_suite/pwrite64_x.cpp @@ -48,9 +48,18 @@ TEST(SyscallExit, pwrite64X_no_snaplen) { /* Parameter 2: data (type: PT_BYTEBUF) */ evt_test->assert_bytebuf_param(2, buf, DEFAULT_SNAPLEN / 2); + /* Parameter 3: fd (type: PT_FD) */ + evt_test->assert_numeric_param(3, (int64_t)fd); + + /* Parameter 4: size (type: PT_UINT32) */ + evt_test->assert_numeric_param(4, (uint32_t)data_len); + + /* Parameter 5: pos (type: PT_UINT64) */ + evt_test->assert_numeric_param(5, (uint64_t)off); + /*=============================== ASSERT PARAMETERS ===========================*/ - evt_test->assert_num_params_pushed(2); + evt_test->assert_num_params_pushed(5); } TEST(SyscallExit, pwrite64X_snaplen) { @@ -90,9 +99,18 @@ TEST(SyscallExit, pwrite64X_snaplen) { /* Parameter 2: data (type: PT_BYTEBUF) */ evt_test->assert_bytebuf_param(2, buf, DEFAULT_SNAPLEN); + /* Parameter 3: fd (type: PT_FD) */ + evt_test->assert_numeric_param(3, (int64_t)-1); + + /* Parameter 4: size (type: PT_UINT32) */ + evt_test->assert_numeric_param(4, (uint32_t)data_len); + + /* Parameter 5: pos (type: PT_UINT64) */ + evt_test->assert_numeric_param(5, (uint64_t)off); + /*=============================== ASSERT PARAMETERS ===========================*/ - evt_test->assert_num_params_pushed(2); + evt_test->assert_num_params_pushed(5); } TEST(SyscallExit, pwrite64X_fail) { @@ -133,9 +151,18 @@ TEST(SyscallExit, pwrite64X_fail) { /* Parameter 2: data (type: PT_BYTEBUF) */ evt_test->assert_empty_param(2); + /* Parameter 3: fd (type: PT_FD) */ + evt_test->assert_numeric_param(3, (int64_t)-1); + + /* Parameter 4: size (type: PT_UINT32) */ + evt_test->assert_numeric_param(4, (uint32_t)data_len); + + /* Parameter 5: pos (type: PT_UINT64) */ + evt_test->assert_numeric_param(5, (uint64_t)off); + /*=============================== ASSERT PARAMETERS ===========================*/ - evt_test->assert_num_params_pushed(2); + evt_test->assert_num_params_pushed(5); } #endif diff --git a/test/drivers/test_suites/syscall_exit_suite/write_x.cpp b/test/drivers/test_suites/syscall_exit_suite/write_x.cpp index 077d666e84d..801adf9ba53 100644 --- a/test/drivers/test_suites/syscall_exit_suite/write_x.cpp +++ b/test/drivers/test_suites/syscall_exit_suite/write_x.cpp @@ -44,9 +44,15 @@ TEST(SyscallExit, writeX_no_snaplen) { /* Parameter 2: data (type: PT_BYTEBUF) */ evt_test->assert_bytebuf_param(2, buf, write_bytes); + /* Parameter 3: fd (type: PT_FD) */ + evt_test->assert_numeric_param(3, (int64_t)fd); + + /* Parameter 4: size (type: PT_UINT32)*/ + evt_test->assert_numeric_param(4, (uint32_t)data_len); + /*=============================== ASSERT PARAMETERS ===========================*/ - evt_test->assert_num_params_pushed(2); + evt_test->assert_num_params_pushed(4); } TEST(SyscallExit, writeX_snaplen) { @@ -88,9 +94,15 @@ TEST(SyscallExit, writeX_snaplen) { /* Parameter 2: data (type: PT_BYTEBUF) */ evt_test->assert_bytebuf_param(2, buf, DEFAULT_SNAPLEN); + /* Parameter 3: fd (type: PT_FD) */ + evt_test->assert_numeric_param(3, (int64_t)fd); + + /* Parameter 4: size (type: PT_UINT32)*/ + evt_test->assert_numeric_param(4, (uint32_t)data_len); + /*=============================== ASSERT PARAMETERS ===========================*/ - evt_test->assert_num_params_pushed(2); + evt_test->assert_num_params_pushed(4); } TEST(SyscallExit, writeX_fail) { @@ -128,9 +140,15 @@ TEST(SyscallExit, writeX_fail) { /* Parameter 2: data (type: PT_BYTEBUF) */ evt_test->assert_bytebuf_param(2, buf, DEFAULT_SNAPLEN); + /* Parameter 3: fd (type: PT_FD) */ + evt_test->assert_numeric_param(3, (int64_t)-1); + + /* Parameter 4: size (type: PT_UINT32)*/ + evt_test->assert_numeric_param(4, (uint32_t)data_len); + /*=============================== ASSERT PARAMETERS ===========================*/ - evt_test->assert_num_params_pushed(2); + evt_test->assert_num_params_pushed(4); } TEST(SyscallExit, writeX_empty) { @@ -167,9 +185,15 @@ TEST(SyscallExit, writeX_empty) { /* Parameter 2: data (type: PT_BYTEBUF) */ evt_test->assert_empty_param(2); + /* Parameter 3: fd (type: PT_FD) */ + evt_test->assert_numeric_param(3, (int64_t)-1); + + /* Parameter 4: size (type: PT_UINT32)*/ + evt_test->assert_numeric_param(4, (uint32_t)data_len); + /*=============================== ASSERT PARAMETERS ===========================*/ - evt_test->assert_num_params_pushed(2); + evt_test->assert_num_params_pushed(4); } TEST(SyscallExit, writeX_ipv4_tcp_message_truncated_by_snaplen) { @@ -206,7 +230,7 @@ TEST(SyscallExit, writeX_ipv4_tcp_message_truncated_by_snaplen) { /*=============================== ASSERT PARAMETERS ===========================*/ - evt_test->assert_num_params_pushed(2); + evt_test->assert_num_params_pushed(4); } TEST(SyscallExit, writeX_ipv4_tcp_message_not_truncated_fullcapture_port) { @@ -254,7 +278,7 @@ TEST(SyscallExit, writeX_ipv4_tcp_message_not_truncated_fullcapture_port) { /*=============================== ASSERT PARAMETERS ===========================*/ - evt_test->assert_num_params_pushed(2); + evt_test->assert_num_params_pushed(4); } // We cannot call a write without a destination address in UDP. Errno: 89 err_message: Destination diff --git a/test/libscap/test_suites/engines/savefile/converter.cpp b/test/libscap/test_suites/engines/savefile/converter.cpp index 8a0d822988f..029d3bc3766 100644 --- a/test/libscap/test_suites/engines/savefile/converter.cpp +++ b/test/libscap/test_suites/engines/savefile/converter.cpp @@ -71,7 +71,7 @@ TEST_F(convert_event_test, PPME_SYSCALL_READ_X_to_4_params_no_enter) { size)); } -TEST_F(convert_event_test, PPME_SYSCALL_READ_X__to_4_params_with_enter) { +TEST_F(convert_event_test, PPME_SYSCALL_READ_X_to_4_params_with_enter) { uint64_t ts = 12; int64_t tid = 25; @@ -151,7 +151,7 @@ TEST_F(convert_event_test, PPME_SYSCALL_PREAD_X_to_4_params_no_enter) { pos)); } -TEST_F(convert_event_test, PPME_SYSCALL_PREAD_X__to_4_params_with_enter) { +TEST_F(convert_event_test, PPME_SYSCALL_PREAD_X_to_4_params_with_enter) { uint64_t ts = 12; int64_t tid = 25; @@ -184,3 +184,162 @@ TEST_F(convert_event_test, PPME_SYSCALL_PREAD_X__to_4_params_with_enter) { size, pos)); } + +//////////////////////////// +// WRITE +//////////////////////////// + +TEST_F(convert_event_test, PPME_SYSCALL_WRITE_E_store) { + uint64_t ts = 12; + int64_t tid = 25; + + int64_t fd = 25; + uint32_t size = 89; + + auto evt = create_safe_scap_event(ts, tid, PPME_SYSCALL_WRITE_E, 2, fd, size); + assert_single_conversion_skip(evt); + assert_event_storage_presence(evt); +} + +TEST_F(convert_event_test, PPME_SYSCALL_WRITE_X_to_4_params_no_enter) { + uint64_t ts = 12; + int64_t tid = 25; + + int64_t res = 89; + uint8_t buf[] = {'h', 'e', 'l', 'l', 'o'}; + + // Defaulted to 0 + int64_t fd = 0; + uint32_t size = 0; + + assert_single_conversion_success( + conversion_result::CONVERSION_COMPLETED, + create_safe_scap_event(ts, + tid, + PPME_SYSCALL_WRITE_X, + 2, + res, + scap_const_sized_buffer{buf, sizeof(buf)}), + create_safe_scap_event(ts, + tid, + PPME_SYSCALL_WRITE_X, + 4, + res, + scap_const_sized_buffer{buf, sizeof(buf)}, + fd, + size)); +} + +TEST_F(convert_event_test, PPME_SYSCALL_WRITE_X_to_4_params_with_enter) { + uint64_t ts = 12; + int64_t tid = 25; + + int64_t res = 89; + uint8_t buf[] = {'h', 'e', 'l', 'l', 'o'}; + int64_t fd = 25; + uint32_t size = 36; + + // After the first conversion we should have the storage + auto evt = create_safe_scap_event(ts, tid, PPME_SYSCALL_WRITE_E, 2, fd, size); + assert_single_conversion_skip(evt); + assert_event_storage_presence(evt); + + assert_single_conversion_success( + conversion_result::CONVERSION_COMPLETED, + create_safe_scap_event(ts, + tid, + PPME_SYSCALL_WRITE_X, + 2, + res, + scap_const_sized_buffer{buf, sizeof(buf)}), + create_safe_scap_event(ts, + tid, + PPME_SYSCALL_WRITE_X, + 4, + res, + scap_const_sized_buffer{buf, sizeof(buf)}, + fd, + size)); +} + +//////////////////////////// +// PWRITE +//////////////////////////// + +TEST_F(convert_event_test, PPME_SYSCALL_PWRITE_E_store) { + uint64_t ts = 12; + int64_t tid = 25; + + int64_t fd = 25; + uint32_t size = 89; + uint64_t pos = 7; + + auto evt = create_safe_scap_event(ts, tid, PPME_SYSCALL_PWRITE_E, 3, fd, size, pos); + assert_single_conversion_skip(evt); + assert_event_storage_presence(evt); +} + +TEST_F(convert_event_test, PPME_SYSCALL_PWRITE_X_to_4_params_no_enter) { + uint64_t ts = 12; + int64_t tid = 25; + + int64_t res = 89; + uint8_t buf[] = {'h', 'e', 'l', 'l', 'o'}; + + // Defaulted to 0 + int64_t fd = 0; + uint32_t size = 0; + int64_t pos = 0; + + assert_single_conversion_success( + conversion_result::CONVERSION_COMPLETED, + create_safe_scap_event(ts, + tid, + PPME_SYSCALL_PWRITE_X, + 2, + res, + scap_const_sized_buffer{buf, sizeof(buf)}), + create_safe_scap_event(ts, + tid, + PPME_SYSCALL_PWRITE_X, + 5, + res, + scap_const_sized_buffer{buf, sizeof(buf)}, + fd, + size, + pos)); +} + +TEST_F(convert_event_test, PPME_SYSCALL_PWRITE_X_to_4_params_with_enter) { + uint64_t ts = 12; + int64_t tid = 25; + + int64_t res = 89; + uint8_t buf[] = {'h', 'e', 'l', 'l', 'o'}; + int64_t fd = 25; + uint32_t size = 36; + uint64_t pos = 7; + + // After the first conversion we should have the storage + auto evt = create_safe_scap_event(ts, tid, PPME_SYSCALL_PWRITE_E, 3, fd, size, pos); + assert_single_conversion_skip(evt); + assert_event_storage_presence(evt); + + assert_single_conversion_success( + conversion_result::CONVERSION_COMPLETED, + create_safe_scap_event(ts, + tid, + PPME_SYSCALL_PWRITE_X, + 2, + res, + scap_const_sized_buffer{buf, sizeof(buf)}), + create_safe_scap_event(ts, + tid, + PPME_SYSCALL_PWRITE_X, + 5, + res, + scap_const_sized_buffer{buf, sizeof(buf)}, + fd, + size, + pos)); +} diff --git a/userspace/libscap/engine/gvisor/fillers.cpp b/userspace/libscap/engine/gvisor/fillers.cpp index 48de12dfae2..9ce870cd7d0 100644 --- a/userspace/libscap/engine/gvisor/fillers.cpp +++ b/userspace/libscap/engine/gvisor/fillers.cpp @@ -1402,14 +1402,18 @@ int32_t fill_event_write_e(scap_sized_buffer scap_buf, int32_t fill_event_write_x(scap_sized_buffer scap_buf, size_t* event_size, char* scap_err, - int64_t res) { + int64_t res, + int64_t fd, + uint32_t size) { return scap_event_encode_params(scap_buf, event_size, scap_err, PPME_SYSCALL_WRITE_X, - 2, + 4, res, - scap_const_sized_buffer{NULL, 0}); // data -- INVALID + scap_const_sized_buffer{NULL, 0}, + fd, + size); // data -- INVALID } // PPME_SYSCALL_PWRITE_E @@ -1437,14 +1441,20 @@ int32_t fill_event_pwrite_e(scap_sized_buffer scap_buf, int32_t fill_event_pwrite_x(scap_sized_buffer scap_buf, size_t* event_size, char* scap_err, - int64_t res) { + int64_t res, + int64_t fd, + uint32_t size, + uint64_t pos) { return scap_event_encode_params(scap_buf, event_size, scap_err, PPME_SYSCALL_PWRITE_X, - 2, + 5, res, - scap_const_sized_buffer{NULL, 0}); // data -- INVALID + scap_const_sized_buffer{NULL, 0}, + fd, + size, + pos); // data -- INVALID } // PPME_SYSCALL_WRITEV_E diff --git a/userspace/libscap/engine/gvisor/fillers.h b/userspace/libscap/engine/gvisor/fillers.h index d7b7b8bd9ad..7dd96e759db 100644 --- a/userspace/libscap/engine/gvisor/fillers.h +++ b/userspace/libscap/engine/gvisor/fillers.h @@ -506,7 +506,9 @@ int32_t fill_event_write_e(scap_sized_buffer scap_buf, int32_t fill_event_write_x(scap_sized_buffer scap_buf, size_t* event_size, char* scap_err, - int64_t res); + int64_t res, + int64_t fd, + uint32_t size); int32_t fill_event_pwrite_e(scap_sized_buffer scap_buf, size_t* event_size, @@ -518,7 +520,10 @@ int32_t fill_event_pwrite_e(scap_sized_buffer scap_buf, int32_t fill_event_pwrite_x(scap_sized_buffer scap_buf, size_t* event_size, char* scap_err, - int64_t res); + int64_t res, + int64_t fd, + uint32_t size, + uint64_t pos); int32_t fill_event_writev_e(scap_sized_buffer scap_buf, size_t* event_size, diff --git a/userspace/libscap/engine/gvisor/parsers.cpp b/userspace/libscap/engine/gvisor/parsers.cpp index 73cdaedeca5..5e0f96773e0 100644 --- a/userspace/libscap/engine/gvisor/parsers.cpp +++ b/userspace/libscap/engine/gvisor/parsers.cpp @@ -1943,14 +1943,19 @@ static parse_result parse_write(uint32_t id, ret.status = scap_gvisor::fillers::fill_event_write_x(scap_buf, &ret.size, scap_err, - gvisor_evt.exit().result()); + gvisor_evt.exit().result(), + gvisor_evt.fd(), + gvisor_evt.count()); break; case __NR_pwrite64: ret.status = scap_gvisor::fillers::fill_event_pwrite_x(scap_buf, &ret.size, scap_err, - gvisor_evt.exit().result()); + gvisor_evt.exit().result(), + gvisor_evt.fd(), + gvisor_evt.count(), + gvisor_evt.offset()); break; case __NR_writev: diff --git a/userspace/libscap/engine/savefile/converter/table.cpp b/userspace/libscap/engine/savefile/converter/table.cpp index e00f683e7dc..27dca96d17e 100644 --- a/userspace/libscap/engine/savefile/converter/table.cpp +++ b/userspace/libscap/engine/savefile/converter/table.cpp @@ -36,4 +36,18 @@ const std::unordered_map g_conversion_table = { .instrs({{C_INSTR_FROM_ENTER, 0}, {C_INSTR_FROM_ENTER, 1}, {C_INSTR_FROM_ENTER, 2}})}, + /*====================== WRITE ======================*/ + {conversion_key{PPME_SYSCALL_WRITE_E, 2}, conversion_info().action(C_ACTION_STORE)}, + {conversion_key{PPME_SYSCALL_WRITE_X, 2}, + conversion_info() + .action(C_ACTION_ADD_PARAMS) + .instrs({{C_INSTR_FROM_ENTER, 0}, {C_INSTR_FROM_ENTER, 1}})}, + /*====================== PWRITE ======================*/ + {conversion_key{PPME_SYSCALL_PWRITE_E, 3}, conversion_info().action(C_ACTION_STORE)}, + {conversion_key{PPME_SYSCALL_PWRITE_X, 2}, + conversion_info() + .action(C_ACTION_ADD_PARAMS) + .instrs({{C_INSTR_FROM_ENTER, 0}, + {C_INSTR_FROM_ENTER, 1}, + {C_INSTR_FROM_ENTER, 2}})}, }; diff --git a/userspace/libscap/scap.h b/userspace/libscap/scap.h index 6685f9f32bd..3b253385a0c 100644 --- a/userspace/libscap/scap.h +++ b/userspace/libscap/scap.h @@ -102,7 +102,7 @@ struct scap_vtable; // and handle the result // #define SCAP_MINIMUM_DRIVER_API_VERSION PPM_API_VERSION(8, 0, 0) -#define SCAP_MINIMUM_DRIVER_SCHEMA_VERSION PPM_API_VERSION(3, 0, 0) +#define SCAP_MINIMUM_DRIVER_SCHEMA_VERSION PPM_API_VERSION(4, 0, 0) // // This is the dimension we used before introducing the variable buffer size. diff --git a/userspace/libscap/scap_event.c b/userspace/libscap/scap_event.c index c13a55efb55..60d2e2f645a 100644 --- a/userspace/libscap/scap_event.c +++ b/userspace/libscap/scap_event.c @@ -531,6 +531,8 @@ int get_exit_event_fd_location(ppm_event_code etype) { switch(etype) { case PPME_SYSCALL_READ_X: case PPME_SYSCALL_PREAD_X: + case PPME_SYSCALL_WRITE_X: + case PPME_SYSCALL_PWRITE_X: location = 2; break; default: diff --git a/userspace/libsinsp/test/events_param.ut.cpp b/userspace/libsinsp/test/events_param.ut.cpp index a9e466d9f97..5e9b0f5485e 100644 --- a/userspace/libsinsp/test/events_param.ut.cpp +++ b/userspace/libsinsp/test/events_param.ut.cpp @@ -108,26 +108,30 @@ TEST_F(sinsp_with_test_input, bytebuf_empty_param) { add_default_init_thread(); open_inspector(); - sinsp_evt* evt = NULL; - const sinsp_evt_param* param = NULL; - - int64_t test_errno = 0; - /* `PPME_SYSCALL_PWRITE_X` is a simple event that uses a `PT_BYTEBUF` */ scap_const_sized_buffer bytebuf_param; bytebuf_param.buf = NULL; bytebuf_param.size = 0; - evt = add_event_advance_ts(increasing_ts(), - 1, - PPME_SYSCALL_PWRITE_X, - 2, - test_errno, - bytebuf_param); + int64_t test_errno = 0; + uint32_t size = 0; + int64_t fd = 0; + uint64_t pos = 0; + + /* `PPME_SYSCALL_PWRITE_X` is a simple event that uses a `PT_BYTEBUF` */ + auto evt = add_event_advance_ts(increasing_ts(), + INIT_TID, + PPME_SYSCALL_PWRITE_X, + 5, + test_errno, + bytebuf_param, + fd, + size, + pos); + ASSERT_EQ(get_field_as_string(evt, "evt.arg.data"), "NULL"); // "NULL" is the string representation output of the empty buffer - - param = evt->get_param(1); - ASSERT_EQ(param->m_len, 0); + ASSERT_TRUE(evt->get_param(1)); + ASSERT_EQ(evt->get_param(1)->m_len, 0); } /* Assert that empty (`PT_SOCKADDR`, `PT_SOCKTUPLE`, `PT_FDLIST`) params are NOT converted to `` diff --git a/userspace/libsinsp/test/parsers/parse_pwrite.cpp b/userspace/libsinsp/test/parsers/parse_pwrite.cpp new file mode 100644 index 00000000000..a8f91b08952 --- /dev/null +++ b/userspace/libsinsp/test/parsers/parse_pwrite.cpp @@ -0,0 +1,88 @@ + +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2024 The Falco Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include + +TEST_F(sinsp_with_test_input, parse_pwrite_success) { + add_default_init_thread(); + open_inspector(); + + auto evt = generate_open_x_event(); + ASSERT_TRUE(evt->get_fd_info()); + + std::string data = "hello"; + uint32_t size = data.size(); + uint64_t pos = 0; + evt = add_event_advance_ts(increasing_ts(), + INIT_TID, + PPME_SYSCALL_PWRITE_X, + 5, + (int64_t)size, + scap_const_sized_buffer{data.c_str(), size}, + sinsp_test_input::open_params::default_fd, + size, + pos); + + ASSERT_TRUE(evt->get_fd_info()); + assert_fd_fields(evt, + sinsp_test_input::fd_info_fields{ + .fd_num = sinsp_test_input::open_params::default_fd, + .fd_name = sinsp_test_input::open_params::default_path, + .fd_name_raw = sinsp_test_input::open_params::default_path, + .fd_directory = sinsp_test_input::open_params::default_directory, + .fd_filename = sinsp_test_input::open_params::default_filename}); + + assert_return_value(evt, size); + + ASSERT_EQ(get_field_as_string(evt, "evt.arg[1]"), data); + ASSERT_EQ(get_field_as_string(evt, "evt.rawarg.data"), data); + + ASSERT_EQ(get_field_as_string(evt, "evt.arg[2]"), + std::string("") + sinsp_test_input::open_params::default_path); + ASSERT_EQ(get_field_as_string(evt, "evt.rawarg.fd"), + std::to_string(sinsp_test_input::open_params::default_fd)); + + ASSERT_EQ(get_field_as_string(evt, "evt.arg[3]"), std::to_string(size)); + ASSERT_EQ(get_field_as_string(evt, "evt.rawarg.size"), std::to_string(size)); +} + +TEST_F(sinsp_with_test_input, parse_pwrite_failure) { + add_default_init_thread(); + open_inspector(); + + auto evt = generate_open_x_event(); + + std::string data = "hello"; + uint32_t size = data.size(); + int64_t errno_code = -3; + uint64_t pos = 0; + evt = add_event_advance_ts(increasing_ts(), + INIT_TID, + PPME_SYSCALL_PWRITE_X, + 5, + errno_code, + scap_const_sized_buffer{data.c_str(), size}, + sinsp_test_input::open_params::default_fd, + size, + pos); + + // Check we have the correct fd info associated with the event + auto fdinfo = evt->get_fd_info(); + ASSERT_TRUE(fdinfo); + ASSERT_EQ(fdinfo->m_fd, sinsp_test_input::open_params::default_fd); + + // Assert return value filterchecks + assert_return_value(evt, errno_code); +} diff --git a/userspace/libsinsp/test/parsers/parse_write.cpp b/userspace/libsinsp/test/parsers/parse_write.cpp new file mode 100644 index 00000000000..779d375d1a8 --- /dev/null +++ b/userspace/libsinsp/test/parsers/parse_write.cpp @@ -0,0 +1,84 @@ + +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2024 The Falco Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include + +TEST_F(sinsp_with_test_input, parse_write_success) { + add_default_init_thread(); + open_inspector(); + + auto evt = generate_open_x_event(); + ASSERT_TRUE(evt->get_fd_info()); + + std::string data = "hello"; + uint32_t size = data.size(); + evt = add_event_advance_ts(increasing_ts(), + INIT_TID, + PPME_SYSCALL_WRITE_X, + 4, + (int64_t)size, + scap_const_sized_buffer{data.c_str(), size}, + sinsp_test_input::open_params::default_fd, + size); + + ASSERT_TRUE(evt->get_fd_info()); + assert_fd_fields(evt, + sinsp_test_input::fd_info_fields{ + .fd_num = sinsp_test_input::open_params::default_fd, + .fd_name = sinsp_test_input::open_params::default_path, + .fd_name_raw = sinsp_test_input::open_params::default_path, + .fd_directory = sinsp_test_input::open_params::default_directory, + .fd_filename = sinsp_test_input::open_params::default_filename}); + + assert_return_value(evt, size); + + ASSERT_EQ(get_field_as_string(evt, "evt.arg[1]"), data); + ASSERT_EQ(get_field_as_string(evt, "evt.rawarg.data"), data); + + ASSERT_EQ(get_field_as_string(evt, "evt.arg[2]"), + std::string("") + sinsp_test_input::open_params::default_path); + ASSERT_EQ(get_field_as_string(evt, "evt.rawarg.fd"), + std::to_string(sinsp_test_input::open_params::default_fd)); + + ASSERT_EQ(get_field_as_string(evt, "evt.arg[3]"), std::to_string(size)); + ASSERT_EQ(get_field_as_string(evt, "evt.rawarg.size"), std::to_string(size)); +} + +TEST_F(sinsp_with_test_input, parse_write_failure) { + add_default_init_thread(); + open_inspector(); + + auto evt = generate_open_x_event(); + + std::string data = "hello"; + uint32_t size = data.size(); + int64_t errno_code = -3; + evt = add_event_advance_ts(increasing_ts(), + INIT_TID, + PPME_SYSCALL_WRITE_X, + 4, + errno_code, + scap_const_sized_buffer{data.c_str(), size}, + sinsp_test_input::open_params::default_fd, + size); + + // Check we have the correct fd info associated with the event + auto fdinfo = evt->get_fd_info(); + ASSERT_TRUE(fdinfo); + ASSERT_EQ(fdinfo->m_fd, sinsp_test_input::open_params::default_fd); + + // Assert return value filterchecks + assert_return_value(evt, errno_code); +} diff --git a/userspace/libsinsp/test/scap_files/converter_tests.cpp b/userspace/libsinsp/test/scap_files/converter_tests.cpp index 1e7817af0bf..dd6dd20f02d 100644 --- a/userspace/libsinsp/test/scap_files/converter_tests.cpp +++ b/userspace/libsinsp/test/scap_files/converter_tests.cpp @@ -128,3 +128,49 @@ TEST_F(scap_file_test, pread_x_check_final_converted_event) { size, pos)); } + +//////////////////////////// +// WRITE +//////////////////////////// + +TEST_F(scap_file_test, write_x_check_final_converted_event) { + open_filename("scap_2013.scap"); + + // Inside the scap-file the event `511534` is the following: + // - type=PPME_SYSCALL_WRITE_X + // - ts=1380933088286397273 + // - tid=44106 + // - args=res=77 data=GET / HTTP/1.0..Host: 127.0.0.1..User-Agent: ApacheBench/2.3..Accept: + // */*... + // + // And its corresponding enter event `511520` is the following: + // - type=PPME_SYSCALL_WRITE_E + // - ts=1380933088286362703 + // - tid=44106, + // - args=fd=13(<4t>127.0.0.1:38904->127.0.0.1:80) size=77 + // + uint64_t ts = 1380933088286397273; + int64_t tid = 44106; + int64_t res = 77; + // this is NULL termiinated so we have 81 bytes but in the scap-file we want only 80 bytes + // without the NULL terminator + char buf[] = { + "GET / HTTP/1.0\r\nHost: 127.0.0.1\r\nUser-Agent: ApacheBench/2.3\r\nAccept: " + "*/*\r\n\r\n"}; + int64_t fd = 13; + uint32_t size = 77; + assert_event_presence(create_safe_scap_event(ts, + tid, + PPME_SYSCALL_WRITE_X, + 4, + res, + scap_const_sized_buffer{buf, sizeof(buf) - 1}, + fd, + size)); +} + +//////////////////////////// +// PWRITE +//////////////////////////// + +// We don't have scap-files with PWRITE events. Add it if we face a failure.