diff --git a/driver/SCHEMA_VERSION b/driver/SCHEMA_VERSION index 6ceb272eec..3cf561c0b6 100644 --- a/driver/SCHEMA_VERSION +++ b/driver/SCHEMA_VERSION @@ -1 +1 @@ -2.11.1 +2.12.1 diff --git a/driver/bpf/fillers.h b/driver/bpf/fillers.h index 2a986a988b..8417c5de48 100644 --- a/driver/bpf/fillers.h +++ b/driver/bpf/fillers.h @@ -4187,6 +4187,10 @@ FILLER(sys_recvmsg_x, true) CHECK_RES(res); /* Parameter 4: tuple (type: PT_SOCKTUPLE) */ + res = bpf_push_empty_param(data); + CHECK_RES(res); + + /* Parameter 5: msg_control (type: PT_BYTEBUF) */ return bpf_push_empty_param(data); } @@ -4225,49 +4229,57 @@ FILLER(sys_recvmsg_x_2, true) retval = bpf_syscall_get_retval(data->ctx); /* - * tuple + * tuple and msg_control */ - if (retval >= 0) { - /* - * Retrieve the message header - */ - val = bpf_syscall_get_argument(data, 1); - if (bpf_probe_read_user(&mh, sizeof(mh), (void *)val)) - return PPM_FAILURE_INVALID_USER_MEMORY; - /* - * Get the address - */ - usrsockaddr = (struct sockaddr *)mh.msg_name; - addrlen = mh.msg_namelen; + /* + * Retrieve the message header + */ + val = bpf_syscall_get_argument(data, 1); + if (bpf_probe_read_user(&mh, sizeof(mh), (void *)val)) + return PPM_FAILURE_INVALID_USER_MEMORY; - if (usrsockaddr && addrlen != 0) { - /* - * Copy the address - */ - res = bpf_addr_to_kernel(usrsockaddr, - addrlen, - (struct sockaddr *)data->tmp_scratch); + /* + * Get the address + */ + usrsockaddr = (struct sockaddr *)mh.msg_name; + addrlen = mh.msg_namelen; - if (res >= 0) { - fd = bpf_syscall_get_argument(data, 0); + if (usrsockaddr && addrlen != 0) { + /* + * Copy the address + */ + res = bpf_addr_to_kernel(usrsockaddr, + addrlen, + (struct sockaddr *)data->tmp_scratch); - /* - * Convert the fd into socket endpoint information - */ - size = bpf_fd_to_socktuple(data, - fd, - (struct sockaddr *)data->tmp_scratch, - addrlen, - true, - true, - data->tmp_scratch + sizeof(struct sockaddr_storage)); - } + if (res >= 0) { + fd = bpf_syscall_get_argument(data, 0); + + /* + * Convert the fd into socket endpoint information + */ + size = bpf_fd_to_socktuple(data, + fd, + (struct sockaddr *)data->tmp_scratch, + addrlen, + true, + true, + data->tmp_scratch + sizeof(struct sockaddr_storage)); } } data->curarg_already_on_frame = true; res = __bpf_val_to_ring(data, 0, size, PT_SOCKTUPLE, -1, false, KERNEL); + CHECK_RES(res); + + if(mh.msg_control != NULL) + { + res = __bpf_val_to_ring(data, (unsigned long)mh.msg_control, mh.msg_controllen, PT_BYTEBUF, -1, false, USER); + } else + { + res = bpf_push_empty_param(data); + } return res; } diff --git a/driver/event_table.c b/driver/event_table.c index 1a7fde1fed..f2b9c0afaa 100644 --- a/driver/event_table.c +++ b/driver/event_table.c @@ -101,7 +101,7 @@ const struct ppm_event_info g_event_info[] = { [PPME_SOCKET_SENDMMSG_E] = {"sendmmsg", EC_IO_WRITE | EC_SYSCALL, EF_NONE, 0}, [PPME_SOCKET_SENDMMSG_X] = {"sendmmsg", EC_IO_WRITE | EC_SYSCALL, EF_NONE, 0}, [PPME_SOCKET_RECVMSG_E] = {"recvmsg", EC_IO_READ | EC_SYSCALL, EF_USES_FD | EF_READS_FROM_FD | EF_MODIFIES_STATE, 1, {{"fd", PT_FD, PF_DEC} } }, - [PPME_SOCKET_RECVMSG_X] = {"recvmsg", EC_IO_READ | EC_SYSCALL, EF_USES_FD | EF_READS_FROM_FD | EF_MODIFIES_STATE, 4, {{"res", PT_ERRNO, PF_DEC}, {"size", PT_UINT32, PF_DEC}, {"data", PT_BYTEBUF, PF_NA}, {"tuple", PT_SOCKTUPLE, PF_NA} } }, + [PPME_SOCKET_RECVMSG_X] = {"recvmsg", EC_IO_READ | EC_SYSCALL, EF_USES_FD | EF_READS_FROM_FD | EF_MODIFIES_STATE, 5, {{"res", PT_ERRNO, PF_DEC}, {"size", PT_UINT32, PF_DEC}, {"data", PT_BYTEBUF, PF_NA}, {"tuple", PT_SOCKTUPLE, PF_NA}, {"msgcontrol", PT_BYTEBUF, PF_NA} } }, [PPME_SOCKET_RECVMMSG_E] = {"recvmmsg", EC_IO_READ | EC_SYSCALL, EF_NONE, 0}, [PPME_SOCKET_RECVMMSG_X] = {"recvmmsg", EC_IO_READ | EC_SYSCALL, EF_NONE, 0}, [PPME_SOCKET_ACCEPT4_E] = {"accept", EC_NET | EC_SYSCALL, EF_CREATES_FD | EF_MODIFIES_STATE | EF_OLD_VERSION, 1, {{"flags", PT_INT32, PF_HEX} } }, diff --git a/driver/modern_bpf/helpers/store/auxmap_store_params.h b/driver/modern_bpf/helpers/store/auxmap_store_params.h index ef0680a320..c4b776b859 100644 --- a/driver/modern_bpf/helpers/store/auxmap_store_params.h +++ b/driver/modern_bpf/helpers/store/auxmap_store_params.h @@ -945,8 +945,10 @@ static __always_inline void auxmap__store_msghdr_size_param(struct auxiliary_map * @param auxmap pointer to the auxmap in which we are storing the param. * @param msghdr_pointer pointer to `user_msghdr` struct. * @param len_to_read imposed snaplen. + * + * @return the `user_msghdr` struct that has been read. */ -static __always_inline void auxmap__store_msghdr_data_param(struct auxiliary_map *auxmap, unsigned long msghdr_pointer, unsigned long len_to_read) +static __always_inline struct user_msghdr auxmap__store_msghdr_data_param(struct auxiliary_map *auxmap, unsigned long msghdr_pointer, unsigned long len_to_read) { /* Read the usr_msghdr struct into the stack, if we fail, * we return an empty param. @@ -956,10 +958,11 @@ static __always_inline void auxmap__store_msghdr_data_param(struct auxiliary_map { /* in case of NULL msghdr we return an empty param */ push__param_len(auxmap->data, &auxmap->lengths_pos, 0); - return; + return msghdr; } auxmap__store_iovec_data_param(auxmap, (unsigned long)msghdr.msg_iov, msghdr.msg_iovlen, len_to_read); + return msghdr; } /** diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/recvmsg.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/recvmsg.bpf.c index 8ae6fb731e..11421d6574 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/recvmsg.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/recvmsg.bpf.c @@ -89,11 +89,19 @@ int BPF_PROG(recvmsg_x, /* Parameter 3: data (type: PT_BYTEBUF) */ unsigned long msghdr_pointer = args[1]; - auxmap__store_msghdr_data_param(auxmap, msghdr_pointer, snaplen); + struct user_msghdr msghhdr = auxmap__store_msghdr_data_param(auxmap, msghdr_pointer, snaplen); /* Parameter 4: tuple (type: PT_SOCKTUPLE) */ u32 socket_fd = (u32)args[0]; auxmap__store_socktuple_param(auxmap, socket_fd, INBOUND); + + /* Parameter 5: msg_control (type: PT_BYTEBUF) */ + if (msghhdr.msg_control != NULL) + { + auxmap__store_bytebuf_param(auxmap, (unsigned long)msghhdr.msg_control, msghhdr.msg_controllen, USER); + } else { + auxmap__store_empty_param(auxmap); + } } else { @@ -105,6 +113,9 @@ int BPF_PROG(recvmsg_x, /* Parameter 4: tuple (type: PT_SOCKTUPLE) */ auxmap__store_empty_param(auxmap); + + /* Parameter 5: msg_control (type: PT_BYTEBUF) */ + auxmap__store_empty_param(auxmap); } /*=============================== COLLECT PARAMETERS ===========================*/ diff --git a/driver/ppm_fillers.c b/driver/ppm_fillers.c index ba08ee7885..3bbb548bd3 100644 --- a/driver/ppm_fillers.c +++ b/driver/ppm_fillers.c @@ -2878,6 +2878,10 @@ int f_sys_recvmsg_x(struct event_filler_arguments *args) res = push_empty_param(args); CHECK_RES(res); + /* Parameter 5: msg_control (type: PT_BYTEBUF) */ + res = push_empty_param(args); + CHECK_RES(res); + return add_sentinel(args); } @@ -2959,6 +2963,22 @@ int f_sys_recvmsg_x(struct event_filler_arguments *args) false, 0); CHECK_RES(res); + + /* + msg_control: ancillary data. + */ + if (mh.msg_control != NULL && mh.msg_controllen > 0) + { + res = val_to_ring(args, (uint64_t)mh.msg_control, (uint32_t)mh.msg_controllen, true, 0); + CHECK_RES(res); + } + else + { + /* pushing empty data */ + res = push_empty_param(args); + CHECK_RES(res); + } + return add_sentinel(args); } diff --git a/test/drivers/test_suites/syscall_exit_suite/recvmsg_x.cpp b/test/drivers/test_suites/syscall_exit_suite/recvmsg_x.cpp index 4137eb372f..cf0f4a4616 100644 --- a/test/drivers/test_suites/syscall_exit_suite/recvmsg_x.cpp +++ b/test/drivers/test_suites/syscall_exit_suite/recvmsg_x.cpp @@ -2,7 +2,7 @@ #ifdef __NR_recvmsg -#if defined(__NR_accept4) && defined(__NR_connect) && defined(__NR_socket) && defined(__NR_bind) && defined(__NR_listen) && defined(__NR_close) && defined(__NR_setsockopt) && defined(__NR_shutdown) && defined(__NR_sendto) +#if defined(__NR_accept4) && defined(__NR_connect) && defined(__NR_socket) && defined(__NR_bind) && defined(__NR_listen) && defined(__NR_close) && defined(__NR_setsockopt) && defined(__NR_shutdown) && defined(__NR_sendto) && defined(__NR_sendmsg) TEST(SyscallExit, recvmsgX_tcp_connection_no_snaplen) { @@ -93,13 +93,16 @@ TEST(SyscallExit, recvmsgX_tcp_connection_no_snaplen) /// TODO: If the socket is connected, the msg_name and msg_namelen members shall be ignored, but /// right now we use them to send data also in TCP connections so we need to change this behavior! evt_test->assert_empty_param(4); - evt_test->assert_num_params_pushed(4); + evt_test->assert_num_params_pushed(5); GTEST_SKIP() << "[RECVMSG_X]: we receive an empty tuple but we have all the data in the kernel to obtain the correct tuple" << std::endl; } + /* Parameter 5: msg_control (type: PT_BYTEBUF) */ + evt_test->assert_empty_param(5); + /*=============================== ASSERT PARAMETERS ===========================*/ - evt_test->assert_num_params_pushed(4); + evt_test->assert_num_params_pushed(5); } TEST(SyscallExit, recvmsgX_tcp_connection_snaplen) @@ -188,13 +191,16 @@ TEST(SyscallExit, recvmsgX_tcp_connection_snaplen) else { evt_test->assert_empty_param(4); - evt_test->assert_num_params_pushed(4); + evt_test->assert_num_params_pushed(5); GTEST_SKIP() << "[RECVMSG_X]: we receive an empty tuple but we have all the data in the kernel to obtain the correct tuple" << std::endl; } + /* Parameter 5: msg_control (type: PT_BYTEBUF) */ + evt_test->assert_empty_param(5); + /*=============================== ASSERT PARAMETERS ===========================*/ - evt_test->assert_num_params_pushed(4); + evt_test->assert_num_params_pushed(5); } TEST(SyscallExit, recvmsgX_tcp_connection_NULL_sockaddr) @@ -283,13 +289,16 @@ TEST(SyscallExit, recvmsgX_tcp_connection_NULL_sockaddr) else { evt_test->assert_empty_param(4); - evt_test->assert_num_params_pushed(4); + evt_test->assert_num_params_pushed(5); GTEST_SKIP() << "[RECVMSG_X]: we receive an empty tuple because the pointer to sockaddr is NULL, but we should rely on kernel structs" << std::endl; } + /* Parameter 5: msg_control (type: PT_BYTEBUF) */ + evt_test->assert_empty_param(5); + /*=============================== ASSERT PARAMETERS ===========================*/ - evt_test->assert_num_params_pushed(4); + evt_test->assert_num_params_pushed(5); } TEST(SyscallExit, recvmsgX_udp_connection_snaplen) @@ -370,7 +379,7 @@ TEST(SyscallExit, recvmsgX_udp_connection_snaplen) * we don't use the userspace struct. */ evt_test->assert_tuple_inet_param(4, PPM_AF_INET, IPV4_EMPTY, IPV4_SERVER, IPV4_PORT_EMPTY_STRING, IPV4_PORT_SERVER_STRING); - evt_test->assert_num_params_pushed(4); + evt_test->assert_num_params_pushed(5); GTEST_SKIP() << "[RECVMSG_X]: we are not able to get the sorce ip + port because right now we don't use the userspace struct." << std::endl; } else @@ -378,9 +387,12 @@ TEST(SyscallExit, recvmsgX_udp_connection_snaplen) evt_test->assert_tuple_inet_param(4, PPM_AF_INET, IPV4_CLIENT, IPV4_SERVER, IPV4_PORT_CLIENT_STRING, IPV4_PORT_SERVER_STRING); } + /* Parameter 5: msg_control (type: PT_BYTEBUF) */ + evt_test->assert_empty_param(5); + /*=============================== ASSERT PARAMETERS ===========================*/ - evt_test->assert_num_params_pushed(4); + evt_test->assert_num_params_pushed(5); } TEST(SyscallExit, recvmsgX_udp_connection_NULL_sockaddr) @@ -469,9 +481,12 @@ TEST(SyscallExit, recvmsgX_udp_connection_NULL_sockaddr) GTEST_SKIP() << "[RECVMSG_X]: we send an empty tuple, but we can at least send the dest ip and source" << std::endl; } + /* Parameter 5: msg_control (type: PT_BYTEBUF) */ + evt_test->assert_empty_param(5); + /*=============================== ASSERT PARAMETERS ===========================*/ - evt_test->assert_num_params_pushed(4); + evt_test->assert_num_params_pushed(5); } #endif @@ -518,7 +533,134 @@ TEST(SyscallExit, recvmsgX_fail) /* Parameter 4: tuple (type: PT_SOCKTUPLE) */ evt_test->assert_empty_param(4); + /* Parameter 5: msg_control (type: PT_BYTEBUF) */ + evt_test->assert_empty_param(5); + + /*=============================== ASSERT PARAMETERS ===========================*/ +} + +TEST(SyscallExit, recvmsg_ancillary_data) +{ + auto evt_test = get_syscall_event_test(__NR_recvmsg, EXIT_EVENT); + + evt_test->enable_capture(); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + int32_t client_socket_fd = 0; + int32_t server_socket_fd = 0; + struct sockaddr_un client_addr = {0}; + struct sockaddr_un server_addr = {0}; + evt_test->connect_unix_client_to_server(&client_socket_fd, &client_addr, &server_socket_fd, &server_addr); + int64_t received_bytes, sent_bytes, msg_controllen; + struct msghdr msg; + struct cmsghdr *cmsg; + char cmsg_buf[CMSG_SPACE(sizeof(int))]; + struct iovec iov = { + .iov_base = (void *)FULL_MESSAGE, + .iov_len = FULL_MESSAGE_LEN, + }; + + /* We don't want to get any info about the connected socket so `addr` and `addrlen` are NULL. */ + int connected_socket_fd = syscall(__NR_accept, server_socket_fd, NULL, NULL); + assert_syscall_state(SYSCALL_SUCCESS, "accept (server)", connected_socket_fd, NOT_EQUAL, -1); + + /* Now we can fork. We still maintain the connected_socket_fd in both parent and child processes */ + pid_t pid = fork(); + if(pid) + { + /* Create a socket. It is used to pass it to the child process, just for test purposes */ + int sock = socket(AF_UNIX, SOCK_STREAM, 0); + msg = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = cmsg_buf, + .msg_controllen = sizeof(cmsg_buf) + }; + msg_controllen = msg.msg_controllen; + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(sock)); + + memcpy(CMSG_DATA(cmsg), &sock, sizeof(sock)); + + sent_bytes = syscall(__NR_sendmsg, client_socket_fd, &msg, 0); + assert_syscall_state(SYSCALL_SUCCESS, "sendmsg (client)", sent_bytes, NOT_EQUAL, -1);; + + int wstatus; + waitpid(pid, &wstatus, 0); + + syscall(__NR_shutdown, sock); + syscall(__NR_close, sock); + } else + { + char buf[FULL_MESSAGE_LEN]; + struct iovec iov = { + iov.iov_base = (void *)buf, + iov.iov_len = sizeof(buf) + }; + + struct msghdr msg = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = cmsg_buf, + .msg_controllen = sizeof(cmsg_buf) + }; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + iov.iov_base = (void *)buf; + iov.iov_len = sizeof(buf); + + received_bytes = syscall(__NR_recvmsg, connected_socket_fd, &msg, 0); + assert_syscall_state(SYSCALL_SUCCESS, "recvmsg (server)", received_bytes, NOT_EQUAL, -1); + exit(0); + } + + /* Cleaning phase */ + syscall(__NR_shutdown, connected_socket_fd, 2); + syscall(__NR_shutdown, server_socket_fd, 2); + syscall(__NR_shutdown, client_socket_fd, 2); + syscall(__NR_close, connected_socket_fd); + syscall(__NR_close, server_socket_fd); + syscall(__NR_close, client_socket_fd); + syscall(__NR_unlinkat, 0, UNIX_CLIENT, 0); + syscall(__NR_unlinkat, 0, UNIX_SERVER, 0); + + /*=============================== TRIGGER SYSCALL ===========================*/ + + evt_test->disable_capture(); + + evt_test->assert_event_presence(pid); + + 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)FULL_MESSAGE_LEN); + + /* Parameter 2: size (type: PT_UINT32) */ + evt_test->assert_numeric_param(2, (uint32_t)FULL_MESSAGE_LEN); + + /* Parameter 3: data (type: PT_BYTEBUF) */ + evt_test->assert_bytebuf_param(3, FULL_MESSAGE, DEFAULT_SNAPLEN); + + /* Parameter 5: msg_control (type: PT_BYTEBUF) */ + evt_test->assert_bytebuf_param(5, (const char *)cmsg, msg_controllen); + /*=============================== ASSERT PARAMETERS ===========================*/ + + evt_test->assert_num_params_pushed(5); } #endif diff --git a/test/drivers/test_suites/syscall_exit_suite/socketcall_x.cpp b/test/drivers/test_suites/syscall_exit_suite/socketcall_x.cpp index b98c4fbef5..e06ea8a98e 100644 --- a/test/drivers/test_suites/syscall_exit_suite/socketcall_x.cpp +++ b/test/drivers/test_suites/syscall_exit_suite/socketcall_x.cpp @@ -2026,13 +2026,16 @@ TEST(SyscallExit, socketcall_recvmsgX_no_snaplen) /// but these could be empty so this is not the correct way to retrieve information we have to /// change it. evt_test->assert_empty_param(4); - evt_test->assert_num_params_pushed(4); + evt_test->assert_num_params_pushed(5); GTEST_SKIP() << "[RECVMSG_X]: what we receive is correct but we need to reimplement it, see the code" << std::endl; } + /* Parameter 5: msg_control (type: PT_BYTEBUF) */ + evt_test->assert_empty_param(5); + /*=============================== ASSERT PARAMETERS ===========================*/ - evt_test->assert_num_params_pushed(4); + evt_test->assert_num_params_pushed(5); } TEST(SyscallExit, socketcall_recvmsgX_snaplen) @@ -2128,13 +2131,16 @@ TEST(SyscallExit, socketcall_recvmsgX_snaplen) /// but these could be empty so this is not the correct way to retrieve information we have to /// change it. evt_test->assert_empty_param(4); - evt_test->assert_num_params_pushed(4); + evt_test->assert_num_params_pushed(5); GTEST_SKIP() << "[RECVMSG_X]: what we receive is correct but we need to reimplement it, see the code" << std::endl; } + /* Parameter 5: msg_control (type: PT_BYTEBUF) */ + evt_test->assert_empty_param(5); + /*=============================== ASSERT PARAMETERS ===========================*/ - evt_test->assert_num_params_pushed(4); + evt_test->assert_num_params_pushed(5); } #endif @@ -2186,7 +2192,12 @@ TEST(SyscallExit, socketcall_recvmsgX_fail) /* Parameter 4: tuple (type: PT_SOCKTUPLE) */ evt_test->assert_empty_param(4); + /* Parameter 5: msg_control (type: PT_BYTEBUF) */ + evt_test->assert_empty_param(5); + /*=============================== ASSERT PARAMETERS ===========================*/ + + evt_test->assert_num_params_pushed(5); } #endif diff --git a/userspace/libscap/engine/gvisor/gvisor.cpp b/userspace/libscap/engine/gvisor/gvisor.cpp index d72b35129b..7706b942c6 100644 --- a/userspace/libscap/engine/gvisor/gvisor.cpp +++ b/userspace/libscap/engine/gvisor/gvisor.cpp @@ -127,6 +127,7 @@ static const struct scap_platform_vtable scap_gvisor_platform_vtable = { .is_thread_alive = scap_gvisor_is_thread_alive, .get_global_pid = NULL, .get_threadlist = gvisor_get_threadlist, + .get_fdlist = NULL, .close_platform = scap_gvisor_close_platform, .free_platform = scap_gvisor_free_platform, diff --git a/userspace/libscap/linux/scap_linux_int.h b/userspace/libscap/linux/scap_linux_int.h index 4fc7bec938..bb4951a7eb 100644 --- a/userspace/libscap/linux/scap_linux_int.h +++ b/userspace/libscap/linux/scap_linux_int.h @@ -52,6 +52,7 @@ int32_t scap_linux_refresh_proc_table(struct scap_platform* platform, struct sca bool scap_linux_is_thread_alive(struct scap_platform* platform, int64_t pid, int64_t tid, const char* comm); int32_t scap_linux_getpid_global(struct scap_platform* platform, int64_t *pid, char* error); int32_t scap_linux_get_threadlist(struct scap_platform* platform, struct ppm_proclist_info **procinfo_p, char *lasterr); +int32_t scap_linux_get_fdlist(struct scap_platform* platform, struct scap_threadinfo *tinfo, char *lasterr); // read all sockets and add them to the socket table hashed by their ino int32_t scap_fd_read_sockets(char* procdir, struct scap_ns_socket_list* sockets, char *error); diff --git a/userspace/libscap/linux/scap_linux_platform.c b/userspace/libscap/linux/scap_linux_platform.c index e2ead6f57d..ca3020579e 100644 --- a/userspace/libscap/linux/scap_linux_platform.c +++ b/userspace/libscap/linux/scap_linux_platform.c @@ -116,6 +116,7 @@ static const struct scap_platform_vtable scap_linux_platform = { .is_thread_alive = scap_linux_is_thread_alive, .get_global_pid = scap_linux_getpid_global, .get_threadlist = scap_linux_get_threadlist, + .get_fdlist = scap_linux_get_fdlist, .close_platform = scap_linux_close_platform, .free_platform = scap_linux_free_platform, }; diff --git a/userspace/libscap/linux/scap_procs.c b/userspace/libscap/linux/scap_procs.c index 7d8483f160..c624bf9a3c 100644 --- a/userspace/libscap/linux/scap_procs.c +++ b/userspace/libscap/linux/scap_procs.c @@ -1377,3 +1377,16 @@ int32_t scap_linux_get_threadlist(struct scap_platform* platform, struct ppm_pro } return SCAP_SUCCESS; } + +int32_t scap_linux_get_fdlist(struct scap_platform* platform, struct scap_threadinfo *tinfo, char *lasterr) +{ + uint64_t num_fds_ret = 0; + char proc_dir[SCAP_MAX_PATH_SIZE]; + struct scap_ns_socket_list* sockets_by_ns = NULL; + struct scap_linux_platform* linux_platform = (struct scap_linux_platform*)platform; + + // We collect file descriptors only for the main thread + snprintf(proc_dir, sizeof(proc_dir), "%s/proc/%d/", scap_get_host_root(), tinfo->pid); + + return scap_fd_scan_fd_dir(linux_platform, &platform->m_proclist, proc_dir, tinfo, &sockets_by_ns, &num_fds_ret, lasterr); +} diff --git a/userspace/libscap/scap_platform_api.c b/userspace/libscap/scap_platform_api.c index 87cdedbb82..89a50fd542 100644 --- a/userspace/libscap/scap_platform_api.c +++ b/userspace/libscap/scap_platform_api.c @@ -159,3 +159,19 @@ struct ppm_proclist_info* scap_get_threadlist(scap_t* handle) snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "operation not supported"); return NULL; } + + +int32_t scap_get_fdlist(struct scap* handle, struct scap_threadinfo *tinfo) +{ + if (handle && handle->m_platform && handle->m_platform->m_vtable->get_fdlist) + { + int32_t res = handle->m_platform->m_vtable->get_fdlist(handle->m_platform, tinfo, handle->m_lasterr); + if(res != SCAP_SUCCESS) + { + return SCAP_FAILURE; + } + } + + snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "operation not supported"); + return SCAP_FAILURE; +} diff --git a/userspace/libscap/scap_platform_api.h b/userspace/libscap/scap_platform_api.h index 49e3fef336..15e32b1678 100644 --- a/userspace/libscap/scap_platform_api.h +++ b/userspace/libscap/scap_platform_api.h @@ -126,6 +126,11 @@ const scap_agent_info* scap_get_agent_info(struct scap* handle); */ struct ppm_proclist_info* scap_get_threadlist(struct scap* handle); +/*! + \brief Get the file descriptor list for a given pid. +*/ +int32_t scap_get_fdlist(struct scap* handle, struct scap_threadinfo* tinfo); + #ifdef __cplusplus }; #endif diff --git a/userspace/libscap/scap_platform_impl.h b/userspace/libscap/scap_platform_impl.h index 4abcdfe555..78248709e3 100644 --- a/userspace/libscap/scap_platform_impl.h +++ b/userspace/libscap/scap_platform_impl.h @@ -65,6 +65,7 @@ struct scap_platform_vtable bool (*is_thread_alive)(struct scap_platform*, int64_t pid, int64_t tid, const char* comm); int32_t (*get_global_pid)(struct scap_platform*, int64_t *pid, char *error); int32_t (*get_threadlist)(struct scap_platform* platform, struct ppm_proclist_info **procinfo_p, char *lasterr); + int32_t (*get_fdlist)(struct scap_platform* platform, struct scap_threadinfo *tinfo, char *lasterr); // close the platform structure // clean up all data, make it ready for another call to `init_platform` diff --git a/userspace/libsinsp/parsers.cpp b/userspace/libsinsp/parsers.cpp index d28fd2be79..ab89391af4 100644 --- a/userspace/libsinsp/parsers.cpp +++ b/userspace/libsinsp/parsers.cpp @@ -4921,6 +4921,42 @@ void sinsp_parser::parse_rw_exit(sinsp_evt *evt) (*it)->on_read(evt, data, datalen); } } + + // + // Check if recvmsg contains ancillary data. If so, we check for SCM_RIGHTS, + // which is used to pass FDs between processes, and update the sinsp state + // accordingly via procfs scan. + // +#ifndef _WIN32 + if(etype == PPME_SOCKET_RECVMSG_X && evt->get_num_params() >= 5) + { + parinfo = evt->get_param(4); + if(parinfo->m_len > sizeof(struct cmsghdr)) + { + struct cmsghdr *cmsg = (struct cmsghdr *)parinfo->m_val; + if(cmsg->cmsg_type == SCM_RIGHTS) + { + auto scap_tinfo = scap_proc_alloc(m_inspector->m_h); + if (scap_tinfo == NULL) + { + g_logger.format(sinsp_logger::SEV_CRITICAL, "scap_proc_alloc failed, proc table will not be updated with new fds."); + return; + } + m_inspector->m_thread_manager->thread_to_scap(*evt->m_tinfo, scap_tinfo); + + // Get the new fds. The callbacks we have registered populate the fd table + // with the new file descriptors. + if (scap_get_fdlist(m_inspector->m_h, scap_tinfo) != SCAP_SUCCESS) + { + g_logger.format(sinsp_logger::SEV_ERROR, "scap_get_fdlist failed, proc table will not be updated with new fds."); + } + + scap_proc_free(m_inspector->m_h, scap_tinfo); + } + } + } +#endif + } else {