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 <[email protected]>
Co-authored-by: Roberto Scolaro <[email protected]>
  • Loading branch information
ekoops and therealbobo committed Jan 31, 2025
1 parent 710a236 commit a2ffa0a
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 34 deletions.
115 changes: 81 additions & 34 deletions userspace/libsinsp/parsers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3662,6 +3662,86 @@ void sinsp_parser::parse_fspath_related_exit(sinsp_evt *evt) {
}
}

#ifndef _WIN32

// "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 3676 in userspace/libsinsp/parsers.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3676

Added line #L3676 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 3680 in userspace/libsinsp/parsers.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3678-L3680

Added lines #L3678 - L3680 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 3685 in userspace/libsinsp/parsers.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3684-L3685

Added lines #L3684 - L3685 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

inline void sinsp_parser::process_recvmsg_ancillary_data(sinsp_evt *evt,

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

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3701

Added line #L3701 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 3706 in userspace/libsinsp/parsers.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3704-L3706

Added lines #L3704 - L3706 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 3713 in userspace/libsinsp/parsers.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3710-L3713

Added lines #L3710 - L3713 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 3718 in userspace/libsinsp/parsers.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3715-L3718

Added lines #L3715 - L3718 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 3722 in userspace/libsinsp/parsers.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3720-L3722

Added lines #L3720 - L3722 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 3727 in userspace/libsinsp/parsers.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3727

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

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

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3730

Added line #L3730 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 3734 in userspace/libsinsp/parsers.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3734

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

void sinsp_parser::parse_rw_exit(sinsp_evt *evt) {
const sinsp_evt_param *parinfo;
int64_t retval;
Expand Down Expand Up @@ -3792,40 +3872,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 3875 in userspace/libsinsp/parsers.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/parsers.cpp#L3875

Added line #L3875 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 a2ffa0a

Please sign in to comment.