diff --git a/.github/workflows/drivers_ci.yml b/.github/workflows/drivers_ci.yml index 29238df125..c7f1b35428 100644 --- a/.github/workflows/drivers_ci.yml +++ b/.github/workflows/drivers_ci.yml @@ -249,7 +249,7 @@ jobs: build-modern-bpf-skeleton: needs: paths-filter # See https://github.com/actions/runner/issues/409#issuecomment-1158849936 - runs-on: ${{ (inputs.arch == 'aarch64' && 'actuated-arm64-8cpu-16gb') || 'ubuntu-latest' }} + runs-on: 'ubuntu-latest' if: needs.paths-filter.outputs.driver == 'true' || needs.paths-filter.outputs.libscap == 'true' || needs.paths-filter.outputs.libpman == 'true' container: fedora:latest steps: @@ -270,21 +270,38 @@ jobs: - name: Upload skeleton uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: - name: bpf_probe_${{ inputs.arch }}.skel.h + name: bpf_probe_x86_64.skel.h path: skeleton-build/skel_dir/bpf_probe.skel.h retention-days: 1 build-scap-open-w-extern-bpf-skeleton: + env: + ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true needs: [paths-filter,build-modern-bpf-skeleton] # See https://github.com/actions/runner/issues/409#issuecomment-1158849936 - runs-on: ${{ (inputs.arch == 'aarch64' && 'actuated-arm64-8cpu-16gb') || 'ubuntu-latest' }} + runs-on: 'ubuntu-latest' if: needs.paths-filter.outputs.driver == 'true' || needs.paths-filter.outputs.libscap == 'true' || needs.paths-filter.outputs.libpman == 'true' container: centos:7 steps: # Always install deps before invoking checkout action, to properly perform a full clone. - - name: Install build dependencies + - name: Fix mirrors to use vault.centos.org + run: | + sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo + sed -i s/^#.*baseurl=http/baseurl=https/g /etc/yum.repos.d/*.repo + sed -i s/^mirrorlist=http/#mirrorlist=https/g /etc/yum.repos.d/*.repo + + - name: Install scl repos run: | yum -y install centos-release-scl + + - name: Fix new mirrors to use vault.centos.org + run: | + sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo + sed -i s/^#.*baseurl=http/baseurl=https/g /etc/yum.repos.d/*.repo + sed -i s/^mirrorlist=http/#mirrorlist=https/g /etc/yum.repos.d/*.repo + + - name: Install build dependencies + run: | yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ source /opt/rh/devtoolset-9/enable yum install -y wget git make m4 rpm-build perl-IPC-Cmd @@ -296,7 +313,7 @@ jobs: - name: Download skeleton uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: - name: bpf_probe_${{ inputs.arch }}.skel.h + name: bpf_probe_x86_64.skel.h path: /tmp - name: Install updated cmake diff --git a/cmake/modules/zlib.cmake b/cmake/modules/zlib.cmake index 6d3903ea1f..b03e8acc1b 100644 --- a/cmake/modules/zlib.cmake +++ b/cmake/modules/zlib.cmake @@ -68,19 +68,24 @@ else() else() if(BUILD_SHARED_LIBS) set(ZLIB_LIB_SUFFIX "${CMAKE_SHARED_LIBRARY_SUFFIX}") + set(ZLIB_LIB "${ZLIB_SRC}/lib/zlib$<$:d>${ZLIB_LIB_SUFFIX}") else() set(ZLIB_LIB_SUFFIX "${CMAKE_STATIC_LIBRARY_SUFFIX}") + set(ZLIB_LIB "${ZLIB_SRC}/lib/zlibstatic$<$:d>${ZLIB_LIB_SUFFIX}") endif() - set(ZLIB_LIB "${ZLIB_SRC}/zlib${ZLIB_LIB_SUFFIX}") ExternalProject_Add(zlib PREFIX "${PROJECT_BINARY_DIR}/zlib-prefix" URL "https://github.com/madler/zlib/archive/v1.2.13.tar.gz" URL_HASH "SHA256=1525952a0a567581792613a9723333d7f8cc20b87a81f920fb8bc7e3f2251428" - CONFIGURE_COMMAND "" - BUILD_COMMAND nmake -f win32/Makefile.msc LOC=-DZLIB_WINAPI BUILD_IN_SOURCE 1 BUILD_BYPRODUCTS ${ZLIB_LIB} - INSTALL_COMMAND "") + CMAKE_ARGS + -DCMAKE_POLICY_DEFAULT_CMP0091:STRING=NEW + -DCMAKE_MSVC_RUNTIME_LIBRARY=${CMAKE_MSVC_RUNTIME_LIBRARY} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_POSITION_INDEPENDENT_CODE=${ENABLE_PIC} + -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS} + -DCMAKE_INSTALL_PREFIX=${ZLIB_SRC}) install(FILES "${ZLIB_LIB}" DESTINATION "${CMAKE_INSTALL_LIBDIR}/${LIBS_PACKAGE_NAME}" COMPONENT "libs-deps") install(FILES ${ZLIB_HEADERS} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${LIBS_PACKAGE_NAME}/zlib" diff --git a/driver/bpf/fillers.h b/driver/bpf/fillers.h index 85d2c87de0..80f623f3c3 100644 --- a/driver/bpf/fillers.h +++ b/driver/bpf/fillers.h @@ -2392,48 +2392,28 @@ static __always_inline bool get_exe_writable(struct inode *inode, struct cred *c return false; } -static __always_inline bool get_exe_upper_layer(struct dentry *dentry, struct super_block *sb) +static __always_inline bool get_exe_upper_layer(struct file *file) { + struct dentry* dentry = NULL; + bpf_probe_read_kernel(&dentry, sizeof(dentry), &file->f_path.dentry); + struct super_block* sb = (struct super_block*)_READ(dentry->d_sb); unsigned long sb_magic = _READ(sb->s_magic); - if(sb_magic == PPM_OVERLAYFS_SUPER_MAGIC) - { - struct dentry *upper_dentry = NULL; - char *vfs_inode = (char *)_READ(dentry->d_inode); - - // Pointer arithmetics due to unexported ovl_inode struct - // warning: this works if and only if the dentry pointer is placed right after the inode struct - struct dentry *tmp = (struct dentry *)(vfs_inode + sizeof(struct inode)); - upper_dentry = _READ(tmp); - if(!upper_dentry) - { - return false; - } - unsigned int d_flags = _READ(dentry->d_flags); - bool disconnected = (d_flags & DCACHE_DISCONNECTED); - if(disconnected) - { - return true; - } - -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0) - struct ovl_entry *oe = (struct ovl_entry*)_READ(dentry->d_fsdata); - unsigned long has_upper = (unsigned long)_READ(oe->has_upper); -#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0) - struct ovl_entry *oe = (struct ovl_entry*)_READ(dentry->d_fsdata); - unsigned long flags = _READ(oe->flags); - unsigned long has_upper = (flags & (1U << (OVL_E_UPPER_ALIAS))); -#else - unsigned long flags = (unsigned long)_READ(dentry->d_fsdata); - unsigned long has_upper = (flags & (1U << (OVL_E_UPPER_ALIAS))); -#endif + if(sb_magic != PPM_OVERLAYFS_SUPER_MAGIC) + { + return false; + } - if(has_upper) - { - return true; - } + char *vfs_inode = (char *)_READ(dentry->d_inode); + struct dentry *upper_dentry = NULL; + bpf_probe_read_kernel(&upper_dentry, sizeof(upper_dentry), (char *)vfs_inode + sizeof(struct inode)); + if(!upper_dentry) + { + return false; } - return false; + + struct inode *upper_ino = _READ(upper_dentry->d_inode); + return _READ(upper_ino->i_ino) != 0; } FILLER(proc_startupdate, true) @@ -2945,43 +2925,27 @@ FILLER(execve_extra_tail_1, true) struct cred *cred = (struct cred *)_READ(task->cred); struct file *exe_file = get_exe_file(task); struct inode *inode = get_file_inode(exe_file); - struct path f_path = (struct path)_READ(exe_file->f_path); - struct dentry* dentry = f_path.dentry; - -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0) - struct super_block* sb = _READ(dentry->d_sb); -#else - struct super_block *sb = _READ(inode->i_sb); -#endif - - /* `exe_writable` and `exe_upper_layer` flag logic */ - bool exe_writable = false; - bool exe_upper_layer = false; - uint32_t flags = 0; - kuid_t euid; + kuid_t euid = {0}; - if(sb && inode) + if(inode) { /* * exe_writable */ - exe_writable = get_exe_writable(inode, cred); + bool exe_writable = get_exe_writable(inode, cred); if (exe_writable) { flags |= PPM_EXE_WRITABLE; } + } - /* - * exe_upper_layer - */ - exe_upper_layer = get_exe_upper_layer(dentry,sb); - if (exe_upper_layer) - { - flags |= PPM_EXE_UPPER_LAYER; - } - - // write all additional flags for execve family here... + /* + * exe_upper_layer + */ + if(exe_file && get_exe_upper_layer(exe_file)) + { + flags |= PPM_EXE_UPPER_LAYER; } if(exe_file && get_exe_from_memfd(exe_file)) @@ -6869,42 +6833,27 @@ FILLER(sched_prog_exec_4, false) struct cred *cred = (struct cred *)_READ(task->cred); struct file *exe_file = get_exe_file(task); struct inode *inode = get_file_inode(exe_file); - struct path f_path = (struct path)_READ(exe_file->f_path); - struct dentry* dentry = f_path.dentry; - -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0) - struct super_block* sb = _READ(dentry->d_sb); -#else - struct super_block *sb = _READ(inode->i_sb); -#endif - - /* `exe_writable` and `exe_upper_layer` flag logic */ - bool exe_writable = false; - bool exe_upper_layer = false; uint32_t flags = 0; - kuid_t euid; + kuid_t euid = {0}; - if(sb && inode) + if(inode) { /* * exe_writable */ - exe_writable = get_exe_writable(inode, cred); + bool exe_writable = get_exe_writable(inode, cred); if (exe_writable) { flags |= PPM_EXE_WRITABLE; } + } - /* - * exe_upper_layer - */ - exe_upper_layer = get_exe_upper_layer(dentry,sb); - if (exe_upper_layer) - { - flags |= PPM_EXE_UPPER_LAYER; - } - - // write all additional flags for execve family here... + /* + * exe_upper_layer + */ + if (exe_file && get_exe_upper_layer(exe_file)) + { + flags |= PPM_EXE_UPPER_LAYER; } if(exe_file && get_exe_from_memfd(exe_file)) diff --git a/driver/bpf/missing_definitions.h b/driver/bpf/missing_definitions.h index 6777eb1e74..e4c21f86ef 100644 --- a/driver/bpf/missing_definitions.h +++ b/driver/bpf/missing_definitions.h @@ -10,37 +10,6 @@ or GPL2.txt for full copies of the license. #ifndef __BPF_MISSING_DEFINITIONS_H__ #define __BPF_MISSING_DEFINITIONS_H__ -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0) -struct ovl_entry { - union { - struct { - unsigned long has_upper; - bool opaque; - }; - struct rcu_head rcu; - }; - unsigned numlower; - struct path lowerstack[]; -}; -#else -struct ovl_entry { - union { - struct { - unsigned long flags; - }; - struct rcu_head rcu; - }; - unsigned numlower; - //struct ovl_path lowerstack[]; -}; - -enum ovl_entry_flag { - OVL_E_UPPER_ALIAS, - OVL_E_OPAQUE, - OVL_E_CONNECTED, -}; -#endif - #include /* This require the inlclude `linux/mount.h` for `vfsmount` definition */ struct mount { diff --git a/driver/modern_bpf/definitions/missing_definitions.h b/driver/modern_bpf/definitions/missing_definitions.h index f7264886ae..c4e05bf3d8 100644 --- a/driver/modern_bpf/definitions/missing_definitions.h +++ b/driver/modern_bpf/definitions/missing_definitions.h @@ -1586,9 +1586,4 @@ #define MODULE_INIT_COMPRESSED_FILE 4 /*==================================== FINIT FLAGS ================================*/ -/*==================================== OVERLAY FLAGS ================================*/ -#define DCACHE_DISCONNECTED 0x20 -#define OVL_E_UPPER_ALIAS 0 -/*==================================== OVERLAY FLAGS ================================*/ - #endif /* __MISSING_DEFINITIONS_H__ */ diff --git a/driver/modern_bpf/definitions/struct_flavors.h b/driver/modern_bpf/definitions/struct_flavors.h index 677a98c630..b7a9508fb9 100644 --- a/driver/modern_bpf/definitions/struct_flavors.h +++ b/driver/modern_bpf/definitions/struct_flavors.h @@ -52,11 +52,6 @@ struct inode___v6_7 { struct timespec64 __i_mtime; }; -struct ovl_entry___before_v6_5 -{ - long unsigned int flags; -}; - #ifndef BPF_NO_PRESERVE_ACCESS_INDEX #pragma clang attribute pop #endif diff --git a/driver/modern_bpf/helpers/extract/extract_from_kernel.h b/driver/modern_bpf/helpers/extract/extract_from_kernel.h index 6306e84d84..f8f3380d66 100644 --- a/driver/modern_bpf/helpers/extract/extract_from_kernel.h +++ b/driver/modern_bpf/helpers/extract/extract_from_kernel.h @@ -831,60 +831,35 @@ static __always_inline void extract__egid(struct task_struct *task, uint32_t *eg // EXECVE FLAGS EXTRACTION //////////////////////// -static __always_inline bool extract__exe_upper_layer(struct inode *inode, struct file *exe_file) +static __always_inline bool extract__exe_upper_layer(struct file *file) { - unsigned long sb_magic = BPF_CORE_READ(inode, i_sb, s_magic); + struct dentry *dentry = (struct dentry *)BPF_CORE_READ(file, f_path.dentry); + unsigned long sb_magic = BPF_CORE_READ(dentry, d_sb, s_magic); - if(sb_magic == PPM_OVERLAYFS_SUPER_MAGIC) + if(sb_magic != PPM_OVERLAYFS_SUPER_MAGIC) { - struct dentry *upper_dentry = NULL; - char *vfs_inode = (char *)inode; - unsigned long inode_size = bpf_core_type_size(struct inode); - if(!inode_size) - { - return false; - } - - bpf_probe_read_kernel(&upper_dentry, sizeof(upper_dentry), vfs_inode + inode_size); - - if(!upper_dentry) - { - return false; - } - - struct dentry *dentry = (struct dentry *)BPF_CORE_READ(exe_file, f_path.dentry); - - unsigned int d_flags = BPF_CORE_READ(dentry, d_flags); - bool disconnected = (d_flags & DCACHE_DISCONNECTED); - if(disconnected) - { - return true; - } - - unsigned long flags = 0; - if(bpf_core_field_exists(((struct ovl_entry___before_v6_5*)0)->flags)) - { - // kernel <6.5 - struct ovl_entry___before_v6_5 *oe = (struct ovl_entry___before_v6_5*)BPF_CORE_READ(dentry, d_fsdata); - flags = (unsigned long)BPF_CORE_READ(oe, flags); - } - else - { - // In kernels >=6.5 d_fsdata represents an ovl_entry_flag. - flags = (unsigned long)BPF_CORE_READ(dentry, d_fsdata); - } + return false; + } - unsigned long has_upper = (flags & (1U << (OVL_E_UPPER_ALIAS))); - if(has_upper) - { - return true; - } + char *vfs_inode = (char *)BPF_CORE_READ(dentry, d_inode); + // We need to compute the size of the inode struct at load time since it can change between kernel versions + unsigned long inode_size = bpf_core_type_size(struct inode); + if(!inode_size) + { + return false; + } + struct dentry *upper_dentry = NULL; + bpf_probe_read_kernel(&upper_dentry, sizeof(upper_dentry), (char *)vfs_inode + inode_size); + if(!upper_dentry) + { + return false; } - return false; + return BPF_CORE_READ(upper_dentry, d_inode, i_ino) != 0; } + /* * Detect whether the file being referenced is an anonymous file created using memfd_create() * and is being executed by referencing its file descriptor (fd). This type of file does not diff --git a/driver/modern_bpf/programs/attached/events/sched_process_exec.bpf.c b/driver/modern_bpf/programs/attached/events/sched_process_exec.bpf.c index d60638039b..cc30218afb 100644 --- a/driver/modern_bpf/programs/attached/events/sched_process_exec.bpf.c +++ b/driver/modern_bpf/programs/attached/events/sched_process_exec.bpf.c @@ -170,7 +170,7 @@ int BPF_PROG(t1_sched_p_exec, { flags |= PPM_EXE_WRITABLE; } - if(extract__exe_upper_layer(exe_inode, exe_file)) + if(extract__exe_upper_layer(exe_file)) { flags |= PPM_EXE_UPPER_LAYER; } diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/execve.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/execve.bpf.c index 23579738ff..1b7a31d51b 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/execve.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/execve.bpf.c @@ -230,7 +230,7 @@ int BPF_PROG(t1_execve_x, { flags |= PPM_EXE_WRITABLE; } - if(extract__exe_upper_layer(exe_inode, exe_file)) + if(extract__exe_upper_layer(exe_file)) { flags |= PPM_EXE_UPPER_LAYER; } diff --git a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/execveat.bpf.c b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/execveat.bpf.c index 718d876877..75f225701e 100644 --- a/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/execveat.bpf.c +++ b/driver/modern_bpf/programs/tail_called/events/syscall_dispatched_events/execveat.bpf.c @@ -244,7 +244,7 @@ int BPF_PROG(t1_execveat_x, { flags |= PPM_EXE_WRITABLE; } - if(extract__exe_upper_layer(exe_inode, exe_file)) + if(extract__exe_upper_layer(exe_file)) { flags |= PPM_EXE_UPPER_LAYER; } diff --git a/driver/ppm_fillers.c b/driver/ppm_fillers.c index ef75021338..6d34d712e3 100644 --- a/driver/ppm_fillers.c +++ b/driver/ppm_fillers.c @@ -61,35 +61,6 @@ struct ovl_entry { unsigned numlower; struct path lowerstack[]; }; -#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0) -struct ovl_entry { - union { - struct { - unsigned long has_upper; - bool opaque; - }; - struct rcu_head rcu; - }; - unsigned numlower; - struct path lowerstack[]; -}; -#else -struct ovl_entry { - union { - struct { - unsigned long flags; - }; - struct rcu_head rcu; - }; - unsigned numlower; - //struct ovl_path lowerstack[]; -}; - -enum ovl_entry_flag { - OVL_E_UPPER_ALIAS, - OVL_E_OPAQUE, - OVL_E_CONNECTED, -}; #endif #define merge_64(hi, lo) ((((unsigned long long)(hi)) << 32) + ((lo) & 0xffffffffUL)) @@ -881,54 +852,76 @@ static uint32_t ppm_get_tty(void) return tty_nr; } -bool ppm_is_upper_layer(struct file *exe_file){ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) - struct super_block *sb = NULL; - unsigned long sb_magic = 0; -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0) - sb = exe_file->f_path.dentry->d_sb; -#else - sb = exe_file->f_inode->i_sb; -#endif - if(sb) +static bool ppm_is_upper_layer(struct file *file) +{ + // 3.18 is the Kernel version where overlayfs was introduced +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) + return false; +#else + struct dentry * dentry = NULL; + struct super_block* sb = NULL; + struct dentry *upper_dentry = NULL; + struct inode * upper_ino = NULL; + + if(!file) { - struct ovl_entry *oe = (struct ovl_entry*)(exe_file->f_path.dentry->d_fsdata); - sb_magic = sb->s_magic; - if(sb_magic == PPM_OVERLAYFS_SUPER_MAGIC && oe) - { - unsigned long has_upper = 0; + return false; + } + + dentry = file->f_path.dentry; + if(!dentry) + { + return false; + } + + sb = dentry->d_sb; + if(!sb) + { + return false; + } + + if(sb->s_magic != PPM_OVERLAYFS_SUPER_MAGIC) + { + return false; + } + #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0) - if(oe->__upperdentry) - { - return true; - } -#else - struct dentry *upper_dentry = NULL; - unsigned int d_flags = exe_file->f_path.dentry->d_flags; - bool disconnected = (d_flags & DCACHE_DISCONNECTED); - - // Pointer arithmetics due to unexported ovl_inode struct - // warning: this works if and only if the dentry pointer - // is placed right after the inode struct - upper_dentry = (struct dentry *)((char *)exe_file->f_path.dentry->d_inode + sizeof(struct inode)); - -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0) - has_upper = oe->has_upper; -#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0) - has_upper = test_bit(OVL_E_UPPER_ALIAS, &(oe->flags)); + // New scope to avoid `ISO C90 forbids mixed declarations and code` error + { + struct ovl_entry *oe = (struct ovl_entry*)(dentry->d_fsdata); + if(!oe) + { + return false; + } + upper_dentry = oe->__upperdentry; + } #else - has_upper = test_bit(OVL_E_UPPER_ALIAS, (unsigned long*)&oe); -#endif - - if(upper_dentry && (has_upper || disconnected)) - { - return true; - } -#endif + { + char *vfs_inode = (char*)dentry->d_inode; + if(!vfs_inode) + { + return false; } + + // Pointer arithmetics due to unexported ovl_inode struct + // warning: this works if and only if the dentry pointer + // is placed right after the inode struct + // todo!: this is dangerous we should find a way to check it at compile time. + upper_dentry = *(struct dentry **)(vfs_inode + sizeof(struct inode)); } -#endif - return false; +#endif // LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0) + if(!upper_dentry) + { + return false; + } + + upper_ino = upper_dentry->d_inode; + if(!upper_ino) + { + return false; + } + return upper_ino->i_ino != 0; +#endif // LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) } int f_proc_startupdate(struct event_filler_arguments *args) diff --git a/test/drivers/test_suites/syscall_exit_suite/execve_x.cpp b/test/drivers/test_suites/syscall_exit_suite/execve_x.cpp index 3fd3c62191..a8429d28a4 100644 --- a/test/drivers/test_suites/syscall_exit_suite/execve_x.cpp +++ b/test/drivers/test_suites/syscall_exit_suite/execve_x.cpp @@ -7,6 +7,117 @@ #include +#define CREATE_OVERLAY_FS \ + \ + /* Create temp directories */ \ + char work[] = "/tmp/work.XXXXXX"; \ + char lower[] = "/tmp/lower.XXXXXX"; \ + char upper[] = "/tmp/upper.XXXXXX"; \ + char merge[] = "/tmp/overlay.XXXXXX"; \ + \ + char *workdir = mkdtemp(work); \ + char *lowerdir = mkdtemp(lower); \ + char *upperdir = mkdtemp(upper); \ + char *mergedir = mkdtemp(merge); \ + \ + if(workdir == NULL || lowerdir == NULL || upperdir == NULL || mergedir == NULL) \ + { \ + FAIL() << "Cannot create temporary directories." << std::endl; \ + } \ + \ + /* 1. We create the lower layer file before mounting the overlayfs */ \ + \ + /* Copy local bin/true to lower layer */ \ + int true_fd = open("/bin/true", O_RDONLY); \ + if(true_fd == -1) \ + { \ + FAIL() << "Cannot open /bin/true." << std::endl; \ + } \ + \ + char lower_exe_path[1024]; \ + snprintf(lower_exe_path, 1024, "%s/lowertrue", lowerdir); \ + int lower_exe_fd = open(lower_exe_path, O_WRONLY | O_CREAT, 0777); \ + if(lower_exe_fd < 0) \ + { \ + FAIL() << "Cannot open /tmp/merged/lowertrue." << std::endl; \ + } \ + \ + char buf[1024]; \ + ssize_t bytes_read; \ + while((bytes_read = read(true_fd, buf, sizeof(buf))) > 0) \ + { \ + if(write(lower_exe_fd, buf, bytes_read) != bytes_read) \ + { \ + FAIL() << "Cannot write /tmp/merged/lowertrue." << std::endl; \ + } \ + } \ + \ + if(bytes_read == -1) \ + { \ + FAIL() << "Error copying /bin/true" << std::endl; \ + } \ + \ + if(close(lower_exe_fd) == -1) \ + { \ + FAIL() << "Error closing /tmp/merged/lowertrue" << std::endl; \ + } \ + if(close(true_fd) == -1) \ + { \ + FAIL() << "Error closing /bin/true" << std::endl; \ + } \ + \ + /* 2. We mount the overlayfs */ \ + \ + /* Construct the mount options string */ \ + char mntopts[1024]; \ + snprintf(mntopts, 1024, "lowerdir=%s,upperdir=%s,workdir=%s", lowerdir, upperdir, \ + workdir); /* Mount the overlayfs */ \ + if(mount("overlay", mergedir, "overlay", MS_MGC_VAL, mntopts) != 0) \ + { \ + FAIL() << "Cannot mount overlay." << std::endl; \ + } /* 3. We create a file in the upper layer */ \ + char upper_exe_path[1024]; \ + sprintf(upper_exe_path, "%s/uppertrue", mergedir); \ + int upper_exe_fd = open(upper_exe_path, O_WRONLY | O_CREAT, 0777); \ + if(upper_exe_fd == -1) \ + { \ + FAIL() << "Cannot open /tmp/merged/uppertrue." << std::endl; \ + } \ + true_fd = open("/bin/true", O_RDONLY); \ + if(true_fd == -1) \ + { \ + FAIL() << "Cannot open /bin/true." << std::endl; \ + } \ + while((bytes_read = read(true_fd, buf, sizeof(buf))) > 0) \ + { \ + if(write(upper_exe_fd, buf, bytes_read) != bytes_read) \ + { \ + FAIL() << "Cannot write /tmp/merged/uppertrue." << std::endl; \ + } \ + } \ + if(bytes_read == -1) \ + { \ + FAIL() << "Error copying /bin/true" << std::endl; \ + } \ + if(close(true_fd) == -1) \ + { \ + FAIL() << "Error closing /bin/true" << std::endl; \ + } \ + if(close(upper_exe_fd) == -1) \ + { \ + FAIL() << "Error closing /tmp/merged/uppertrue" << std::endl; \ + } + +#define DESTROY_OVERLAY_FS \ + /* Unmount the overlay file system */ \ + unlink(upper_exe_path); \ + unlink(lower_exe_path); \ + rmdir(upperdir); \ + rmdir(workdir); \ + rmdir(lowerdir); \ + umount2(mergedir, MNT_FORCE); \ + rmdir(mergedir); + TEST(SyscallExit, execveX_failure) { auto evt_test = get_syscall_event_test(__NR_execve, EXIT_EVENT); @@ -442,76 +553,13 @@ TEST(SyscallExit, execveX_not_upperlayer) /*=============================== TRIGGER SYSCALL ===========================*/ - const char lowerdir[] = "/bin"; - const char upperdir[] = "/tmp/upper"; - const char target[] = "/tmp/merged"; - char tmp_template[] = "/tmp/tmpdir.XXXXXX"; - char *mntopts; - - /* Create a temporary directory for the work layer */ - char *workdir = mkdtemp(tmp_template); - - /* Create the overlay mount target directory */ - mkdir(upperdir, 0777); - mkdir(target, 0777); - - /* Construct the mount options string */ - if(asprintf(&mntopts, "lowerdir=%s,upperdir=%s,workdir=%s", lowerdir, upperdir, workdir) == -1){ - FAIL() << "Cannot construct mount options string"; - }; - - /* Mount the overlayfs */ - if (mount("overlay", target, "overlay", MS_MGC_VAL, mntopts) != 0) - { - FAIL() << "Cannot mount overlay." << std::endl; - } - - /* Copy /bin/true to /tmp/merged/uppertrue in the overlay file system */ - char true_path[1024], upper_exe_path[1024]; - sprintf(true_path, "%s/true", lowerdir); - sprintf(upper_exe_path, "%s/uppertrue", target); - - int true_fd = open(true_path, O_RDONLY); - if (true_fd == -1) - { - FAIL() << "Cannot open /bin/true." << std::endl; - } - - int upper_exe_fd = open(upper_exe_path, O_WRONLY|O_CREAT, 0777); - if (upper_exe_fd == -1) - { - FAIL() << "Cannot open /tmp/merged/uppertrue." << std::endl; - } - - char buf[1024]; - ssize_t bytes_read; - while ((bytes_read = read(true_fd, buf, sizeof(buf))) > 0) - { - if (write(upper_exe_fd, buf, bytes_read) != bytes_read) - { - FAIL() << "Cannot write /tmp/merged/uppertrue." << std::endl; - } - } - - if (bytes_read == -1) - { - FAIL() << "Error copying /bin/true" << std::endl; - } - - if (close(true_fd) == -1) - { - FAIL() << "Error closing /bin/true" << std::endl; - } - - if (close(upper_exe_fd) == -1) - { - FAIL() << "Error closing /tmp/merged/uppertrue" << std::endl; - } + CREATE_OVERLAY_FS /* Prepare the execve args */ - const char *pathname = true_path; - const char *comm = "true"; - const char *argv[] = {true_path, "randomarg", NULL}; + char merged_exe_path[1024]; + snprintf(merged_exe_path, 1024, "%s/lowertrue", mergedir); + const char *comm = "lowertrue"; + const char *argv[] = {merged_exe_path, "randomarg", NULL}; const char *envp[] = {"IN_TEST=yes", "3_ARGUMENT=yes", "2_ARGUMENT=no", NULL}; /* We need to use `SIGCHLD` otherwise the parent won't receive any signal @@ -526,7 +574,8 @@ TEST(SyscallExit, execveX_not_upperlayer) */ if(ret_pid == 0) { - syscall(__NR_execve, pathname, argv, envp); + syscall(__NR_execve, merged_exe_path, argv, envp); + printf("execve failed: %s\n", strerror(errno)); exit(EXIT_FAILURE); } @@ -546,16 +595,7 @@ TEST(SyscallExit, execveX_not_upperlayer) evt_test->disable_capture(); - /* Unmount the overlay file system */ - if (umount(target)) - { - FAIL() << "Cannot unmount target dir." << std::endl; - } - - /* Remove the upper and work directories */ - rmdir(upperdir); - rmdir(workdir); - rmdir(target); + DESTROY_OVERLAY_FS /* We search for a child event. */ evt_test->assert_event_presence(ret_pid); @@ -577,7 +617,7 @@ TEST(SyscallExit, execveX_not_upperlayer) evt_test->assert_numeric_param(1, (int64_t)0); /* Parameter 2: exe (type: PT_CHARBUF) */ - evt_test->assert_charbuf_param(2, pathname); + evt_test->assert_charbuf_param(2, merged_exe_path); /* Parameter 3: args (type: PT_CHARBUFARRAY) */ /* Starting from `1` because the first is `exe`. */ @@ -624,7 +664,7 @@ TEST(SyscallExit, execveX_not_upperlayer) evt_test->assert_numeric_param(27, (uint32_t)geteuid(), EQUAL); /* Parameter 28: trusted_exepath (type: PT_FSPATH) */ - evt_test->assert_charbuf_param(28, "/usr/bin/true"); + evt_test->assert_charbuf_param(28, merged_exe_path); /*=============================== ASSERT PARAMETERS ===========================*/ @@ -639,71 +679,7 @@ TEST(SyscallExit, execveX_upperlayer_success) /*=============================== TRIGGER SYSCALL ===========================*/ - const char lowerdir[] = "/bin"; - const char upperdir[] = "/tmp/upper"; - const char target[] = "/tmp/merged"; - char tmp_template[] = "/tmp/tmpdir.XXXXXX"; - char *mntopts; - - /* Create a temporary directory for the work layer */ - char *workdir = mkdtemp(tmp_template); - - /* Create the overlay mount target directory */ - mkdir(upperdir, 0777); - mkdir(target, 0777); - - /* Construct the mount options string */ - if(asprintf(&mntopts, "lowerdir=%s,upperdir=%s,workdir=%s", lowerdir, upperdir, workdir) == -1){ - FAIL() << "Cannot construct mount options string"; - }; - - /* Mount the overlayfs */ - if (mount("overlay", target, "overlay", MS_MGC_VAL, mntopts) != 0) - { - FAIL() << "Cannot mount overlay." << std::endl; - } - - /* Copy /bin/true to /tmp/merged/uppertrue in the overlay file system */ - char true_path[1024], upper_exe_path[1024]; - sprintf(true_path, "%s/true", lowerdir); - sprintf(upper_exe_path, "%s/uppertrue", target); - - int true_fd = open(true_path, O_RDONLY); - if (true_fd == -1) - { - FAIL() << "Cannot open /bin/true." << std::endl; - } - - int upper_exe_fd = open(upper_exe_path, O_WRONLY|O_CREAT, 0777); - if (upper_exe_fd == -1) - { - FAIL() << "Cannot open /tmp/merged/uppertrue." << std::endl; - } - - char buf[1024]; - ssize_t bytes_read; - while ((bytes_read = read(true_fd, buf, sizeof(buf))) > 0) - { - if (write(upper_exe_fd, buf, bytes_read) != bytes_read) - { - FAIL() << "Cannot write /tmp/merged/uppertrue." << std::endl; - } - } - - if (bytes_read == -1) - { - FAIL() << "Error copying /bin/true" << std::endl; - } - - if (close(true_fd) == -1) - { - FAIL() << "Error closing /bin/true" << std::endl; - } - - if (close(upper_exe_fd) == -1) - { - FAIL() << "Error closing /tmp/merged/uppertrue" << std::endl; - } + CREATE_OVERLAY_FS /* Prepare the execve args */ const char *pathname = upper_exe_path; @@ -724,6 +700,7 @@ TEST(SyscallExit, execveX_upperlayer_success) if(ret_pid == 0) { syscall(__NR_execve, pathname, argv, envp); + printf("execve failed: %s\n", strerror(errno)); exit(EXIT_FAILURE); } @@ -743,16 +720,7 @@ TEST(SyscallExit, execveX_upperlayer_success) evt_test->disable_capture(); - /* Unmount the overlay file system */ - if (umount(target)) - { - FAIL() << "Cannot unmount target dir." << std::endl; - } - - /* Remove the upper and work directories */ - rmdir(upperdir); - rmdir(workdir); - rmdir(target); + DESTROY_OVERLAY_FS /* We search for a child event. */ evt_test->assert_event_presence(ret_pid); @@ -821,7 +789,7 @@ TEST(SyscallExit, execveX_upperlayer_success) evt_test->assert_numeric_param(27, (uint32_t)geteuid(), EQUAL); /* Parameter 28: trusted_exepath (type: PT_FSPATH) */ - evt_test->assert_charbuf_param(28, "/tmp/merged/uppertrue"); + evt_test->assert_charbuf_param(28, upper_exe_path); /*=============================== ASSERT PARAMETERS ===========================*/ diff --git a/test/vm/containers/modern-falco-builder.Dockerfile b/test/vm/containers/modern-falco-builder.Dockerfile index 0acdda2f0c..cded8cc3fe 100644 --- a/test/vm/containers/modern-falco-builder.Dockerfile +++ b/test/vm/containers/modern-falco-builder.Dockerfile @@ -1,7 +1,20 @@ FROM centos:7 -RUN yum -y install centos-release-scl; \ - yum -y install devtoolset-9-gcc \ +# fix broken mirrors +RUN sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo; \ + sed -i s/^#.*baseurl=http/baseurl=https/g /etc/yum.repos.d/*.repo; \ + sed -i s/^mirrorlist=http/#mirrorlist=https/g /etc/yum.repos.d/*.repo + +RUN yum -y install centos-release-scl + +# fix broken mirrors (again) +RUN sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo; \ + sed -i s/^#.*baseurl=http/baseurl=https/g /etc/yum.repos.d/*.repo; \ + sed -i s/^mirrorlist=http/#mirrorlist=https/g /etc/yum.repos.d/*.repo + +RUN [ $(uname -m) == 'aarch64' ] && sed -i 's/vault.centos.org\/centos/vault.centos.org\/altarch/g' /etc/yum.repos.d/CentOS-SCLo-scl*.repo + +RUN yum -y install devtoolset-9-gcc \ devtoolset-9-gcc-c++; \ source scl_source enable devtoolset-9; \ yum -y install git \ diff --git a/userspace/libscap/engine/bpf/scap_bpf.c b/userspace/libscap/engine/bpf/scap_bpf.c index c0638efd9c..29430a382f 100644 --- a/userspace/libscap/engine/bpf/scap_bpf.c +++ b/userspace/libscap/engine/bpf/scap_bpf.c @@ -1384,6 +1384,15 @@ int32_t scap_bpf_close(struct scap_engine_handle engine) handle->program_fd = -1; } + for(int i = 0; i < BPF_MAPS_MAX; i++) + { + if(handle->m_bpf_map_fds[i] >= 0) + { + close(handle->m_bpf_map_fds[i]); + handle->m_bpf_map_fds[i] = -1; + } + } + return SCAP_SUCCESS; } diff --git a/userspace/libsinsp/container.cpp b/userspace/libsinsp/container.cpp index b28855eddd..1ea31a1a78 100644 --- a/userspace/libsinsp/container.cpp +++ b/userspace/libsinsp/container.cpp @@ -20,6 +20,7 @@ limitations under the License. #if !defined(MINIMAL_BUILD) && !defined(__EMSCRIPTEN__) #include +#include #ifndef _WIN32 #include #include @@ -49,6 +50,14 @@ sinsp_container_manager::sinsp_container_manager(sinsp* inspector, bool static_c m_static_image(static_image), m_container_engine_mask(~0ULL) { + if (m_inspector != nullptr) + { + m_sinsp_stats_v2 = m_inspector->get_sinsp_stats_v2(); + } + else + { + m_sinsp_stats_v2 = nullptr; + } } bool sinsp_container_manager::remove_inactive_containers() @@ -82,22 +91,22 @@ bool sinsp_container_manager::remove_inactive_containers() }); auto containers = m_containers.lock(); - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2 != nullptr) { - m_inspector->get_sinsp_stats_v2()->m_n_missing_container_images = 0; + m_sinsp_stats_v2->m_n_missing_container_images = 0; // Will include pod sanboxes, but that's ok - m_inspector->get_sinsp_stats_v2()->m_n_containers = containers->size(); + m_sinsp_stats_v2->m_n_containers = containers->size(); } for(auto it = containers->begin(); it != containers->end();) { sinsp_container_info::ptr_t container = it->second; - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2) { auto container_info = container.get(); if (!container_info || (container_info && !container_info->m_is_pod_sandbox && container_info->m_image.empty())) { // Only count missing container images and exclude sandboxes - m_inspector->get_sinsp_stats_v2()->m_n_missing_container_images++; + m_sinsp_stats_v2->m_n_missing_container_images++; } } if(containers_in_use.find(it->first) == containers_in_use.end()) diff --git a/userspace/libsinsp/container.h b/userspace/libsinsp/container.h index 2bc3fb2344..1301acc79b 100644 --- a/userspace/libsinsp/container.h +++ b/userspace/libsinsp/container.h @@ -242,6 +242,7 @@ class sinsp_container_manager : std::map> m_container_engine_by_type; sinsp* m_inspector; + std::shared_ptr m_sinsp_stats_v2; libsinsp::Mutex>> m_containers; std::unordered_map> m_lookups; std::list m_new_callbacks; diff --git a/userspace/libsinsp/event.cpp b/userspace/libsinsp/event.cpp index dbb1e4d44b..82d221e756 100644 --- a/userspace/libsinsp/event.cpp +++ b/userspace/libsinsp/event.cpp @@ -39,6 +39,7 @@ limitations under the License. #include #include +#include extern sinsp_evttables g_infotables; @@ -2174,10 +2175,14 @@ void sinsp_evt_param::throw_invalid_len_error(size_t requested_length) const std::stringstream ss; ss << "could not parse param " << m_idx << " (" << parinfo->name << ") for event " - << m_evt->get_num() << " of type " << m_evt->get_type() << " (" << m_evt->get_name() << "): expected length " - << requested_length << ", found " << m_len; + << m_evt->get_num() << " of type " << m_evt->get_type() << " (" << m_evt->get_name() << "), for tid " << m_evt->get_tid() + << ": expected length " << requested_length << ", found " << m_len; + std::string error_string = ss.str(); - throw sinsp_exception(ss.str()); + libsinsp_logger()->log(error_string, sinsp_logger::SEV_ERROR); + libsinsp_logger()->log("parameter raw data: \n" + buffer_to_multiline_hex(m_val, m_len), sinsp_logger::SEV_ERROR); + + throw sinsp_exception(error_string); } const ppm_param_info* sinsp_evt_param::get_info() const diff --git a/userspace/libsinsp/fdinfo.cpp b/userspace/libsinsp/fdinfo.cpp index 39448d3acc..6f069eb4dd 100644 --- a/userspace/libsinsp/fdinfo.cpp +++ b/userspace/libsinsp/fdinfo.cpp @@ -282,6 +282,14 @@ sinsp_fdtable::sinsp_fdtable(sinsp* inspector) { m_tid = 0; m_inspector = inspector; + if (m_inspector != nullptr) + { + m_sinsp_stats_v2 = m_inspector->get_sinsp_stats_v2(); + } + else + { + m_sinsp_stats_v2 = nullptr; + } reset_cache(); } @@ -292,9 +300,9 @@ inline std::shared_ptr sinsp_fdtable::find_ref(int64_t fd) // if(m_last_accessed_fd != -1 && fd == m_last_accessed_fd) { - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2) { - m_inspector->get_sinsp_stats_v2()->m_n_cached_fd_lookups++; + m_sinsp_stats_v2->m_n_cached_fd_lookups++; } return m_last_accessed_fdinfo; } @@ -306,17 +314,17 @@ inline std::shared_ptr sinsp_fdtable::find_ref(int64_t fd) if(fdit == m_table.end()) { - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2) { - m_inspector->get_sinsp_stats_v2()->m_n_failed_fd_lookups++; + m_sinsp_stats_v2->m_n_failed_fd_lookups++; } - return NULL; + return nullptr; } else { - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2 != nullptr) { - m_inspector->get_sinsp_stats_v2()->m_n_noncached_fd_lookups++; + m_sinsp_stats_v2->m_n_noncached_fd_lookups++; } m_last_accessed_fd = fd; @@ -353,9 +361,9 @@ inline std::shared_ptr sinsp_fdtable::add_ref(int64_t fd, std::uni // No entry in the table, this is the normal case // m_last_accessed_fd = -1; - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2 != nullptr) { - m_inspector->get_sinsp_stats_v2()->m_n_added_fds++; + m_sinsp_stats_v2->m_n_added_fds++; } return m_table.emplace(fd, std::move(fdinfo)).first->second; @@ -424,19 +432,19 @@ bool sinsp_fdtable::erase(int64_t fd) // call that created this fd. The assertion will detect it, while in release mode we just // keep going. // - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2 != nullptr) { - m_inspector->get_sinsp_stats_v2()->m_n_failed_fd_lookups++; + m_sinsp_stats_v2->m_n_failed_fd_lookups++; } return false; } else { m_table.erase(fdit); - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2 != nullptr) { - m_inspector->get_sinsp_stats_v2()->m_n_noncached_fd_lookups++; - m_inspector->get_sinsp_stats_v2()->m_n_removed_fds++; + m_sinsp_stats_v2->m_n_noncached_fd_lookups++; + m_sinsp_stats_v2->m_n_removed_fds++; } return true; } diff --git a/userspace/libsinsp/fdinfo.h b/userspace/libsinsp/fdinfo.h index 7890916420..b3c42e52e5 100644 --- a/userspace/libsinsp/fdinfo.h +++ b/userspace/libsinsp/fdinfo.h @@ -438,6 +438,9 @@ class SINSP_PUBLIC sinsp_fdinfo : public libsinsp::state::table_entry /*@}*/ +// Forward declare sinsp_stats_v2 to avoid including metrics_collector.h here. +struct sinsp_stats_v2; + /////////////////////////////////////////////////////////////////////////////// // fd info table /////////////////////////////////////////////////////////////////////////////// @@ -547,6 +550,7 @@ class sinsp_fdtable : public libsinsp::state::table private: sinsp* m_inspector; std::unordered_map> m_table; + std::shared_ptr m_sinsp_stats_v2; // // Simple fd cache diff --git a/userspace/libsinsp/filter/ast.h b/userspace/libsinsp/filter/ast.h index 64f69e61aa..c0d2fad733 100644 --- a/userspace/libsinsp/filter/ast.h +++ b/userspace/libsinsp/filter/ast.h @@ -493,7 +493,7 @@ struct SINSP_PUBLIC unary_check_expr: expr bool is_equal(const expr* other) const override { auto o = dynamic_cast(other); - return o != nullptr && left->is_equal(o->left.get()); + return o != nullptr && left->is_equal(o->left.get()) && op == o->op; } std::unique_ptr left; diff --git a/userspace/libsinsp/filter_compare.cpp b/userspace/libsinsp/filter_compare.cpp index d409db0617..775974b7dc 100644 --- a/userspace/libsinsp/filter_compare.cpp +++ b/userspace/libsinsp/filter_compare.cpp @@ -650,25 +650,25 @@ bool flt_compare(cmpop op, ppm_param_type type, const void* operand1, const void case PT_IPV4ADDR: if (op2_len != sizeof(struct in_addr)) { - return false; + return op == CO_NE; } return flt_compare_ipv4addr(op, flt_cast(operand1), flt_cast(operand2)); case PT_IPV4NET: if (op2_len != sizeof(ipv4net)) { - return false; + return op == CO_NE; } return flt_compare_ipv4net(op, (uint64_t)*(uint32_t*)operand1, (ipv4net*)operand2); case PT_IPV6ADDR: if (op2_len != sizeof(ipv6addr)) { - return false; + return op == CO_NE; } return flt_compare_ipv6addr(op, (ipv6addr *)operand1, (ipv6addr *)operand2); case PT_IPV6NET: if (op2_len != sizeof(ipv6net)) { - return false; + return op == CO_NE; } return flt_compare_ipv6net(op, (ipv6addr *)operand1, (ipv6net*)operand2); case PT_IPADDR: @@ -676,7 +676,7 @@ bool flt_compare(cmpop op, ppm_param_type type, const void* operand1, const void { if (op2_len != sizeof(struct in_addr)) { - return false; + return op == CO_NE; } return flt_compare(op, PT_IPV4ADDR, operand1, operand2, op1_len, op2_len); } @@ -684,7 +684,7 @@ bool flt_compare(cmpop op, ppm_param_type type, const void* operand1, const void { if (op2_len != sizeof(ipv6addr)) { - return false; + return op == CO_NE; } return flt_compare(op, PT_IPV6ADDR, operand1, operand2, op1_len, op2_len); } @@ -697,7 +697,7 @@ bool flt_compare(cmpop op, ppm_param_type type, const void* operand1, const void { if (op2_len != sizeof(ipv4net)) { - return false; + return op == CO_NE; } return flt_compare(op, PT_IPV4NET, operand1, operand2, op1_len, op2_len); } @@ -705,7 +705,7 @@ bool flt_compare(cmpop op, ppm_param_type type, const void* operand1, const void { if (op2_len != sizeof(ipv6net)) { - return false; + return op == CO_NE; } return flt_compare(op, PT_IPV6NET, operand1, operand2, op1_len, op2_len); } diff --git a/userspace/libsinsp/metrics_collector.cpp b/userspace/libsinsp/metrics_collector.cpp index a3fdff1b4c..a9a5beff24 100644 --- a/userspace/libsinsp/metrics_collector.cpp +++ b/userspace/libsinsp/metrics_collector.cpp @@ -449,6 +449,13 @@ uint64_t libs_metrics_collector::get_container_memory_used() const return memory_used; } +// Small helper to be used by sinsp_stats_v2_collectors as +// empty lambda return value. +static metrics_v2 null_metric() +{ + return metrics_v2{}; +} + void libs_metrics_collector::snapshot() { m_metrics.clear(); @@ -485,7 +492,6 @@ void libs_metrics_collector::snapshot() // METRICS_V2_STATE_COUNTERS related uint64_t n_fds = 0; uint64_t n_threads = 0; - std::shared_ptr sinsp_stats_v2 = m_inspector->get_sinsp_stats_v2(); const std::function sinsp_stats_v2_collectors[] = { [SINSP_RESOURCE_UTILIZATION_CPU_PERC] = [this,&cpu_usage_perc]() { @@ -576,151 +582,176 @@ void libs_metrics_collector::snapshot() METRIC_VALUE_METRIC_TYPE_NON_MONOTONIC_CURRENT, n_fds); }, - [SINSP_STATS_V2_NONCACHED_FD_LOOKUPS] = [this,&sinsp_stats_v2]() { - return new_metric("n_noncached_fd_lookups", - METRICS_V2_STATE_COUNTERS, - METRIC_VALUE_TYPE_U64, - METRIC_VALUE_UNIT_COUNT, - METRIC_VALUE_METRIC_TYPE_MONOTONIC, - sinsp_stats_v2->m_n_noncached_fd_lookups); + [SINSP_STATS_V2_NONCACHED_FD_LOOKUPS] = [this]() { + if (m_sinsp_stats_v2) + { + return new_metric("n_noncached_fd_lookups", METRICS_V2_STATE_COUNTERS, + METRIC_VALUE_TYPE_U64, METRIC_VALUE_UNIT_COUNT, + METRIC_VALUE_METRIC_TYPE_MONOTONIC, + m_sinsp_stats_v2->m_n_noncached_fd_lookups); + } + return null_metric(); }, - [SINSP_STATS_V2_CACHED_FD_LOOKUPS] = [this,&sinsp_stats_v2]() { - return new_metric("n_cached_fd_lookups", - METRICS_V2_STATE_COUNTERS, - METRIC_VALUE_TYPE_U64, - METRIC_VALUE_UNIT_COUNT, - METRIC_VALUE_METRIC_TYPE_MONOTONIC, - sinsp_stats_v2->m_n_cached_fd_lookups); + [SINSP_STATS_V2_CACHED_FD_LOOKUPS] = [this]() { + if (m_sinsp_stats_v2) + { + return new_metric("n_cached_fd_lookups", METRICS_V2_STATE_COUNTERS, + METRIC_VALUE_TYPE_U64, METRIC_VALUE_UNIT_COUNT, + METRIC_VALUE_METRIC_TYPE_MONOTONIC, + m_sinsp_stats_v2->m_n_cached_fd_lookups); + } + return null_metric(); }, - [SINSP_STATS_V2_FAILED_FD_LOOKUPS] = [this,&sinsp_stats_v2]() { - return new_metric("n_failed_fd_lookups", - METRICS_V2_STATE_COUNTERS, - METRIC_VALUE_TYPE_U64, - METRIC_VALUE_UNIT_COUNT, - METRIC_VALUE_METRIC_TYPE_MONOTONIC, - sinsp_stats_v2->m_n_failed_fd_lookups); + [SINSP_STATS_V2_FAILED_FD_LOOKUPS] = [this]() { + if (m_sinsp_stats_v2) + { + return new_metric("n_failed_fd_lookups", METRICS_V2_STATE_COUNTERS, + METRIC_VALUE_TYPE_U64, METRIC_VALUE_UNIT_COUNT, + METRIC_VALUE_METRIC_TYPE_MONOTONIC, + m_sinsp_stats_v2->m_n_failed_fd_lookups); + } + return null_metric(); }, - [SINSP_STATS_V2_ADDED_FDS] = [this,&sinsp_stats_v2]() { - return new_metric("n_added_fds", - METRICS_V2_STATE_COUNTERS, - METRIC_VALUE_TYPE_U64, - METRIC_VALUE_UNIT_COUNT, - METRIC_VALUE_METRIC_TYPE_MONOTONIC, - sinsp_stats_v2->m_n_added_fds); + [SINSP_STATS_V2_ADDED_FDS] = [this]() { + if (m_sinsp_stats_v2) + { + return new_metric("n_added_fds", METRICS_V2_STATE_COUNTERS, METRIC_VALUE_TYPE_U64, + METRIC_VALUE_UNIT_COUNT, METRIC_VALUE_METRIC_TYPE_MONOTONIC, + m_sinsp_stats_v2->m_n_added_fds); + } + return null_metric(); }, - [SINSP_STATS_V2_REMOVED_FDS] = [this,&sinsp_stats_v2]() { - return new_metric("n_removed_fds", - METRICS_V2_STATE_COUNTERS, - METRIC_VALUE_TYPE_U64, - METRIC_VALUE_UNIT_COUNT, - METRIC_VALUE_METRIC_TYPE_MONOTONIC, - sinsp_stats_v2->m_n_removed_fds); + [SINSP_STATS_V2_REMOVED_FDS] = [this]() { + if (m_sinsp_stats_v2) + { + return new_metric("n_removed_fds", METRICS_V2_STATE_COUNTERS, METRIC_VALUE_TYPE_U64, + METRIC_VALUE_UNIT_COUNT, METRIC_VALUE_METRIC_TYPE_MONOTONIC, + m_sinsp_stats_v2->m_n_removed_fds); + } + return null_metric(); }, - [SINSP_STATS_V2_STORED_EVTS] = [this,&sinsp_stats_v2]() { - return new_metric("n_stored_evts", - METRICS_V2_STATE_COUNTERS, - METRIC_VALUE_TYPE_U64, - METRIC_VALUE_UNIT_COUNT, - METRIC_VALUE_METRIC_TYPE_MONOTONIC, - sinsp_stats_v2->m_n_stored_evts); + [SINSP_STATS_V2_STORED_EVTS] = [this]() { + if (m_sinsp_stats_v2) + { + return new_metric("n_stored_evts", METRICS_V2_STATE_COUNTERS, METRIC_VALUE_TYPE_U64, + METRIC_VALUE_UNIT_COUNT, METRIC_VALUE_METRIC_TYPE_MONOTONIC, + m_sinsp_stats_v2->m_n_stored_evts); + } + return null_metric(); }, - [SINSP_STATS_V2_STORE_EVTS_DROPS] = [this,&sinsp_stats_v2]() { - return new_metric("n_store_evts_drops", - METRICS_V2_STATE_COUNTERS, - METRIC_VALUE_TYPE_U64, - METRIC_VALUE_UNIT_COUNT, - METRIC_VALUE_METRIC_TYPE_MONOTONIC, - sinsp_stats_v2->m_n_store_evts_drops); + [SINSP_STATS_V2_STORE_EVTS_DROPS] = [this]() { + if (m_sinsp_stats_v2) + { + return new_metric("n_store_evts_drops", METRICS_V2_STATE_COUNTERS, + METRIC_VALUE_TYPE_U64, METRIC_VALUE_UNIT_COUNT, + METRIC_VALUE_METRIC_TYPE_MONOTONIC, + m_sinsp_stats_v2->m_n_store_evts_drops); + } + return null_metric(); }, - [SINSP_STATS_V2_RETRIEVED_EVTS] = [this,&sinsp_stats_v2]() { - return new_metric("n_retrieved_evts", - METRICS_V2_STATE_COUNTERS, - METRIC_VALUE_TYPE_U64, - METRIC_VALUE_UNIT_COUNT, - METRIC_VALUE_METRIC_TYPE_MONOTONIC, - sinsp_stats_v2->m_n_retrieved_evts); + [SINSP_STATS_V2_RETRIEVED_EVTS] = [this]() { + if (m_sinsp_stats_v2) + { + return new_metric("n_retrieved_evts", METRICS_V2_STATE_COUNTERS, METRIC_VALUE_TYPE_U64, + METRIC_VALUE_UNIT_COUNT, METRIC_VALUE_METRIC_TYPE_MONOTONIC, + m_sinsp_stats_v2->m_n_retrieved_evts); + } + return null_metric(); }, - [SINSP_STATS_V2_RETRIEVE_EVTS_DROPS] = [this,&sinsp_stats_v2]() { - return new_metric("n_retrieve_evts_drops", - METRICS_V2_STATE_COUNTERS, - METRIC_VALUE_TYPE_U64, - METRIC_VALUE_UNIT_COUNT, - METRIC_VALUE_METRIC_TYPE_MONOTONIC, - sinsp_stats_v2->m_n_retrieve_evts_drops); + [SINSP_STATS_V2_RETRIEVE_EVTS_DROPS] = [this]() { + if (m_sinsp_stats_v2) + { + return new_metric("n_retrieve_evts_drops", METRICS_V2_STATE_COUNTERS, + METRIC_VALUE_TYPE_U64, METRIC_VALUE_UNIT_COUNT, + METRIC_VALUE_METRIC_TYPE_MONOTONIC, + m_sinsp_stats_v2->m_n_retrieve_evts_drops); + } + return null_metric(); }, - [SINSP_STATS_V2_NONCACHED_THREAD_LOOKUPS] = [this,&sinsp_stats_v2]() { - return new_metric("n_noncached_thread_lookups", - METRICS_V2_STATE_COUNTERS, - METRIC_VALUE_TYPE_U64, - METRIC_VALUE_UNIT_COUNT, - METRIC_VALUE_METRIC_TYPE_MONOTONIC, - sinsp_stats_v2->m_n_noncached_thread_lookups); + [SINSP_STATS_V2_NONCACHED_THREAD_LOOKUPS] = [this]() { + if (m_sinsp_stats_v2) + { + return new_metric("n_noncached_thread_lookups", METRICS_V2_STATE_COUNTERS, + METRIC_VALUE_TYPE_U64, METRIC_VALUE_UNIT_COUNT, + METRIC_VALUE_METRIC_TYPE_MONOTONIC, + m_sinsp_stats_v2->m_n_noncached_thread_lookups); + } + return null_metric(); }, - [SINSP_STATS_V2_CACHED_THREAD_LOOKUPS] = [this,&sinsp_stats_v2]() { - return new_metric("n_cached_thread_lookups", - METRICS_V2_STATE_COUNTERS, - METRIC_VALUE_TYPE_U64, - METRIC_VALUE_UNIT_COUNT, - METRIC_VALUE_METRIC_TYPE_MONOTONIC, - sinsp_stats_v2->m_n_cached_thread_lookups); + [SINSP_STATS_V2_CACHED_THREAD_LOOKUPS] = [this]() { + if (m_sinsp_stats_v2) + { + return new_metric("n_cached_thread_lookups", METRICS_V2_STATE_COUNTERS, + METRIC_VALUE_TYPE_U64, METRIC_VALUE_UNIT_COUNT, + METRIC_VALUE_METRIC_TYPE_MONOTONIC, + m_sinsp_stats_v2->m_n_cached_thread_lookups); + } + return null_metric(); }, - [SINSP_STATS_V2_FAILED_THREAD_LOOKUPS] = [this,&sinsp_stats_v2]() { - return new_metric("n_failed_thread_lookups", - METRICS_V2_STATE_COUNTERS, - METRIC_VALUE_TYPE_U64, - METRIC_VALUE_UNIT_COUNT, - METRIC_VALUE_METRIC_TYPE_MONOTONIC, - sinsp_stats_v2->m_n_failed_thread_lookups); + [SINSP_STATS_V2_FAILED_THREAD_LOOKUPS] = [this]() { + if (m_sinsp_stats_v2) + { + return new_metric("n_failed_thread_lookups", METRICS_V2_STATE_COUNTERS, + METRIC_VALUE_TYPE_U64, METRIC_VALUE_UNIT_COUNT, + METRIC_VALUE_METRIC_TYPE_MONOTONIC, + m_sinsp_stats_v2->m_n_failed_thread_lookups); + } + return null_metric(); }, - [SINSP_STATS_V2_ADDED_THREADS] = [this,&sinsp_stats_v2]() { - return new_metric("n_added_threads", - METRICS_V2_STATE_COUNTERS, - METRIC_VALUE_TYPE_U64, - METRIC_VALUE_UNIT_COUNT, - METRIC_VALUE_METRIC_TYPE_MONOTONIC, - sinsp_stats_v2->m_n_added_threads); + [SINSP_STATS_V2_ADDED_THREADS] = [this]() { + if (m_sinsp_stats_v2) + { + return new_metric("n_added_threads", METRICS_V2_STATE_COUNTERS, METRIC_VALUE_TYPE_U64, + METRIC_VALUE_UNIT_COUNT, METRIC_VALUE_METRIC_TYPE_MONOTONIC, + m_sinsp_stats_v2->m_n_added_threads); + } + return null_metric(); }, - [SINSP_STATS_V2_REMOVED_THREADS] = [this,&sinsp_stats_v2]() { - return new_metric("n_removed_threads", - METRICS_V2_STATE_COUNTERS, - METRIC_VALUE_TYPE_U64, - METRIC_VALUE_UNIT_COUNT, - METRIC_VALUE_METRIC_TYPE_MONOTONIC, - sinsp_stats_v2->m_n_removed_threads); + [SINSP_STATS_V2_REMOVED_THREADS] = [this]() { + if (m_sinsp_stats_v2) + { + return new_metric("n_removed_threads", METRICS_V2_STATE_COUNTERS, METRIC_VALUE_TYPE_U64, + METRIC_VALUE_UNIT_COUNT, METRIC_VALUE_METRIC_TYPE_MONOTONIC, + m_sinsp_stats_v2->m_n_removed_threads); + } + return null_metric(); }, - [SINSP_STATS_V2_N_DROPS_FULL_THREADTABLE] = [this,&sinsp_stats_v2]() { - return new_metric("n_drops_full_threadtable", - METRICS_V2_STATE_COUNTERS, - METRIC_VALUE_TYPE_U32, - METRIC_VALUE_UNIT_COUNT, - METRIC_VALUE_METRIC_TYPE_MONOTONIC, - sinsp_stats_v2->m_n_drops_full_threadtable); + [SINSP_STATS_V2_N_DROPS_FULL_THREADTABLE] = [this]() { + if (m_sinsp_stats_v2) + { + return new_metric("n_drops_full_threadtable", METRICS_V2_STATE_COUNTERS, + METRIC_VALUE_TYPE_U32, METRIC_VALUE_UNIT_COUNT, + METRIC_VALUE_METRIC_TYPE_MONOTONIC, + m_sinsp_stats_v2->m_n_drops_full_threadtable); + } + return null_metric(); }, - [SINSP_STATS_V2_N_MISSING_CONTAINER_IMAGES] = [this,&sinsp_stats_v2]() { - return new_metric("n_missing_container_images", - METRICS_V2_STATE_COUNTERS, - METRIC_VALUE_TYPE_U32, - METRIC_VALUE_UNIT_COUNT, - METRIC_VALUE_METRIC_TYPE_NON_MONOTONIC_CURRENT, - sinsp_stats_v2->m_n_missing_container_images); + [SINSP_STATS_V2_N_MISSING_CONTAINER_IMAGES] = [this]() { + if (m_sinsp_stats_v2) + { + return new_metric("n_missing_container_images", METRICS_V2_STATE_COUNTERS, + METRIC_VALUE_TYPE_U32, METRIC_VALUE_UNIT_COUNT, + METRIC_VALUE_METRIC_TYPE_NON_MONOTONIC_CURRENT, + m_sinsp_stats_v2->m_n_missing_container_images); + } + return null_metric(); }, - [SINSP_STATS_V2_N_CONTAINERS] = [this,&sinsp_stats_v2]() { - return new_metric("n_containers", - METRICS_V2_STATE_COUNTERS, - METRIC_VALUE_TYPE_U32, - METRIC_VALUE_UNIT_COUNT, - METRIC_VALUE_METRIC_TYPE_NON_MONOTONIC_CURRENT, - sinsp_stats_v2->m_n_containers); + [SINSP_STATS_V2_N_CONTAINERS] = [this]() { + if (m_sinsp_stats_v2) + { + return new_metric("n_containers", METRICS_V2_STATE_COUNTERS, METRIC_VALUE_TYPE_U32, + METRIC_VALUE_UNIT_COUNT, + METRIC_VALUE_METRIC_TYPE_NON_MONOTONIC_CURRENT, + m_sinsp_stats_v2->m_n_containers); + } + return null_metric(); }, }; - static_assert(sizeof(sinsp_stats_v2_collectors) / sizeof(sinsp_stats_v2_collectors[0]) == SINSP_MAX_STATS_V2, "sinsp_stats_v2_resource_utilization_names array size does not match expected size"); - /* * libsinsp metrics */ - if((m_metrics_flags & METRICS_V2_RESOURCE_UTILIZATION)) { const scap_agent_info* agent_info = m_inspector->get_agent_info(); @@ -730,43 +761,48 @@ void libs_metrics_collector::snapshot() // Resource utilization of the agent itself for (int i = SINSP_RESOURCE_UTILIZATION_CPU_PERC; i <= SINSP_RESOURCE_UTILIZATION_HOST_FDS; i++) { - m_metrics.emplace_back(sinsp_stats_v2_collectors[i]()); + auto metric = sinsp_stats_v2_collectors[i](); + if (metric.name[0] != '\0') + { + // Check that metric is actually initialized, + // ie: it is not referencing m_sinsp_stats_v2 + // when sinsp stats are not enabled. + m_metrics.emplace_back(metric); + } } } if((m_metrics_flags & METRICS_V2_STATE_COUNTERS)) { - if (!sinsp_stats_v2) + auto thread_manager = m_inspector->m_thread_manager.get(); + if (thread_manager) { - m_inspector->set_sinsp_stats_v2_enabled(); - sinsp_stats_v2 = m_inspector->get_sinsp_stats_v2(); - } - - if (sinsp_stats_v2) - { - auto thread_manager = m_inspector->m_thread_manager.get(); - if (thread_manager) + n_threads = thread_manager->get_thread_count(); + threadinfo_map_t* threadtable = thread_manager->get_threads(); + if (threadtable) { - n_threads = thread_manager->get_thread_count(); - threadinfo_map_t* threadtable = thread_manager->get_threads(); - if (threadtable) + threadtable->loop([&n_fds] (sinsp_threadinfo& tinfo) { - threadtable->loop([&n_fds] (sinsp_threadinfo& tinfo) + sinsp_fdtable* fdtable = tinfo.get_fd_table(); + if (fdtable != nullptr) { - sinsp_fdtable* fdtable = tinfo.get_fd_table(); - if (fdtable != nullptr) - { - n_fds += fdtable->size(); - } - return true; - }); - } + n_fds += fdtable->size(); + } + return true; + }); } + } - // Resource utilization of the agent itself - for (int i = SINSP_STATS_V2_N_THREADS; i < SINSP_MAX_STATS_V2; i++) + // Resource utilization of the agent itself + for (int i = SINSP_STATS_V2_N_THREADS; i < SINSP_MAX_STATS_V2; i++) + { + auto metric = sinsp_stats_v2_collectors[i](); + if (metric.name[0] != '\0') { - m_metrics.emplace_back(sinsp_stats_v2_collectors[i]()); + // Check that metric is actually initialized, + // ie: it is not referencing m_sinsp_stats_v2 + // when sinsp stats are not enabled. + m_metrics.emplace_back(metric); } } } @@ -774,7 +810,6 @@ void libs_metrics_collector::snapshot() /* * plugins metrics */ - if(m_metrics_flags & METRICS_V2_PLUGINS) { for (auto& p : m_inspector->get_plugin_manager()->plugins()) @@ -799,6 +834,14 @@ libs_metrics_collector::libs_metrics_collector(sinsp* inspector, uint32_t flags) m_inspector(inspector), m_metrics_flags(flags) { + if (m_inspector != nullptr) + { + m_sinsp_stats_v2 = m_inspector->get_sinsp_stats_v2(); + } + else + { + m_sinsp_stats_v2 = nullptr; + } } } // namespace libs::metrics diff --git a/userspace/libsinsp/metrics_collector.h b/userspace/libsinsp/metrics_collector.h index 147ac0aeef..4f80e947f6 100644 --- a/userspace/libsinsp/metrics_collector.h +++ b/userspace/libsinsp/metrics_collector.h @@ -271,6 +271,7 @@ class libs_metrics_collector private: sinsp* m_inspector; + std::shared_ptr m_sinsp_stats_v2; uint32_t m_metrics_flags = METRICS_V2_KERNEL_COUNTERS | METRICS_V2_LIBBPF_STATS | METRICS_V2_RESOURCE_UTILIZATION | METRICS_V2_STATE_COUNTERS | METRICS_V2_PLUGINS; std::vector m_metrics; diff --git a/userspace/libsinsp/parsers.cpp b/userspace/libsinsp/parsers.cpp index d73cf45149..4e3b00ca43 100644 --- a/userspace/libsinsp/parsers.cpp +++ b/userspace/libsinsp/parsers.cpp @@ -51,7 +51,14 @@ sinsp_parser::sinsp_parser(sinsp *inspector) : m_tmp_evt(m_inspector), m_syscall_event_source_idx(sinsp_no_event_source_idx) { - + if (m_inspector != nullptr) + { + m_sinsp_stats_v2 = m_inspector->get_sinsp_stats_v2(); + } + else + { + m_sinsp_stats_v2 = nullptr; + } } sinsp_parser::~sinsp_parser() @@ -672,9 +679,9 @@ bool sinsp_parser::reset(sinsp_evt *evt) etype == PPME_SYSCALL_VFORK_20_X || etype == PPME_SYSCALL_CLONE3_X) { - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2 != nullptr) { - m_inspector->get_sinsp_stats_v2()->m_n_failed_thread_lookups--; + m_sinsp_stats_v2->m_n_failed_thread_lookups--; } } return false; @@ -823,9 +830,9 @@ void sinsp_parser::store_event(sinsp_evt *evt) // we won't be able to parse the corresponding exit event and we'll have // to drop the information it carries. // - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2 != nullptr) { - m_inspector->get_sinsp_stats_v2()->m_n_store_evts_drops++; + m_sinsp_stats_v2->m_n_store_evts_drops++; } return; } @@ -859,9 +866,9 @@ void sinsp_parser::store_event(sinsp_evt *evt) memcpy(tinfo->get_last_event_data(), evt->get_scap_evt(), elen); tinfo->set_lastevent_cpuid(evt->get_cpuid()); - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2 != nullptr) { - m_inspector->get_sinsp_stats_v2()->m_n_stored_evts++; + m_sinsp_stats_v2->m_n_stored_evts++; } } @@ -884,9 +891,9 @@ bool sinsp_parser::retrieve_enter_event(sinsp_evt *enter_evt, sinsp_evt *exit_ev // This happen especially at the beginning of trace files, where events // can be truncated // - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2 != nullptr) { - m_inspector->get_sinsp_stats_v2()->m_n_retrieve_evts_drops++; + m_sinsp_stats_v2->m_n_retrieve_evts_drops++; } return false; } @@ -907,9 +914,9 @@ bool sinsp_parser::retrieve_enter_event(sinsp_evt *enter_evt, sinsp_evt *exit_ev && enter_evt->get_type() == PPME_SYSCALL_EXECVEAT_E) { - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2 != nullptr) { - m_inspector->get_sinsp_stats_v2()->m_n_retrieved_evts++; + m_sinsp_stats_v2->m_n_retrieved_evts++; } return true; } @@ -922,15 +929,15 @@ bool sinsp_parser::retrieve_enter_event(sinsp_evt *enter_evt, sinsp_evt *exit_ev { //ASSERT(false); exit_evt->get_tinfo()->set_lastevent_data_validity(false); - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2 != nullptr) { - m_inspector->get_sinsp_stats_v2()->m_n_retrieve_evts_drops++; + m_sinsp_stats_v2->m_n_retrieve_evts_drops++; } return false; } - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2 != nullptr) { - m_inspector->get_sinsp_stats_v2()->m_n_retrieved_evts++; + m_sinsp_stats_v2->m_n_retrieved_evts++; } return true; @@ -3609,15 +3616,15 @@ void sinsp_parser::parse_close_exit(sinsp_evt *evt) // It is normal when a close fails that the fd lookup failed, so we revert the // increment of m_n_failed_fd_lookups (for the enter event too if there's one). // - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2 != nullptr) { - m_inspector->get_sinsp_stats_v2()->m_n_failed_fd_lookups--; + m_sinsp_stats_v2->m_n_failed_fd_lookups--; } if(evt->get_tinfo() && evt->get_tinfo()->is_lastevent_data_valid()) { - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2 != nullptr) { - m_inspector->get_sinsp_stats_v2()->m_n_failed_fd_lookups--; + m_sinsp_stats_v2->m_n_failed_fd_lookups--; } } } diff --git a/userspace/libsinsp/parsers.h b/userspace/libsinsp/parsers.h index 07ab25c807..8b143a10ec 100644 --- a/userspace/libsinsp/parsers.h +++ b/userspace/libsinsp/parsers.h @@ -145,6 +145,8 @@ class sinsp_parser // sinsp* m_inspector; + std::shared_ptr m_sinsp_stats_v2; + // // Temporary storage to avoid memory allocation // diff --git a/userspace/libsinsp/prefix_search.h b/userspace/libsinsp/prefix_search.h index cd4e9c186a..322fc1cac6 100644 --- a/userspace/libsinsp/prefix_search.h +++ b/userspace/libsinsp/prefix_search.h @@ -23,6 +23,7 @@ limitations under the License. #include #include #include +#include #include #include diff --git a/userspace/libsinsp/sinsp.cpp b/userspace/libsinsp/sinsp.cpp index 59068466a5..51c6a211cc 100644 --- a/userspace/libsinsp/sinsp.cpp +++ b/userspace/libsinsp/sinsp.cpp @@ -64,8 +64,9 @@ int32_t on_new_entry_from_proc(void* context, char* error, int64_t tid, scap_thr /////////////////////////////////////////////////////////////////////////////// std::atomic sinsp::instance_count{0}; -sinsp::sinsp(bool static_container, const std::string &static_id, const std::string &static_name, const std::string &static_image) : +sinsp::sinsp(bool static_container, const std::string &static_id, const std::string &static_name, const std::string &static_image, bool with_metrics) : m_external_event_processor(), + m_sinsp_stats_v2(with_metrics ? std::make_shared() : nullptr), m_evt(this), m_lastevent_ts(0), m_host_root(scap_get_host_root()), @@ -80,6 +81,7 @@ sinsp::sinsp(bool static_container, const std::string &static_id, const std::str // used by container_manager curl_global_init(CURL_GLOBAL_DEFAULT); #endif + m_h = NULL; m_parser = NULL; m_is_dumping = false; @@ -104,7 +106,6 @@ sinsp::sinsp(bool static_container, const std::string &static_id, const std::str m_next_flush_time_ns = 0; m_last_procrequest_tod = 0; m_get_procs_cpu_from_driver = false; - m_flush_memory_dump = false; m_next_stats_print_time_ns = 0; m_large_envs_enabled = false; m_increased_snaplen_port_range = DEFAULT_INCREASE_SNAPLEN_PORT_RANGE; @@ -1967,31 +1968,6 @@ void sinsp::set_proc_scan_log_interval_ms(uint64_t val) m_proc_scan_log_interval_ms = val; } -void sinsp::set_sinsp_stats_v2_enabled() -{ - if (m_sinsp_stats_v2 == nullptr) - { - m_sinsp_stats_v2 = std::make_unique(); - m_sinsp_stats_v2->m_n_noncached_fd_lookups = 0; - m_sinsp_stats_v2->m_n_cached_fd_lookups = 0; - m_sinsp_stats_v2->m_n_failed_fd_lookups = 0; - m_sinsp_stats_v2->m_n_added_fds = 0; - m_sinsp_stats_v2->m_n_removed_fds = 0; - m_sinsp_stats_v2->m_n_stored_evts = 0; - m_sinsp_stats_v2->m_n_store_evts_drops = 0; - m_sinsp_stats_v2->m_n_retrieved_evts = 0; - m_sinsp_stats_v2->m_n_retrieve_evts_drops = 0; - m_sinsp_stats_v2->m_n_noncached_thread_lookups = 0; - m_sinsp_stats_v2->m_n_cached_thread_lookups = 0; - m_sinsp_stats_v2->m_n_failed_thread_lookups = 0; - m_sinsp_stats_v2->m_n_added_threads = 0; - m_sinsp_stats_v2->m_n_removed_threads = 0; - m_sinsp_stats_v2->m_n_drops_full_threadtable = 0; - m_sinsp_stats_v2->m_n_missing_container_images = 0; - m_sinsp_stats_v2->m_n_containers= 0; - } -} - /////////////////////////////////////////////////////////////////////////////// // Note: this is defined here so we can inline it in sinso::next /////////////////////////////////////////////////////////////////////////////// diff --git a/userspace/libsinsp/sinsp.h b/userspace/libsinsp/sinsp.h index 6e53c199db..de9c394f31 100644 --- a/userspace/libsinsp/sinsp.h +++ b/userspace/libsinsp/sinsp.h @@ -161,7 +161,8 @@ class SINSP_PUBLIC sinsp : public capture_stats_source sinsp(bool static_container = false, const std::string &static_id = "", const std::string &static_name = "", - const std::string &static_image = ""); + const std::string &static_image = "", + bool with_metrics = false); virtual ~sinsp() override; @@ -438,11 +439,6 @@ class SINSP_PUBLIC sinsp : public capture_stats_source */ void set_proc_scan_log_interval_ms(uint64_t val); - /*! - * \brief enabling sinsp state counters on the hot path via initializing the respective smart pointer. - */ - void set_sinsp_stats_v2_enabled(); - /*! \brief Returns a new instance of a filtercheck supporting fields for a generic event source (e.g. evt.num, evt.time, evt.pluginname...) @@ -700,7 +696,7 @@ class SINSP_PUBLIC sinsp : public capture_stats_source /*! \brief Returns true if truncated environments should be loaded from /proc */ - inline bool large_envs_enabled() + inline bool large_envs_enabled() const { return (is_live() || is_syscall_plugin()) && m_large_envs_enabled; } @@ -1158,6 +1154,7 @@ class SINSP_PUBLIC sinsp : public capture_stats_source return left == static_cast(-1) || left <= right; } + std::shared_ptr m_sinsp_stats_v2; scap_t* m_h; struct scap_platform* m_platform {}; char m_platform_lasterr[SCAP_LASTERR_SIZE]; @@ -1188,9 +1185,7 @@ class SINSP_PUBLIC sinsp : public capture_stats_source bool m_is_dumping; const scap_machine_info* m_machine_info; const scap_agent_info* m_agent_info; - std::shared_ptr m_sinsp_stats_v2; uint32_t m_num_cpus; - bool m_flush_memory_dump; bool m_large_envs_enabled; sinsp_network_interfaces m_network_interfaces {}; diff --git a/userspace/libsinsp/sinsp_filter_transformer.cpp b/userspace/libsinsp/sinsp_filter_transformer.cpp index 73d1af59eb..2cb2bdcb76 100644 --- a/userspace/libsinsp/sinsp_filter_transformer.cpp +++ b/userspace/libsinsp/sinsp_filter_transformer.cpp @@ -57,7 +57,7 @@ bool sinsp_filter_transformer::string_transformer(std::vector& } // we insert a null terminator in case we miss one, just to stay safe - if (buf[buf.size() - 1] != '\0') + if (buf.size() == 0 || buf[buf.size() - 1] != '\0') { buf.push_back('\0'); } diff --git a/userspace/libsinsp/test/filter_transformer.ut.cpp b/userspace/libsinsp/test/filter_transformer.ut.cpp index eb102c072d..becd522822 100644 --- a/userspace/libsinsp/test/filter_transformer.ut.cpp +++ b/userspace/libsinsp/test/filter_transformer.ut.cpp @@ -40,6 +40,13 @@ static std::string supported_type_msg(ppm_param_type t, bool support_expected) + std::string(param_type_to_string(t)); } +static std::string eq_test_msg(const std::pair &tc) +{ + return "expected '" + + tc.first + "' (length: " + std::to_string(tc.first.length()) + ")" + + " to be equal to '" + tc.second + "' (length: " + std::to_string(tc.second.length()) + ")"; +} + static extract_value_t const_str_to_extract_value(const char* v) { extract_value_t ret; @@ -57,9 +64,19 @@ TEST(sinsp_filter_transformer, toupper) auto supported_types = std::unordered_set({ PT_CHARBUF, PT_FSPATH, PT_FSRELPATH }); - auto sample_vals = std::vector({ - const_str_to_extract_value("hello"), - const_str_to_extract_value("world") }); + auto test_cases = std::vector>{ + {"hello", "HELLO"}, + {"world", "WORLD"}, + {"eXcItED", "EXCITED"}, + {"", ""}, + }; + + std::vector sample_vals; + + for (auto& tc : test_cases) + { + sample_vals.push_back(const_str_to_extract_value(tc.first.c_str())); + } // check for unsupported types for (auto t : all_types) @@ -82,11 +99,13 @@ TEST(sinsp_filter_transformer, toupper) auto vals = sample_vals; EXPECT_TRUE(tr.transform_values(vals, t)) << supported_type_msg(original, true); EXPECT_EQ(original, t); - EXPECT_EQ(vals.size(), 2); - EXPECT_EQ(std::string((const char*) vals[0].ptr), "HELLO"); - EXPECT_EQ(vals[0].len, sizeof("HELLO")); - EXPECT_EQ(std::string((const char*) vals[1].ptr), "WORLD"); - EXPECT_EQ(vals[1].len, sizeof("WORLD")); + EXPECT_EQ(vals.size(), test_cases.size()); + + for (uint32_t i = 0; i < test_cases.size(); i++) + { + EXPECT_EQ(std::string((const char *)vals[i].ptr), test_cases[i].second) << eq_test_msg(test_cases[i]); + EXPECT_EQ(vals[i].len, test_cases[i].second.length() + 1) << eq_test_msg(test_cases[i]); + } } } @@ -99,9 +118,19 @@ TEST(sinsp_filter_transformer, tolower) auto supported_types = std::unordered_set({ PT_CHARBUF, PT_FSPATH, PT_FSRELPATH }); - auto sample_vals = std::vector({ - const_str_to_extract_value("HELLO"), - const_str_to_extract_value("WORLD") }); + auto test_cases = std::vector>{ + {"HELLO", "hello"}, + {"world", "world"}, + {"NoT_eXcItED", "not_excited"}, + {"", ""}, + }; + + std::vector sample_vals; + + for (auto& tc : test_cases) + { + sample_vals.push_back(const_str_to_extract_value(tc.first.c_str())); + } // check for unsupported types for (auto t : all_types) @@ -124,11 +153,13 @@ TEST(sinsp_filter_transformer, tolower) auto vals = sample_vals; EXPECT_TRUE(tr.transform_values(vals, t)) << supported_type_msg(original, true); EXPECT_EQ(original, t); - EXPECT_EQ(vals.size(), 2); - EXPECT_EQ(std::string((const char*) vals[0].ptr), "hello"); - EXPECT_EQ(vals[0].len, sizeof("hello")); - EXPECT_EQ(std::string((const char*) vals[1].ptr), "world"); - EXPECT_EQ(vals[1].len, sizeof("world")); + EXPECT_EQ(vals.size(), test_cases.size()); + + for (uint32_t i = 0; i < test_cases.size(); i++) + { + EXPECT_EQ(std::string((const char *)vals[i].ptr), test_cases[i].second) << eq_test_msg(test_cases[i]); + EXPECT_EQ(vals[i].len, test_cases[i].second.length() + 1) << eq_test_msg(test_cases[i]); + } } } @@ -141,9 +172,21 @@ TEST(sinsp_filter_transformer, b64) auto supported_types = std::unordered_set({ PT_CHARBUF, PT_BYTEBUF }); - auto sample_vals = std::vector({ - const_str_to_extract_value("aGVsbG8="), // 'hello' - const_str_to_extract_value("d29ybGQgIQ==") }); // 'world !' + auto test_cases = std::vector>{ + {"aGVsbG8=", "hello"}, + {"d29ybGQgIQ==", "world !"}, + {"", ""}, + }; + + std::vector invalid_test_cases { + "!!!" + }; + + std::vector sample_vals; + for (auto& tc : test_cases) + { + sample_vals.push_back(const_str_to_extract_value(tc.first.c_str())); + } // check for unsupported types for (auto t : all_types) @@ -166,21 +209,25 @@ TEST(sinsp_filter_transformer, b64) auto vals = sample_vals; EXPECT_TRUE(tr.transform_values(vals, t)) << supported_type_msg(original, true); EXPECT_EQ(original, t); - EXPECT_EQ(vals.size(), 2); - EXPECT_EQ(std::string((const char*) vals[0].ptr), "hello"); - EXPECT_EQ(vals[0].len, sizeof("hello")); - EXPECT_EQ(std::string((const char*) vals[1].ptr), "world !"); - EXPECT_EQ(vals[1].len, sizeof("world !")); + EXPECT_EQ(vals.size(), test_cases.size()); + + for (uint32_t i = 0; i < test_cases.size(); i++) + { + EXPECT_EQ(std::string((const char *)vals[i].ptr), test_cases[i].second) << eq_test_msg(test_cases[i]); + EXPECT_EQ(vals[i].len, test_cases[i].second.length() + 1) << eq_test_msg(test_cases[i]); + } + } + + std::vector invalid_vals; + for (auto& tc : invalid_test_cases) + { + invalid_vals.push_back(const_str_to_extract_value(tc.c_str())); } // check invalid input being rejected { auto t = PT_CHARBUF; - auto vals = sample_vals; - vals[0].ptr = (uint8_t*) ((const char*) "!!!"); - vals[0].len = sizeof("!!!"); - EXPECT_FALSE(tr.transform_values(vals, t)); + EXPECT_FALSE(tr.transform_values(invalid_vals, t)); EXPECT_EQ(t, PT_CHARBUF); - EXPECT_EQ(vals.size(), 2); } } diff --git a/userspace/libsinsp/test/sinsp_metrics.ut.cpp b/userspace/libsinsp/test/sinsp_metrics.ut.cpp index f3bc8c08fc..23f60d8db9 100644 --- a/userspace/libsinsp/test/sinsp_metrics.ut.cpp +++ b/userspace/libsinsp/test/sinsp_metrics.ut.cpp @@ -23,9 +23,6 @@ limitations under the License. TEST_F(sinsp_with_test_input, sinsp_libs_metrics_collector_prometheus) { - m_inspector.set_sinsp_stats_v2_enabled(); - // Extra call to verify that we don't fail - m_inspector.set_sinsp_stats_v2_enabled(); DEFAULT_TREE auto evt = generate_random_event(p2_t1_tid); ASSERT_EQ(get_field_as_string(evt, "proc.nthreads"), "3"); @@ -249,9 +246,6 @@ testns_falco_host_boot_timestamp_nanoseconds{raw_name="host_boot_ts"} 1708753667 TEST_F(sinsp_with_test_input, sinsp_libs_metrics_collector_output_rule) { - m_inspector.set_sinsp_stats_v2_enabled(); - // Extra call to verify that we don't fail - m_inspector.set_sinsp_stats_v2_enabled(); DEFAULT_TREE auto evt = generate_random_event(p2_t1_tid); ASSERT_EQ(get_field_as_string(evt, "proc.nthreads"), "3"); @@ -317,7 +311,7 @@ TEST_F(sinsp_with_test_input, sinsp_libs_metrics_collector_output_rule) if (std::find(metrics_names_values_gt.begin(), metrics_names_values_gt.end(), metric.name) != metrics_names_values_gt.end()) { ASSERT_GT(metric.value.u64, 0); - // Just making sure we don't get a high value due to an unitialized variables + // Just making sure we don't get a high value due to an uninitialized variables ASSERT_LT(metric.value.u64, 106721347371); success_values_cnt++; } diff --git a/userspace/libsinsp/test/sinsp_with_test_input.h b/userspace/libsinsp/test/sinsp_with_test_input.h index cac743c436..0511a1706a 100644 --- a/userspace/libsinsp/test/sinsp_with_test_input.h +++ b/userspace/libsinsp/test/sinsp_with_test_input.h @@ -40,7 +40,7 @@ class sinsp_with_test_input : public ::testing::Test sinsp_with_test_input(); ~sinsp_with_test_input(); - sinsp m_inspector; + sinsp m_inspector = sinsp(false, "", "", "", true); void open_inspector(sinsp_mode_t mode = SINSP_MODE_TEST); diff --git a/userspace/libsinsp/threadinfo.cpp b/userspace/libsinsp/threadinfo.cpp index 0ec2972b57..a856098d77 100644 --- a/userspace/libsinsp/threadinfo.cpp +++ b/userspace/libsinsp/threadinfo.cpp @@ -1388,6 +1388,14 @@ sinsp_thread_manager::sinsp_thread_manager(sinsp* inspector) m_fdtable_dyn_fields(std::make_shared()) { m_inspector = inspector; + if (m_inspector != nullptr) + { + m_sinsp_stats_v2 = m_inspector->get_sinsp_stats_v2(); + } + else + { + m_sinsp_stats_v2 = nullptr; + } clear(); } @@ -1493,15 +1501,15 @@ std::shared_ptr sinsp_thread_manager::add_thread(std::unique_p && threadinfo->m_pid != m_inspector->m_self_pid ) { - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2 != nullptr) { // rate limit messages to avoid spamming the logs - if (m_inspector->get_sinsp_stats_v2()->m_n_drops_full_threadtable % m_max_thread_table_size == 0) + if (m_sinsp_stats_v2->m_n_drops_full_threadtable % m_max_thread_table_size == 0) { libsinsp_logger()->format(sinsp_logger::SEV_INFO, "Thread table full, dropping tid %lu (pid %lu, comm \"%s\")", threadinfo->m_tid, threadinfo->m_pid, threadinfo->m_comm.c_str()); } - m_inspector->get_sinsp_stats_v2()->m_n_drops_full_threadtable++; + m_sinsp_stats_v2->m_n_drops_full_threadtable++; } return nullptr; } @@ -1526,9 +1534,9 @@ std::shared_ptr sinsp_thread_manager::add_thread(std::unique_p tinfo_shared_ptr->compute_program_hash(); m_threadtable.put(tinfo_shared_ptr); - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2 != nullptr) { - m_inspector->get_sinsp_stats_v2()->m_n_added_threads++; + m_sinsp_stats_v2->m_n_added_threads++; } return tinfo_shared_ptr; @@ -1674,9 +1682,9 @@ void sinsp_thread_manager::remove_thread(int64_t tid) if(thread_to_remove == nullptr) { // Extra m_inspector nullptr check - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2 != nullptr) { - m_inspector->get_sinsp_stats_v2()->m_n_failed_thread_lookups++; + m_sinsp_stats_v2->m_n_failed_thread_lookups++; } return; } @@ -1796,9 +1804,9 @@ void sinsp_thread_manager::remove_thread(int64_t tid) * the cache just to be sure. */ m_last_tid = -1; - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2 != nullptr) { - m_inspector->get_sinsp_stats_v2()->m_n_removed_threads++; + m_sinsp_stats_v2->m_n_removed_threads++; } } @@ -2140,9 +2148,9 @@ threadinfo_map_t::ptr_t sinsp_thread_manager::find_thread(int64_t tid, bool look thr = m_last_tinfo.lock(); if (thr) { - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2 != nullptr) { - m_inspector->get_sinsp_stats_v2()->m_n_cached_thread_lookups++; + m_sinsp_stats_v2->m_n_cached_thread_lookups++; } // This allows us to avoid performing an actual timestamp lookup // for something that may not need to be precise @@ -2158,9 +2166,9 @@ threadinfo_map_t::ptr_t sinsp_thread_manager::find_thread(int64_t tid, bool look if(thr) { - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2 != nullptr) { - m_inspector->get_sinsp_stats_v2()->m_n_noncached_thread_lookups++; + m_sinsp_stats_v2->m_n_noncached_thread_lookups++; } if(!lookup_only) { @@ -2173,9 +2181,9 @@ threadinfo_map_t::ptr_t sinsp_thread_manager::find_thread(int64_t tid, bool look } else { - if (m_inspector != nullptr && m_inspector->get_sinsp_stats_v2()) + if (m_sinsp_stats_v2 != nullptr) { - m_inspector->get_sinsp_stats_v2()->m_n_failed_thread_lookups++; + m_sinsp_stats_v2->m_n_failed_thread_lookups++; } return NULL; } diff --git a/userspace/libsinsp/threadinfo.h b/userspace/libsinsp/threadinfo.h index 4da7e655ad..ec428de4da 100644 --- a/userspace/libsinsp/threadinfo.h +++ b/userspace/libsinsp/threadinfo.h @@ -198,7 +198,8 @@ class SINSP_PUBLIC sinsp_threadinfo : public libsinsp::state::table_entry */ inline bool is_in_pid_namespace() const { - return (m_flags & PPM_CL_CHILD_IN_PIDNS || m_tid != m_vtid); + // m_tid should be always valid because we read it from the scap event header + return (m_flags & PPM_CL_CHILD_IN_PIDNS || (m_tid != m_vtid && m_vtid >= 0)); } /*! @@ -1015,6 +1016,8 @@ class SINSP_PUBLIC sinsp_thread_manager: public libsinsp::state::table void free_dump_fdinfos(std::vector* fdinfos_to_free); sinsp* m_inspector; + std::shared_ptr m_sinsp_stats_v2; + /* the key is the pid of the group, and the value is a shared pointer to the thread_group_info */ std::unordered_map> m_thread_groups; threadinfo_map_t m_threadtable; diff --git a/userspace/libsinsp/utils.cpp b/userspace/libsinsp/utils.cpp index b448160c6a..6cd8ef0b87 100644 --- a/userspace/libsinsp/utils.cpp +++ b/userspace/libsinsp/utils.cpp @@ -1978,3 +1978,28 @@ std::string concat_set_in_order(const std::set& s, const std::strin std::string s_str = ss.str(); return s_str.substr(0, s_str.size() - delim.size()); } + +#define SINSP_UTILS_FORMATBUF_LEN 32 + +std::string buffer_to_multiline_hex(const char *buf, size_t size) +{ + char format_buf[SINSP_UTILS_FORMATBUF_LEN]; + std::stringstream ss; + + for(size_t i = 0; i < size; i++) + { + if (i % 16 == 0) + { + if (i != 0) + { + ss << "\n"; + } + snprintf(format_buf, SINSP_UTILS_FORMATBUF_LEN, "%03lx | ", i); + ss << format_buf; + } + snprintf(format_buf, SINSP_UTILS_FORMATBUF_LEN, "%02x ", buf[i]); + ss << format_buf; + } + + return ss.str(); +} diff --git a/userspace/libsinsp/utils.h b/userspace/libsinsp/utils.h index 0285a41953..37fc021b1d 100644 --- a/userspace/libsinsp/utils.h +++ b/userspace/libsinsp/utils.h @@ -294,6 +294,8 @@ std::string& trim(std::string& s); std::string& replace_in_place(std::string& s, const std::string& search, const std::string& replacement); std::string replace(const std::string& str, const std::string& search, const std::string& replacement); +std::string buffer_to_multiline_hex(const char *buf, size_t size); + /////////////////////////////////////////////////////////////////////////////// // number parser ///////////////////////////////////////////////////////////////////////////////