Skip to content

Commit

Permalink
feat(userspace/libsinsp): improve recvmsg SCM_RIGHTS cmsg handling
Browse files Browse the repository at this point in the history
Parse all control messages instead of parsing just the first one.
Leverage the new scap_get_fdinfo API to get info only from the file
in procfs associated to the file descriptor, instead of scanning each
time the entire procfs fd directory.

Signed-off-by: Leonardo Di Giovanna <leonardodigiovanna1@gmail.com>
Co-authored-by: Roberto Scolaro <roberto.scolaro21@gmail.com>
  • Loading branch information
ekoops and therealbobo committed Jan 31, 2025
1 parent 710a236 commit 8e104cf
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 34 deletions.
114 changes: 80 additions & 34 deletions userspace/libsinsp/parsers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3662,6 +3662,85 @@ void sinsp_parser::parse_fspath_related_exit(sinsp_evt *evt) {
}
}

// "No aligned" macros definitions. These macros are equivalent to the corresponding variants
// without the "no aligned suffix" but avoid unaligned access to the underlaying data.
extern struct cmsghdr *__cmsg_nxthdr_no_aligned(struct msghdr *__mhdr,
struct cmsghdr *__cmsg) __THROW;
#define CMSG_NXTHDR_NO_ALIGNED(mhdr, cmsg) __cmsg_nxthdr_no_aligned(mhdr, cmsg)
#ifdef __USE_EXTERN_INLINES
#ifndef _EXTERN_INLINE
#define _EXTERN_INLINE __extern_inline
#endif
_EXTERN_INLINE struct cmsghdr *__NTH(__cmsg_nxthdr_no_aligned(struct msghdr *__mhdr,

Check warning on line 3674 in userspace/libsinsp/parsers.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3674

Added line #L3674 was not covered by tests
struct cmsghdr *__cmsg)) {
size_t cmsg_len;
memcpy(&cmsg_len, __cmsg + offsetof(struct cmsghdr, cmsg_len), sizeof(size_t));
if((size_t)cmsg_len < sizeof(struct cmsghdr))

Check warning on line 3678 in userspace/libsinsp/parsers.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3676-L3678

Added lines #L3676 - L3678 were not covered by tests
// The kernel header does this so there may be a reason.
return (struct cmsghdr *)0;

__cmsg = (struct cmsghdr *)((unsigned char *)__cmsg + CMSG_ALIGN(cmsg_len));
if((unsigned char *)(__cmsg + 1) >

Check warning on line 3683 in userspace/libsinsp/parsers.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3682-L3683

Added lines #L3682 - L3683 were not covered by tests
((unsigned char *)__mhdr->msg_control + __mhdr->msg_controllen) ||
((unsigned char *)__cmsg + CMSG_ALIGN(cmsg_len) >
((unsigned char *)__mhdr->msg_control + __mhdr->msg_controllen)))
// No more entries.
return (struct cmsghdr *)0;
return __cmsg;
}
#endif // Use `extern inline'.

#if __glibc_c99_flexarr_available
#define CMSG_DATA_NO_ALIGNED(cmsg) (cmsg + offsetof(struct cmsghdr, __cmsg_data))
#else
#define CMSG_DATA_NO_ALIGNED(cmsg) ((unsigned char *)((struct cmsghdr *)(cmsg) + 1))
#endif

