From ad11aec9aa96a752d97aeb71947853bb77bbf29e 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 | 18 +- 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, 667 insertions(+), 57 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 1545d96657..40c341bdcd 100644 --- a/driver/SCHEMA_VERSION +++ b/driver/SCHEMA_VERSION @@ -1 +1 @@ -3.5.0 +3.6.0 diff --git a/driver/bpf/fillers.h b/driver/bpf/fillers.h index 6a0efbdd0f..74365f647b 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 @@ -5078,6 +5087,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 7a3c8d693b..5ccd42c6c3 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] = @@ -597,16 +600,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 2f882bff91..8146671724 100644 --- a/driver/fillers_table.c +++ b/driver/fillers_table.c @@ -115,7 +115,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 a58ce89e99..196475ba52 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 @@ -66,7 +66,8 @@ int BPF_PROG(pwrite64_x, struct pt_regs *regs, long ret) { .only_port_range = false, .evt_type = PPME_SYSCALL_PWRITE_X, }; - 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, &snaplen_args); if((int64_t)snaplen > bytes_to_read) { @@ -77,6 +78,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 70e52ce87f..0dfbe558e0 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 @@ -62,7 +62,8 @@ int BPF_PROG(write_x, struct pt_regs *regs, long ret) { .only_port_range = false, .evt_type = PPME_SYSCALL_WRITE_X, }; - 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, &snaplen_args); if((int64_t)snaplen > bytes_to_read) { @@ -73,6 +74,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 35f6950a67..39375e1c36 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); } @@ -4183,6 +4191,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 a377955015..95a7d14441 100644 --- a/driver/ppm_fillers.h +++ b/driver/ppm_fillers.h @@ -196,6 +196,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 7cac3d73e3..e8cf15993d 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 288f8d37e6..d72296304e 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 077d666e84..801adf9ba5 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 041728f6f2..22b99d5841 100644 --- a/test/libscap/test_suites/engines/savefile/converter.cpp +++ b/test/libscap/test_suites/engines/savefile/converter.cpp @@ -73,7 +73,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; @@ -153,7 +153,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; @@ -413,3 +413,162 @@ TEST_F(convert_event_test, PPME_SOCKET_ACCEPT_5_E_skip) { auto evt = create_safe_scap_event(ts, tid, PPME_SOCKET_ACCEPT_5_E, 0); assert_single_conversion_skip(evt); } + +//////////////////////////// +// 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 9bd14bd573..bcfb24c453 100644 --- a/userspace/libscap/engine/gvisor/fillers.cpp +++ b/userspace/libscap/engine/gvisor/fillers.cpp @@ -1415,14 +1415,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 @@ -1450,14 +1454,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 29817468f0..0eae47ac06 100644 --- a/userspace/libscap/engine/gvisor/fillers.h +++ b/userspace/libscap/engine/gvisor/fillers.h @@ -510,7 +510,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, @@ -522,7 +524,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 a590bfc212..cfda671880 100644 --- a/userspace/libscap/engine/gvisor/parsers.cpp +++ b/userspace/libscap/engine/gvisor/parsers.cpp @@ -1948,14 +1948,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 f1aad43f4f..a1d25ec768 100644 --- a/userspace/libscap/engine/savefile/converter/table.cpp +++ b/userspace/libscap/engine/savefile/converter/table.cpp @@ -54,6 +54,7 @@ const std::unordered_map g_conversion_table = { conversion_info() .action(C_ACTION_ADD_PARAMS) .instrs({{C_INSTR_FROM_ENTER, 0}, {C_INSTR_FROM_ENTER, 1}})}, + /*====================== ACCEPT ======================*/ {conversion_key{PPME_SOCKET_ACCEPT_E, 0}, conversion_info().action(C_ACTION_SKIP)}, {conversion_key{PPME_SOCKET_ACCEPT_X, 3}, conversion_info() @@ -66,4 +67,19 @@ const std::unordered_map g_conversion_table = { {C_INSTR_FROM_DEFAULT, 0}, {C_INSTR_FROM_DEFAULT, 0}, })}, - {conversion_key{PPME_SOCKET_ACCEPT_5_E, 0}, conversion_info().action(C_ACTION_SKIP)}}; + {conversion_key{PPME_SOCKET_ACCEPT_5_E, 0}, conversion_info().action(C_ACTION_SKIP)}, + /*====================== 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 4a06bfa3eb..9550bc2e46 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, 5, 0) +#define SCAP_MINIMUM_DRIVER_SCHEMA_VERSION PPM_API_VERSION(3, 6, 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 d143212bae..9e388f374f 100644 --- a/userspace/libscap/scap_event.c +++ b/userspace/libscap/scap_event.c @@ -535,6 +535,8 @@ int get_exit_event_fd_location(ppm_event_code etype) { case PPME_SYSCALL_READ_X: case PPME_SYSCALL_PREAD_X: case PPME_SOCKET_BIND_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 fbf8923fcf..64e3d79fba 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 0000000000..a8f91b0895 --- /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 0000000000..779d375d1a --- /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 5dde6b176b..cd491490e1 100644 --- a/userspace/libsinsp/test/scap_files/converter_tests.cpp +++ b/userspace/libsinsp/test/scap_files/converter_tests.cpp @@ -199,3 +199,49 @@ TEST_F(scap_file_test, listen_x_check_final_converted_event) { assert_event_presence( create_safe_scap_event(ts, tid, PPME_SOCKET_LISTEN_X, 3, res, fd, backlog)); } + +//////////////////////////// +// 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.