#ifndef _WIN32
inline void sinsp_parser::process_recvmsg_ancillary_data(sinsp_evt *evt,

Check warning on line 3700 in userspace/libsinsp/parsers.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3700

Added line #L3700 was not covered by tests
sinsp_evt_param const *parinfo) const {
// Create a msg header to be used with CMSG_* macros.
msghdr msgh;
msgh.msg_control = (void *)parinfo->m_val;
msgh.msg_controllen = parinfo->m_len;

Check warning on line 3705 in userspace/libsinsp/parsers.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3703-L3705

Added lines #L3703 - L3705 were not covered by tests
// Seek for SCM_RIGHTS control message headers and extract passed file descriptors.
for(cmsghdr *cmsg = CMSG_FIRSTHDR(&msgh); cmsg != nullptr;
cmsg = CMSG_NXTHDR_NO_ALIGNED(&msgh, cmsg)) {
cmsghdr c;
memcpy(&c, cmsg, sizeof(cmsghdr));
int const cmsg_type = c.cmsg_type;
size_t const cmsg_len = c.cmsg_len;

Check warning on line 3712 in userspace/libsinsp/parsers.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3709-L3712

Added lines #L3709 - L3712 were not covered by tests
if(cmsg_type == SCM_RIGHTS) {
char error[SCAP_LASTERR_SIZE];
scap_threadinfo scap_tinfo{};
memset(&scap_tinfo, 0, sizeof(scap_tinfo));
m_inspector->m_thread_manager->thread_to_scap(*evt->get_tinfo(), &scap_tinfo);

Check warning on line 3717 in userspace/libsinsp/parsers.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3714-L3717

Added lines #L3714 - L3717 were not covered by tests
#define SCM_MAX_FD 253 // Taken from kernel.
int fds[SCM_MAX_FD];
unsigned long const data_size = cmsg_len - CMSG_LEN(0);
unsigned long const fds_len = data_size / sizeof(int);

Check warning on line 3721 in userspace/libsinsp/parsers.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3719-L3721

Added lines #L3719 - L3721 were not covered by tests
// Guard against malformed event, by checking that data size is a multiple of
// sizeof(int) (file descriptor size) and the control message doesn't contain more data
// than allowed by kernel constraints.
if(data_size % sizeof(int) || fds_len > SCM_MAX_FD) {
continue;

Check warning on line 3726 in userspace/libsinsp/parsers.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3726

Added line #L3726 was not covered by tests
}
#undef SCM_MAX_FD
memcpy(&fds, CMSG_DATA_NO_ALIGNED(cmsg), data_size);

Check warning on line 3729 in userspace/libsinsp/parsers.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3729

Added line #L3729 was not covered by tests
for(unsigned long i = 0; i < fds_len; i++) {
if(scap_get_fdinfo(m_inspector->get_scap_platform(), &scap_tinfo, fds[i], error) !=
SCAP_SUCCESS) {
libsinsp_logger()->format(sinsp_logger::SEV_DEBUG,

Check warning on line 3733 in userspace/libsinsp/parsers.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3733

Added line #L3733 was not covered by tests
"scap_get_fdinfo failed: %s, proc table will not be "
"updated with new fd.",
error);
}
}
}
}
}
#endif

void sinsp_parser::parse_rw_exit(sinsp_evt *evt) {
const sinsp_evt_param *parinfo;
int64_t retval;
Expand Down Expand Up @@ -3792,40 +3871,7 @@ void sinsp_parser::parse_rw_exit(sinsp_evt *evt) {

if(cmparam != -1) {
parinfo = evt->get_param(cmparam);
if(parinfo->m_len > sizeof(cmsghdr)) {
cmsghdr cmsg;
memcpy(&cmsg, parinfo->m_val, sizeof(cmsghdr));
if(cmsg.cmsg_type == SCM_RIGHTS) {
char error[SCAP_LASTERR_SIZE];
scap_threadinfo scap_tinfo{};

memset(&scap_tinfo, 0, sizeof(scap_tinfo));

m_inspector->m_thread_manager->thread_to_scap(*evt->get_tinfo(),
&scap_tinfo);

// Store current fd; it might get changed by scap_get_fdlist below.
int64_t fd = -1;
if(evt->get_fd_info()) {
fd = evt->get_fd_info()->m_fd;
}

// Get the new fds. The callbacks we have registered populate the fd table
// with the new file descriptors.
if(scap_get_fdlist(m_inspector->get_scap_platform(), &scap_tinfo, error) !=
SCAP_SUCCESS) {
libsinsp_logger()->format(sinsp_logger::SEV_DEBUG,
"scap_get_fdlist failed: %s, proc table will "
"not be updated with new fds.",
error);
}

// Force refresh event fdinfo
if(fd != -1) {
evt->set_fd_info(evt->get_tinfo()->get_fd(fd));
}
}
}
process_recvmsg_ancillary_data(evt, parinfo);

Check warning on line 3874 in userspace/libsinsp/parsers.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3874

Added line #L3874 was not covered by tests
}
#endif

Expand Down
5 changes: 5 additions & 0 deletions userspace/libsinsp/parsers.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ class sinsp_parser {
inline void add_pipe(sinsp_evt* evt, int64_t fd, uint64_t ino, uint32_t openflags);
// Return false if the update didn't happen (for example because the tuple is NULL)
bool update_fd(sinsp_evt* evt, const sinsp_evt_param* parinfo);
#ifndef _WIN32
// Process recvmsg ancillary data
inline void process_recvmsg_ancillary_data(sinsp_evt* evt,
sinsp_evt_param const* parinfo) const;
#endif

// Next 4 return false if the update didn't happen because the tuple is identical to the given
// address
Expand Down

0 comments on commit 8e104cf

Please # to comment.