Compare commits

...

28 Commits

Author SHA1 Message Date
Roberto Scolaro c7276a354a fix(test/e2e): pin python requests version
Signed-off-by: Roberto Scolaro <roberto.scolaro21@gmail.com>
2024-05-21 17:24:13 +02:00
Roberto Scolaro 351acf09eb fix(libsinsp_e2e): increase tcp_client_server* tests stability
Signed-off-by: Roberto Scolaro <roberto.scolaro21@gmail.com>
2024-05-21 17:24:13 +02:00
Roberto Scolaro 27f484fcc7 fix(modern_bpf): address review comments
Signed-off-by: Roberto Scolaro <roberto.scolaro21@gmail.com>
2024-05-21 17:24:13 +02:00
Roberto Scolaro 79bb161cd2 fix(bpf): address review comments
Signed-off-by: Roberto Scolaro <roberto.scolaro21@gmail.com>
2024-05-21 17:24:13 +02:00
therealbobo 6be9def07e fix(libsinsp_e2e): increase tcp_client_server test stability
Signed-off-by: therealbobo <robi.ayrton@gmail.com>
2024-05-21 17:24:13 +02:00
therealbobo 6d05cc5e8d fix(bpf): fix missing definitions
Signed-off-by: therealbobo <robi.ayrton@gmail.com>
2024-05-21 17:24:13 +02:00
therealbobo f4670cffea fix(modern_bpf): define the compat timespec in struct flavors
Signed-off-by: therealbobo <robi.ayrton@gmail.com>
2024-05-21 17:24:13 +02:00
therealbobo 4c9dcc0a0e fix(bpf): use CONFIG_COMPAT to exclude ia32 calls
Signed-off-by: therealbobo <robi.ayrton@gmail.com>
2024-05-21 17:24:13 +02:00
therealbobo e1a2db6b78 refactor(modern_bpf): split iovec helper in 32/64 bit variants
Signed-off-by: therealbobo <robi.ayrton@gmail.com>
2024-05-21 17:24:13 +02:00
therealbobo e4d4263a3d cleanup(modern_bpf,bpf): use already defined struct
Signed-off-by: therealbobo <robi.ayrton@gmail.com>
2024-05-21 17:24:13 +02:00
therealbobo 9c6b325e84 cleanup(bpf): rename 32/64bit bpf helpers
Signed-off-by: therealbobo <robi.ayrton@gmail.com>
2024-05-21 17:24:13 +02:00
therealbobo c8985e2289 fix(libsinsp_e2e): remove flaky test
Signed-off-by: therealbobo <robi.ayrton@gmail.com>
2024-05-21 17:24:13 +02:00
therealbobo ec11f29c1c fix(modern_bpf): read MAX_UNIX_SOCKET_PATH from sockaddr
Signed-off-by: therealbobo <robi.ayrton@gmail.com>
2024-05-21 17:24:13 +02:00
therealbobo b7e51eb669 refactor(bpf): split bpf_parse_readv_writev_bufs even more
Signed-off-by: therealbobo <robi.ayrton@gmail.com>
2024-05-21 17:24:13 +02:00
therealbobo 105659ad1b refactor(bpf): split ia32 bpf_parse_readv_writev_bufs helper
Signed-off-by: therealbobo <robi.ayrton@gmail.com>
2024-05-21 17:24:13 +02:00
Roberto Scolaro d477a05906 fix(bpf): fix bpf on aarch64
Signed-off-by: Roberto Scolaro <roberto.scolaro21@gmail.com>
2024-05-21 17:24:13 +02:00
Roberto Scolaro a9e4072d67 cleanup(bpf): hide ia32 login in helper function
Signed-off-by: Roberto Scolaro <roberto.scolaro21@gmail.com>
2024-05-21 17:24:13 +02:00
Roberto Scolaro 9a5b7dc5fe cleanup(bpf): remove useless ifdefs
Signed-off-by: Roberto Scolaro <roberto.scolaro21@gmail.com>
2024-05-21 17:24:13 +02:00
Roberto Scolaro 9248c1695e fix(bpf): cast incompatibile pointer
Signed-off-by: Roberto Scolaro <roberto.scolaro21@gmail.com>
2024-05-21 17:24:13 +02:00
Roberto Scolaro ceadfcc7cd fix(ci): workaround to avoid random failures of asan
Signed-off-by: Roberto Scolaro <roberto.scolaro21@gmail.com>
2024-05-21 17:24:13 +02:00
Roberto Scolaro 32aa75e9f3 fix(libsinsp_e2e): add retry to subprocess class
Signed-off-by: Roberto Scolaro <roberto.scolaro21@gmail.com>
2024-05-21 17:24:13 +02:00
Roberto Scolaro d921af231d fix(modern_bpf): avoid overflow reading sockaddr_storage
Signed-off-by: Roberto Scolaro <roberto.scolaro21@gmail.com>
2024-05-21 17:24:13 +02:00
Roberto Scolaro 9a6bd05498 fix(libsinsp_e2e): dump only necessary events
Signed-off-by: Roberto Scolaro <roberto.scolaro21@gmail.com>
2024-05-21 17:24:13 +02:00
Roberto Scolaro 18dc06410b update(e2e_ci): bump actions
Signed-off-by: Roberto Scolaro <roberto.scolaro21@gmail.com>
2024-05-21 17:24:13 +02:00
Roberto Scolaro e1765a9e89 fix(bpf,modern_bpf): better 32bit support preadv/pwritev family
Signed-off-by: Roberto Scolaro <roberto.scolaro21@gmail.com>
2024-05-21 17:24:13 +02:00
Roberto Scolaro adbdec31b8 new(libsinsp_e2e): new misc tests
Signed-off-by: Roberto Scolaro <roberto.scolaro21@gmail.com>
2024-05-21 17:24:13 +02:00
therealbobo 019b361959 fix(bpf): better timespec support for ia32
Signed-off-by: therealbobo <robi.ayrton@gmail.com>
2024-05-21 17:24:13 +02:00
Federico Di Pierro f7a5a50a91 fix(driver/bpf): fixed old bpf probe with clang-18.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2024-05-21 17:24:13 +02:00
32 changed files with 1738 additions and 127 deletions

View File

@ -293,6 +293,10 @@ jobs:
git config --global --add safe.directory $GITHUB_WORKSPACE
- name: Build and test 🏗️🧪
env:
# This avoids random failures on CI.
# (https://github.com/google/sanitizers/issues/1322#issuecomment-699946942)
ASAN_OPTIONS: intercept_tls_get_addr=0
run: |
mkdir -p build && cd build
cmake -DBUILD_BPF=ON \
@ -302,8 +306,8 @@ jobs:
-DUSE_BUNDLED_LIBBPF=ON \
..
make -j$(nproc) sinsp-example driver bpf
sudo make e2e-install-deps
sudo ../test/e2e/scripts/run_tests.sh
sudo -E make e2e-install-deps
sudo -E ../test/e2e/scripts/run_tests.sh
- name: Archive test reports
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3

View File

@ -74,7 +74,7 @@ jobs:
sudo apt install -y --no-install-recommends linux-headers-$(uname -r) gcc-multilib g++-multilib
- name: Run sccache-cache
uses: mozilla-actions/sccache-action@v0.0.3
uses: mozilla-actions/sccache-action@v0.0.4
- name: Build e2e tests 🏗️
env:
@ -96,10 +96,11 @@ jobs:
-DUSE_BUNDLED_GTEST=ON \
..
make -j6 libsinsp_e2e_tests
sudo rm -vfr test/libsinsp_e2e/resources/_proc
cd ..
- name: Cache build
uses: actions/cache/save@v3
uses: actions/cache/save@v4.0.2
if: always()
id: cache
with:
@ -123,7 +124,7 @@ jobs:
- name: Restore build
id: cache
uses: actions/cache/restore@v3
uses: actions/cache/restore@v4.0.2
with:
path: build
key: build-e2e-${{ matrix.arch }}-${{ github.run_id }}
@ -146,7 +147,7 @@ jobs:
- name: Install deps
run: |
sudo apt install -y --no-install-recommends clang gcc llvm build-essential cmake
sudo apt install -y --no-install-recommends clang gcc llvm build-essential cmake python2
- name: Install kernel headers (actuated)
uses: self-actuated/get-kernel-sources@201eed7d915ac0a6021fb402cde5be7a6b945b59
@ -161,9 +162,12 @@ jobs:
# different workers, so we rebuild the drivers.
- name: Rebuild drivers
run: |
cd build
pushd build
make -B driver bpf
cd ..
pushd test/libsinsp_e2e/resources/
sudo tar xzf fake-proc.tar.gz
popd
popd
- name: Run e2e tests with ${{ matrix.driver.name }} 🏎️
if: matrix.arch == 'amd64'

View File

@ -1 +1 @@
2.20.0
2.20.1

View File

@ -33,23 +33,25 @@ or GPL2.txt for full copies of the license.
* structures. But the syscalls (might) still use them.
*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
#include <linux/time64.h>
struct compat_timespec {
int32_t tv_sec;
int32_t tv_nsec;
};
#include <linux/time32.h>
struct timespec {
int32_t tv_sec;
int32_t tv_nsec;
};
struct timespec {
int32_t tv_sec;
int32_t tv_nsec;
};
struct timeval {
int32_t tv_sec;
int32_t tv_usec;
};
struct timeval {
int32_t tv_sec;
int32_t tv_usec;
};
typedef struct old_timespec32 old_timespec32;
#else
#define timeval64 timeval
#if __has_include(<linux/time32.h>)
#include <linux/time32.h>
#else
#include <linux/compat.h>
#endif
#define timeval64 timeval
typedef struct compat_timespec old_timespec32;
#endif
#define FILLER_RAW(x) \
@ -469,7 +471,7 @@ FILLER(sys_write_x, true)
static __always_inline int bpf_poll_parse_fds(struct filler_data *data,
bool enter_event)
{
unsigned int read_size;
unsigned long read_size;
unsigned int fds_count;
int res = PPM_SUCCESS;
unsigned long nfds;
@ -570,17 +572,18 @@ FILLER(sys_poll_x, true)
}
#define MAX_IOVCNT 32
#define MAX_IOVCNT_COMPAT 8
static __always_inline int bpf_parse_readv_writev_bufs(struct filler_data *data,
const struct iovec __user *iovsrc,
static __always_inline int bpf_parse_readv_writev_bufs_64(struct filler_data *data,
const void __user *iovsrc,
unsigned long iovcnt,
long retval,
int flags)
int flags,
unsigned long *size)
{
const struct iovec *iov;
int res = PPM_SUCCESS;
unsigned int copylen;
long size = 0;
unsigned long copylen;
int j;
copylen = iovcnt * sizeof(struct iovec);
@ -592,42 +595,41 @@ static __always_inline int bpf_parse_readv_writev_bufs(struct filler_data *data,
}
#ifdef BPF_FORBIDS_ZERO_ACCESS
if (copylen)
if (bpf_probe_read_user((void *)iov,
((copylen - 1) & SCRATCH_SIZE_MAX) + 1,
(void *)iovsrc))
if (copylen)
if (bpf_probe_read_user((void *)iov,
((copylen - 1) & SCRATCH_SIZE_MAX) + 1,
(void *)iovsrc))
#else
if (bpf_probe_read_user((void *)iov,
copylen & SCRATCH_SIZE_MAX,
(void *)iovsrc))
if (bpf_probe_read_user((void *)iov,
copylen & SCRATCH_SIZE_MAX,
(void *)iovsrc))
#endif
return PPM_FAILURE_INVALID_USER_MEMORY;
return PPM_FAILURE_INVALID_USER_MEMORY;
#pragma unroll
for (j = 0; j < MAX_IOVCNT; ++j) {
if (j == iovcnt)
break;
// BPF seems to require a hard limit to avoid overflows
if (size == LONG_MAX)
if (*size == LONG_MAX)
break;
size += iov[j].iov_len;
*size += iov[j].iov_len;
}
if ((flags & PRB_FLAG_IS_WRITE) == 0)
if (size > retval)
size = retval;
if (*size > retval)
*size = retval;
if (flags & PRB_FLAG_PUSH_SIZE) {
res = bpf_push_u32_to_ring(data, (uint32_t)size);
if (flags & PRB_FLAG_PUSH_SIZE && res == PPM_SUCCESS) {
res = bpf_push_u32_to_ring(data, (uint32_t)*size);
CHECK_RES(res);
}
if (flags & PRB_FLAG_PUSH_DATA) {
if (size > 0) {
if (*size > 0) {
unsigned long off = _READ(data->state->tail_ctx.curoff);
unsigned long remaining = size;
unsigned long remaining = *size;
int j;
#pragma unroll
@ -649,30 +651,160 @@ static __always_inline int bpf_parse_readv_writev_bufs(struct filler_data *data,
if (to_read > SCRATCH_SIZE_HALF)
to_read = SCRATCH_SIZE_HALF;
#ifdef BPF_FORBIDS_ZERO_ACCESS
if (to_read)
#ifdef BPF_FORBIDS_ZERO_ACCESS
if (to_read)
if (bpf_probe_read_user(&data->buf[off_bounded],
((to_read - 1) & SCRATCH_SIZE_HALF) + 1,
(void*)iov[j].iov_base))
#else
if (bpf_probe_read_user(&data->buf[off_bounded],
((to_read - 1) & SCRATCH_SIZE_HALF) + 1,
iov[j].iov_base))
#else
if (bpf_probe_read_user(&data->buf[off_bounded],
to_read & SCRATCH_SIZE_HALF,
iov[j].iov_base))
#endif
return PPM_FAILURE_INVALID_USER_MEMORY;
to_read & SCRATCH_SIZE_HALF,
(void*)iov[j].iov_base))
#endif
return PPM_FAILURE_INVALID_USER_MEMORY;
remaining -= to_read;
off += to_read;
}
} else {
size = 0;
*size = 0;
}
return PPM_SUCCESS;
}
return res;
}
#ifdef CONFIG_COMPAT
static __always_inline int bpf_parse_readv_writev_bufs_32(struct filler_data *data,
const void __user *iovsrc,
unsigned long iovcnt,
long retval,
int flags,
unsigned long *size)
{
const struct compat_iovec *compat_iov;
int res = PPM_SUCCESS;
unsigned long copylen;
int j;
copylen = iovcnt * sizeof(struct compat_iovec);
compat_iov = (const struct compat_iovec *)data->tmp_scratch;
if (copylen > SCRATCH_SIZE_MAX)
{
return PPM_FAILURE_FRAME_SCRATCH_MAP_FULL;
}
#ifdef BPF_FORBIDS_ZERO_ACCESS
if (copylen)
if (bpf_probe_read_user((void *)compat_iov,
((copylen - 1) & SCRATCH_SIZE_MAX) + 1,
(void *)iovsrc))
#else
if (bpf_probe_read_user((void *)compat_iov,
copylen & SCRATCH_SIZE_MAX,
(void *)iovsrc))
#endif
return PPM_FAILURE_INVALID_USER_MEMORY;
#pragma unroll
for (j = 0; j < MAX_IOVCNT_COMPAT; ++j) {
if (j == iovcnt)
break;
// BPF seems to require a hard limit to avoid overflows
if (*size == LONG_MAX)
break;
*size += compat_iov[j].iov_len;
}
if ((flags & PRB_FLAG_IS_WRITE) == 0)
if (*size > retval)
*size = retval;
if (flags & PRB_FLAG_PUSH_SIZE && res == PPM_SUCCESS) {
res = bpf_push_u32_to_ring(data, (uint32_t)*size);
CHECK_RES(res);
}
if (flags & PRB_FLAG_PUSH_DATA) {
if (*size > 0) {
unsigned long off = _READ(data->state->tail_ctx.curoff);
unsigned long remaining = *size;
int j;
// The 14 iovec count limit is due to old kernels verifiers
// complaining.
#pragma unroll
for (j = 0; j < MAX_IOVCNT_COMPAT; ++j) {
volatile unsigned int to_read;
if (j == iovcnt)
break;
unsigned long off_bounded = off & SCRATCH_SIZE_HALF;
if (off > SCRATCH_SIZE_HALF)
break;
if (compat_iov[j].iov_len <= remaining)
to_read = compat_iov[j].iov_len;
else
to_read = remaining;
if (to_read > SCRATCH_SIZE_HALF)
to_read = SCRATCH_SIZE_HALF;
#ifdef BPF_FORBIDS_ZERO_ACCESS
if (to_read)
if (bpf_probe_read_user(&data->buf[off_bounded],
((to_read - 1) & SCRATCH_SIZE_HALF) + 1,
(void*)compat_iov[j].iov_base))
#else
if (bpf_probe_read_user(&data->buf[off_bounded],
to_read & SCRATCH_SIZE_HALF,
(void*)compat_iov[j].iov_base))
#endif
return PPM_FAILURE_INVALID_USER_MEMORY;
remaining -= to_read;
off += to_read;
}
} else {
*size = 0;
}
return PPM_SUCCESS;
}
return res;
}
#endif
static __always_inline int bpf_parse_readv_writev_bufs(struct filler_data *data,
const void __user *iovsrc,
unsigned long iovcnt,
long retval,
int flags)
{
unsigned long size = 0;
int res = PPM_SUCCESS;
if (!bpf_in_ia32_syscall())
{
res = bpf_parse_readv_writev_bufs_64(data, iovsrc, iovcnt, retval, flags, &size);
}
else
{
#ifdef CONFIG_COMPAT
res = bpf_parse_readv_writev_bufs_32(data, iovsrc, iovcnt, retval, flags, &size);
#endif
}
if(flags & PRB_FLAG_PUSH_DATA && res == PPM_SUCCESS)
{
data->fd = bpf_syscall_get_argument(data, 0);
data->curarg_already_on_frame = true;
return __bpf_val_to_ring(data, 0, size, PT_BYTEBUF, -1, true, KERNEL);
}
return res;
}
@ -790,10 +922,10 @@ FILLER(sys_writev_pwritev_x, true)
val = bpf_syscall_get_argument(data, 1);
iovcnt = bpf_syscall_get_argument(data, 2);
res = bpf_parse_readv_writev_bufs(data,
(const struct iovec __user *)val,
iovcnt,
0,
PRB_FLAG_PUSH_DATA | PRB_FLAG_IS_WRITE);
(const struct iovec __user *)val,
iovcnt,
0,
PRB_FLAG_PUSH_DATA | PRB_FLAG_IS_WRITE);
/* if there was an error we send an empty param.
* we can improve this in the future but at least we don't lose the whole event.
@ -809,13 +941,22 @@ FILLER(sys_writev_pwritev_x, true)
static __always_inline int timespec_parse(struct filler_data *data,
unsigned long val)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
struct __kernel_timespec ts = {};
#else
struct timespec ts = {};
#endif
bpf_probe_read_user(&ts, sizeof(ts), (void *)val);
return bpf_push_u64_to_ring(data, ((uint64_t)ts.tv_sec) * 1000000000 + ts.tv_nsec);
if (!bpf_in_ia32_syscall())
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
struct __kernel_timespec ts = {};
#else
struct timespec ts = {};
#endif
bpf_probe_read_user(&ts, sizeof(ts), (void *)val);
return bpf_push_u64_to_ring(data, ((uint64_t)ts.tv_sec) * 1000000000 + ts.tv_nsec);
}
else
{
old_timespec32 ts = {};
bpf_probe_read_user(&ts, sizeof(ts), (void *)val);
return bpf_push_u64_to_ring(data, ((uint32_t)ts.tv_sec) * 1000000000 + ts.tv_nsec);
}
}
FILLER(sys_nanosleep_e, true)
@ -3071,12 +3212,10 @@ FILLER(sys_generic, true)
// if we are in ia32 syscall sys_{enter,exit} already
// validated the converted 32bit->64bit syscall ID for us,
// otherwise the event would've been discarded.
#if defined(CONFIG_X86_64) && defined(CONFIG_IA32_EMULATION)
if (bpf_in_ia32_syscall())
{
native_id = convert_ia32_to_64(native_id);
}
#endif
sc_evt = get_syscall_info(native_id);
if (!sc_evt) {
@ -3993,10 +4132,10 @@ FILLER(sys_pwritev_e, true)
/* Parameter 2: size (type: PT_UINT32) */
res = bpf_parse_readv_writev_bufs(data,
(const struct iovec __user *)iov_pointer,
iov_cnt,
0,
PRB_FLAG_PUSH_SIZE | PRB_FLAG_IS_WRITE);
(const struct iovec __user *)iov_pointer,
iov_cnt,
0,
PRB_FLAG_PUSH_SIZE | PRB_FLAG_IS_WRITE);
/* if there was an error we send a size equal to `0`.
* we can improve this in the future but at least we don't lose the whole event.

View File

@ -79,4 +79,15 @@ struct modern_bpf__kernel_timex_timeval
long long int tv_usec;
};
/*
* This is equivalent to old_timespec32 or compat_timespec. Some old distros
* don't define old_timespec32 (e.g. centos 8 with 4.18 kernel), so we define
* it here.
*/
struct modern_bpf__kernel_timespec_ia32
{
int tv_sec;
int tv_nsec;
};
#endif /* __STRUCT_FLAVORS_H__ */

View File

@ -1003,10 +1003,11 @@ static __always_inline void auxmap__store_iovec_size_param(struct auxiliary_map
* @param iov_cnt number of `iovec` structs to be read from userspace.
* @param len_to_read imposed snaplen.
*/
static __always_inline void auxmap__store_iovec_data_param(struct auxiliary_map *auxmap, unsigned long iov_pointer, unsigned long iov_cnt, unsigned long len_to_read)
static __always_inline void auxmap__store_iovec_data_param_64(struct auxiliary_map *auxmap, unsigned long iov_pointer, unsigned long iov_cnt, unsigned long len_to_read)
{
/* We use the second part of our auxmap as a scratch space. */
uint32_t total_iovec_size = iov_cnt * bpf_core_type_size(struct iovec);
unsigned long total_iovec_size = iov_cnt * bpf_core_type_size(struct iovec);
if(bpf_probe_read_user((void *)&auxmap->data[MAX_PARAM_SIZE],
SAFE_ACCESS(total_iovec_size),
(void *)iov_pointer))
@ -1016,7 +1017,7 @@ static __always_inline void auxmap__store_iovec_data_param(struct auxiliary_map
return;
}
uint32_t total_size_to_read = 0;
unsigned long total_size_to_read = 0;
/* Pointer to iovec structs */
const struct iovec *iovec = (const struct iovec *)&auxmap->data[MAX_PARAM_SIZE];
@ -1050,6 +1051,66 @@ static __always_inline void auxmap__store_iovec_data_param(struct auxiliary_map
push__param_len(auxmap->data, &auxmap->lengths_pos, total_size_to_read);
}
static __always_inline void auxmap__store_iovec_data_param_32(struct auxiliary_map *auxmap, unsigned long iov_pointer, unsigned long iov_cnt, unsigned long len_to_read)
{
/* We use the second part of our auxmap as a scratch space. */
unsigned long total_iovec_size = iov_cnt * bpf_core_type_size(struct compat_iovec);
if(bpf_probe_read_user((void *)&auxmap->data[MAX_PARAM_SIZE],
SAFE_ACCESS(total_iovec_size),
(void *)iov_pointer))
{
/* in case of NULL iovec vector we return an empty param */
push__param_len(auxmap->data, &auxmap->lengths_pos, 0);
return;
}
unsigned long total_size_to_read = 0;
/* Pointer to iovec structs */
const struct compat_iovec *compat_iovec = (const struct compat_iovec *)&auxmap->data[MAX_PARAM_SIZE];
uint64_t initial_payload_pos = auxmap->payload_pos;
for(int j = 0; j < MAX_IOVCNT; j++)
{
if(total_size_to_read > len_to_read)
{
/* If we break here it could be that `payload_pos` overcame the max `len_to_read` for this reason
* we have an enforcement after the for loop.
*/
total_size_to_read = len_to_read;
break;
}
if(j == iov_cnt)
{
break;
}
uint16_t bytes_read = push__bytebuf(auxmap->data, &auxmap->payload_pos, (unsigned long)compat_iovec[j].iov_base, compat_iovec[j].iov_len, USER);
if(!bytes_read)
{
push__param_len(auxmap->data, &auxmap->lengths_pos, total_size_to_read);
return;
}
total_size_to_read += bytes_read;
}
/* We need this enforcement to be sure that we don't overcome the max `len_to_read` */
auxmap->payload_pos = initial_payload_pos + total_size_to_read;
push__param_len(auxmap->data, &auxmap->lengths_pos, total_size_to_read);
}
static __always_inline void auxmap__store_iovec_data_param(struct auxiliary_map *auxmap, unsigned long iov_pointer, unsigned long iov_cnt, unsigned long len_to_read)
{
if(!bpf_in_ia32_syscall())
{
auxmap__store_iovec_data_param_64(auxmap, iov_pointer, iov_cnt, len_to_read);
}
else
{
auxmap__store_iovec_data_param_32(auxmap, iov_pointer, iov_cnt, len_to_read);
}
}
/**
* @brief Store the size extracted from a `user_msghdr` struct.
* Please note: the size is an unsigned 32 bit value so

View File

@ -319,7 +319,16 @@ static __always_inline void ringbuf__store_iovec_size_param(struct ringbuf_struc
return;
}
uint32_t total_iovec_size = iov_cnt * bpf_core_type_size(struct iovec);
uint32_t total_iovec_size = 0;
if(!bpf_in_ia32_syscall())
{
total_iovec_size = iov_cnt * bpf_core_type_size(struct iovec);
}
else
{
total_iovec_size = iov_cnt * bpf_core_type_size(struct compat_iovec);
}
if(bpf_probe_read_user((void *)&auxmap->data[0],
SAFE_ACCESS(total_iovec_size),
(void *)iov_pointer))
@ -331,14 +340,29 @@ static __always_inline void ringbuf__store_iovec_size_param(struct ringbuf_struc
uint32_t total_size_to_read = 0;
/* Pointer to iovec structs */
const struct iovec *iovec = (const struct iovec *)&auxmap->data[0];
for(int j = 0; j < MAX_IOVCNT; j++)
if(!bpf_in_ia32_syscall())
{
if(j == iov_cnt)
const struct iovec *iovec = (const struct iovec *)&auxmap->data[0];
for(int j = 0; j < MAX_IOVCNT; j++)
{
break;
if(j == iov_cnt)
{
break;
}
total_size_to_read += iovec[j].iov_len;
}
}
else
{
const struct compat_iovec *iovec = (const struct compat_iovec *)&auxmap->data[0];
for(int j = 0; j < MAX_IOVCNT; j++)
{
if(j == iov_cnt)
{
break;
}
total_size_to_read += iovec[j].iov_len;
}
total_size_to_read += iovec[j].iov_len;
}
ringbuf__store_u32(ringbuf, total_size_to_read);
}

View File

@ -38,17 +38,26 @@ int BPF_PROG(ppoll_e,
/* Parameter 2: timeout (type: PT_RELTIME) */
uint64_t nanosec = 0;
unsigned long ts_pointer = extract__syscall_argument(regs, 2);
if(bpf_core_type_exists(struct __kernel_timespec))
if(!bpf_in_ia32_syscall())
{
struct __kernel_timespec ts = {0};
bpf_probe_read_user(&ts, bpf_core_type_size(struct __kernel_timespec), (void *)ts_pointer);
nanosec = ((uint64_t)ts.tv_sec) * SECOND_TO_NS + ts.tv_nsec;
if(bpf_core_type_exists(struct __kernel_timespec))
{
struct __kernel_timespec ts = {0};
bpf_probe_read_user(&ts, bpf_core_type_size(struct __kernel_timespec), (void *)ts_pointer);
nanosec = ((uint64_t)ts.tv_sec) * SECOND_TO_NS + ts.tv_nsec;
}
else
{
struct modern_bpf__kernel_timespec ts = {0};
bpf_probe_read_user(&ts, sizeof(ts), (void *)ts_pointer);
nanosec = ((uint64_t)ts.tv_sec) * SECOND_TO_NS + ts.tv_nsec;
}
}
else
{
struct modern_bpf__kernel_timespec ts = {0};
struct modern_bpf__kernel_timespec_ia32 ts = {0};
bpf_probe_read_user(&ts, sizeof(ts), (void *)ts_pointer);
nanosec = ((uint64_t)ts.tv_sec) * SECOND_TO_NS + ts.tv_nsec;
nanosec = ((uint32_t)ts.tv_sec) * SECOND_TO_NS + ts.tv_nsec;
}
auxmap__store_u64_param(auxmap, nanosec);

View File

@ -1,3 +1,4 @@
pytest==7.1.1
pytest-html==3.1.1
docker==6.0.0
requests==2.31.0

View File

@ -32,6 +32,7 @@ configure_file (
)
add_executable(libsinsp_e2e_tests
capture_to_file_test.cpp
container/container.cpp
container/container_cgroup.cpp
container/docker_utils.cpp
@ -50,6 +51,7 @@ add_executable(libsinsp_e2e_tests
threadinfo.cpp
thread_state.cpp
udp_client_server.cpp
unix_client_server.cpp
)
if(BUILD_BPF)
@ -103,3 +105,8 @@ file(COPY
DESTINATION
${CMAKE_CURRENT_BINARY_DIR}/resources/
)
execute_process(
COMMAND tar xzf ${CMAKE_CURRENT_BINARY_DIR}/resources/fake-proc.tar.gz
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/resources/
)

View File

@ -0,0 +1,73 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright (C) 2024 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "sys_call_test.h"
#include <gtest/gtest.h>
#include <sys/stat.h>
#include <libsinsp/sinsp.h>
TEST_F(sys_call_test, can_consume_a_capture_file)
{
int callnum = 0;
event_filter_t filter = [&](sinsp_evt* evt)
{
std::string evt_name(evt->get_name());
return evt_name.find("stat") != std::string::npos &&
m_tid_filter(evt) && evt->get_direction() == SCAP_ED_OUT;
};
run_callback_t test = [](concurrent_object_handle<sinsp> inspector_handle)
{
struct stat sb;
for(int i = 0; i < 100; i++)
{
stat("/tmp", &sb);
}
};
captured_event_callback_t callback = [&](const callback_param& param) { callnum++; };
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); });
EXPECT_EQ(100, callnum);
sinsp inspector;
sinsp_evt* event;
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
auto filename = std::string(LIBSINSP_TEST_CAPTURES_PATH) + test_info->test_case_name() + "_" +
test_info->name() + ".scap";
inspector.open_savefile(filename);
callnum = 0;
int32_t res;
while((res = inspector.next(&event)) != SCAP_EOF)
{
ASSERT_EQ(SCAP_SUCCESS, res);
std::string evt_name(event->get_name());
if(evt_name.find("stat") != std::string::npos &&
m_tid_filter(event) && event->get_direction() == SCAP_ED_OUT)
{
callnum++;
}
}
ASSERT_EQ(SCAP_EOF, res);
ASSERT_EQ(100, callnum);
}

View File

@ -446,7 +446,7 @@ TEST_F(sys_call_test, container_libvirt)
if (system("virsh --help > /dev/null 2>&1") != 0)
{
printf("libvirt not installed, skipping test\n");
GTEST_SKIP() << "libvirt not installed, skipping test";
return;
}
@ -722,6 +722,83 @@ static void healthcheck_helper(
ASSERT_EQ(cstate.healthcheck_seen, expect_healthcheck) << capture_stats_str;
}
static void healthcheck_tracefile_helper(
const std::string& dockerfile,
bool expect_healthcheck,
sinsp_threadinfo::command_category expected_cat = sinsp_threadinfo::CAT_HEALTHCHECK)
{
container_state cstate;
std::string build_cmdline("cd " LIBSINSP_TEST_RESOURCES_PATH "/docker/health_dockerfiles && docker build -t cont_health_ut_img -f "
+ dockerfile + " . > /dev/null 2>&1");
ASSERT_TRUE(system(build_cmdline.c_str()) == 0);
run_callback_t test = [](concurrent_object_handle<sinsp> inspector_handle)
{
// --network=none speeds up the container setup a bit.
ASSERT_TRUE((system("docker run --rm --network=none --name cont_health_ut cont_health_ut_img "
"/bin/sh -c '/bin/sleep 10' > /dev/null 2>&1")) == 0);
};
event_filter_t filter = [&](sinsp_evt* evt)
{
std::string evt_name(evt->get_name());
return evt_name.find("execve") != std::string::npos &&
evt->get_direction() == SCAP_ED_OUT;
};
captured_event_callback_t callback = [&](const callback_param& param) {return;};
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); });
// Now reread the file we just wrote and pass it through
// update_container_state.
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
auto dumpfile = std::string(LIBSINSP_TEST_CAPTURES_PATH) + test_info->test_case_name() + "_" +
test_info->name() + ".scap";
sinsp inspector;
inspector.set_hostname_and_port_resolution_mode(false);
inspector.set_filter("evt.type=execve and evt.dir=<");
inspector.open_savefile(dumpfile);
inspector.start_capture();
while (1)
{
sinsp_evt* ev;
int32_t res = inspector.next(&ev);
if (res == SCAP_TIMEOUT)
{
continue;
}
if (res == SCAP_FILTERED_EVENT)
{
continue;
}
else if (res == SCAP_EOF)
{
break;
}
ASSERT_TRUE(res == SCAP_SUCCESS);
update_container_state(&inspector, ev, cstate, expected_cat);
}
std::string capture_stats_str = capture_stats(&inspector);
inspector.stop_capture();
inspector.close();
ASSERT_TRUE(cstate.root_cmd_seen) << capture_stats_str;
ASSERT_TRUE(cstate.second_cmd_seen) << capture_stats_str;
ASSERT_EQ(cstate.container_w_health_probe, expect_healthcheck) << capture_stats_str;
ASSERT_EQ(cstate.healthcheck_seen, expect_healthcheck) << capture_stats_str;
}
// Run container w/o health check, should not find any health check
// for the container. Should not identify either the entrypoint
// or a second process spawned after as a health check process.
@ -791,6 +868,33 @@ TEST_F(sys_call_test, docker_container_readiness_probe)
sinsp_threadinfo::CAT_READINESS_PROBE);
}
// Identical to above tests, but read events from a trace file instead
// of live. Only doing selected cases.
TEST_F(sys_call_test, docker_container_healthcheck_trace)
{
healthcheck_tracefile_helper("Dockerfile.healthcheck", true);
}
TEST_F(sys_call_test, docker_container_healthcheck_cmd_overlap_trace)
{
healthcheck_tracefile_helper("Dockerfile.healthcheck_cmd_overlap", true);
}
TEST_F(sys_call_test, docker_container_liveness_probe_trace)
{
healthcheck_tracefile_helper("Dockerfile.healthcheck_liveness",
true,
sinsp_threadinfo::CAT_LIVENESS_PROBE);
}
TEST_F(sys_call_test, docker_container_readiness_probe_trace)
{
healthcheck_tracefile_helper("Dockerfile.healthcheck_readiness",
true,
sinsp_threadinfo::CAT_READINESS_PROBE);
}
TEST_F(sys_call_test, docker_container_large_json)
{
bool saw_container_evt = false;

View File

@ -125,7 +125,7 @@ void event_capture::capture()
if (SCAP_SUCCESS == next_result)
{
result = handle_event(event);
if (m_mode != SINSP_MODE_NODRIVER)
if (m_mode != SINSP_MODE_NODRIVER && m_dump)
{
dumper->dump(event);
}
@ -169,7 +169,10 @@ void event_capture::capture()
{
break;
}
dumper->dump(event);
if(m_dump)
{
dumper->dump(event);
}
result = handle_event(event);
}
{
@ -177,7 +180,6 @@ void event_capture::capture()
while (SCAP_SUCCESS == get_inspector()->next(&event))
{
// just consume the remaining events
dumper->dump(event);
}
}
}

View File

@ -147,7 +147,8 @@ public:
uint64_t thread_timeout_ns = (uint64_t)60 * 1000 * 1000 * 1000,
uint64_t inactive_thread_scan_time_ns = (uint64_t)60 * 1000 * 1000 * 1000,
sinsp_mode_t mode = SINSP_MODE_LIVE,
uint64_t max_timeouts = 3)
uint64_t max_timeouts = 3,
bool dump = true)
{
event_capture capturing;
{ // Synchronized section
@ -162,6 +163,7 @@ public:
capturing.m_thread_timeout_ns = thread_timeout_ns;
capturing.m_inactive_thread_scan_time_ns = inactive_thread_scan_time_ns;
capturing.m_max_timeouts = max_timeouts;
capturing.m_dump = dump;
}
@ -233,4 +235,5 @@ private:
static bool inspector_ok;
sinsp_mode_t m_mode;
uint64_t m_max_timeouts;
bool m_dump;
};

View File

@ -1465,7 +1465,11 @@ TEST_F(sys_call_test, large_read_write)
inspector->set_snaplen(DEFAULT_SNAPLEN);
};
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter, setup, cleanup); });
// We don't dump events to scap files, otherwise we could stuck with modern bpf.
ASSERT_NO_FATAL_FAILURE({event_capture::run(test, callback, filter, setup,
cleanup, event_capture::always_continue, 131072,
(uint64_t)60 * 1000 * 1000 * 1000, (uint64_t)60 * 1000 * 1000 * 1000,
SINSP_MODE_LIVE, 3, false); });
EXPECT_EQ(4, callnum);
}
@ -1603,7 +1607,11 @@ TEST_F(sys_call_test, large_readv_writev)
inspector->set_snaplen(DEFAULT_SNAPLEN);
};
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter, setup, cleanup); });
// We don't dump events to scap files, otherwise we could stuck with modern bpf.
ASSERT_NO_FATAL_FAILURE({event_capture::run(test, callback, filter, setup,
cleanup, event_capture::always_continue, 131072,
(uint64_t)60 * 1000 * 1000 * 1000, (uint64_t)60 * 1000 * 1000 * 1000,
SINSP_MODE_LIVE, 3, false); });
EXPECT_EQ(4, callnum);
}

View File

@ -0,0 +1,17 @@
foreach(
dockerfile
Dockerfile.healthcheck
Dockerfile.healthcheck_shell
Dockerfile.healthcheck_cmd_overlap
Dockerfile.healthcheck_liveness
Dockerfile.healthcheck_readiness
Dockerfile.no_healthcheck
Dockerfile.none_healthcheck
)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/${dockerfile} ${CMAKE_CURRENT_BINARY_DIR}/${dockerfile}
COPYONLY
)
endforeach(dockerfile)

View File

@ -0,0 +1,3 @@
FROM busybox
RUN cp /bin/true /bin/ut-health-check
HEALTHCHECK --interval=0.5s CMD ["/bin/ut-health-check"]

View File

@ -0,0 +1,2 @@
FROM busybox
HEALTHCHECK --interval=0.5s CMD ["/bin/sh", "-c", "/bin/sleep 10"]

View File

@ -0,0 +1,8 @@
FROM busybox
RUN cp /bin/true /bin/ut-health-check
# This container runs a docker healthcheck, but due to the
# annotation.... label, it gets interpretated as if it were a k8s
# liveness check.
HEALTHCHECK --interval=0.5s CMD ["/bin/ut-health-check"]
LABEL annotation.kubectl.kubernetes.io/last-applied-configuration="{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{},\"name\":\"mysql-app\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"MYSQL_ROOT_PASSWORD\",\"value\":\"no\"}],\"image\":\"mstemm/mysql:healthcheck\",\"livenessProbe\":{\"exec\":{\"command\":[\"/bin/ut-health-check\"]},\"initialDelaySeconds\":5,\"periodSeconds\":5},\"name\":\"mysql\"}]}}\n"

View File

@ -0,0 +1,8 @@
FROM busybox
RUN cp /bin/true /bin/ut-health-check
# This container runs a docker healthcheck, but due to the
# annotation.... label, it gets interpretated as if it were a k8s
# readiness check.
HEALTHCHECK --interval=0.5s CMD ["/bin/ut-health-check"]
LABEL annotation.kubectl.kubernetes.io/last-applied-configuration="{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{},\"name\":\"mysql-app\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"MYSQL_ROOT_PASSWORD\",\"value\":\"no\"}],\"image\":\"mstemm/mysql:healthcheck\",\"readinessProbe\":{\"exec\":{\"command\":[\"/bin/ut-health-check\"]},\"initialDelaySeconds\":5,\"periodSeconds\":5},\"name\":\"mysql\"}]}}\n"

View File

@ -0,0 +1,3 @@
FROM busybox
RUN cp /bin/true /bin/ut-health-check
HEALTHCHECK --interval=0.5s CMD /bin/ut-health-check

View File

@ -0,0 +1 @@
FROM busybox:latest

View File

@ -0,0 +1,2 @@
FROM busybox:latest
HEALTHCHECK NONE

Binary file not shown.

View File

@ -0,0 +1,43 @@
#!/usr/bin/python2
# coding: utf-8 -*-
import socket
import os, os.path
import time
import sys
PAYLOAD = "0123456789QWERTYUIOPASDFGHJKLZXCVBNM"
NAME = "/tmp/python_unix_sockets_example"
if sys.argv[1] == 'server':
if os.path.exists(NAME):
os.remove(NAME)
server = socket.socket( socket.AF_UNIX, socket.SOCK_STREAM )
server.bind(NAME)
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
print "STARTED"
server.listen(5)
connect, address = server.accept()
resp = connect.recv( 1024 )
connect.send(resp)
connect.close()
server.close()
os.remove(NAME)
else:
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
if os.path.exists(NAME):
client = socket.socket( socket.AF_UNIX, socket.SOCK_STREAM )
client.connect(NAME)
print "STARTED"
client.send(PAYLOAD)
resp = client.recv(1024)
client.close()
else:
print >> sys.stderr, "Couldn't Connect!"

View File

@ -30,11 +30,13 @@ limitations under the License.
#include <sys/wait.h>
#include <ext/stdio_filebuf.h>
subprocess::subprocess(std::string command, std::vector<std::string> arguments, bool start_now)
: m_pid(-1)
subprocess::subprocess(std::string command, std::vector<std::string> arguments,
bool start_now, int retry_attempts):
m_pid(-1),
m_retry_attemps(retry_attempts),
m_command(command),
m_args(arguments)
{
m_command = command;
m_args = arguments;
if(start_now)
{
start();
@ -60,20 +62,26 @@ void subprocess::wait_for_start()
timeout.tv_usec = 0;
int result = select(m_out_pipe[0] + 1, &read_set, nullptr, nullptr, &timeout);
int attempt = 0;
switch(result)
while(attempt < m_retry_attemps)
{
case -1:
perror("select");
break;
case 0:
std::cerr << "Timeout waiting for process to start." << std::endl;
break;
default:
if (!FD_ISSET(m_out_pipe[0], &read_set)) {
std::cerr << "Unexpected error during select." << std::endl;
}
break;
switch(result)
{
case -1:
perror("select");
break;
case 0:
std::cerr << "Timeout waiting for process to start. Retry n."
<< (attempt + 1) << "/" << m_retry_attemps << std::endl;
break;
default:
if (!FD_ISSET(m_out_pipe[0], &read_set)) {
std::cerr << "Unexpected error during select." << std::endl;
}
break;
}
attempt++;
}
}

View File

@ -31,8 +31,8 @@ limitations under the License.
class subprocess {
public:
subprocess(std::string command, std::vector<std::string> arguments, bool start_now=true);
subprocess(std::string command, std::vector<std::string> arguments,
bool start_now=true, int retry_attempts=3);
~subprocess();
void wait_for_start();
@ -51,6 +51,7 @@ class subprocess {
pid_t m_pid;
int m_in_pipe[2];
int m_out_pipe[2];
int m_retry_attemps;
std::ostream* m_in_stream;
std::istream* m_out_stream;

View File

@ -23,6 +23,7 @@ limitations under the License.
#include <cstdint>
#include <libscap/scap-int.h>
#include <libscap/scap_engines.h>
#include <libscap/scap_platform.h>
#include <gtest/gtest.h>
@ -351,6 +352,92 @@ TEST_F(sys_call_test, close_badfd_dropping)
EXPECT_EQ(0, callnum);
}
// The poll syscall is not defined on arm64.
#if !defined(__aarch64__)
TEST_F(sys_call_test, poll_timeout)
{
int callnum = 0;
event_filter_t filter = [&](sinsp_evt* evt)
{
uint16_t type = evt->get_type();
auto ti = evt->get_thread_info(false);
return (type == PPME_SYSCALL_POLL_E ||
type == PPME_SYSCALL_POLL_X) &&
ti->m_comm == "test_helper";
};
std::string my_pipe[2];
run_callback_t test = [&](concurrent_object_handle<sinsp> inspector_handle)
{
subprocess handle(LIBSINSP_TEST_PATH "/test_helper", {"poll_timeout"});
std::stringstream ss;
ss << handle.out();
my_pipe[0] = ss.str();
ss.clear();
ss.str(" ");
ss << handle.out();
my_pipe[1] = ss.str();
ss.clear();
ss.str(" ");
handle.wait();
};
captured_event_callback_t callback = [&](const callback_param& param)
{
sinsp_evt* e = param.m_evt;
uint16_t type = e->get_type();
if (type == PPME_SYSCALL_POLL_E)
{
//
// stdin and stdout can be a file or a fifo depending
// on how the tests are invoked
//
std::string fds = e->get_param_value_str("fds");
std::string expected_fds = my_pipe[0] + ":p1 " +
my_pipe[1] + ":p4";
EXPECT_EQ(expected_fds, fds)
<< "Value of fds is not one of expected values: " << fds;
EXPECT_EQ("20", e->get_param_value_str("timeout"));
callnum++;
}
else if (type == PPME_SYSCALL_POLL_X)
{
std::string fds = e->get_param_value_str("fds");
std::string expected_fds = my_pipe[0] + ":p0 " +
my_pipe[1] + ":p4";
int64_t res = std::stol(e->get_param_value_str("res"));
EXPECT_GT(res, 0);
EXPECT_LE(res, 2);
switch (res)
{
case 1:
EXPECT_EQ(expected_fds, fds)
<< "Value of fds is not one of expected values: " << fds;
;
break;
case 2:
//
// On EC2 called from jenkins stdin returns POLLHUP
//
EXPECT_EQ(expected_fds, fds)
<< "Value of fds is not one of expected values: " << fds;
break;
default:
FAIL();
}
callnum++;
}
};
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); });
EXPECT_EQ(2, callnum);
}
#endif
TEST(inspector, invalid_file_name)
{
sinsp inspector;
@ -1665,6 +1752,123 @@ TEST_F(sys_call_test, failing_execve)
EXPECT_EQ(2, callnum);
}
TEST_F(sys_call_test, large_execve)
{
const int buf_size = 100 * 1024;
const int driver_truncation_size = getpagesize();
const string non_existing_binary = "/non/existent";
const string existing_binary = "/bin/true";
int ctid;
int callnum = 0;
event_filter_t filter = [&](sinsp_evt* evt) { return evt->get_tid() == ctid; };
srandom(42);
string buf;
while (buf.length() < buf_size)
{
buf.append(std::to_string(random()));
}
run_callback_t test = [&](concurrent_object_handle<sinsp> inspector)
{
ctid = fork();
if (ctid < 0)
{
FAIL();
}
if (ctid == 0)
{
{
const char* eargv[] = {non_existing_binary.c_str(), buf.c_str(), NULL};
const char* eenvp[] = {buf.c_str(), NULL};
int ret = execve(eargv[0], (char* const*)eargv, (char* const*)eenvp);
ASSERT_TRUE(ret < 0);
}
{
const char* eargv[] = {existing_binary.c_str(), buf.c_str(), NULL};
const char* eenvp[] = {buf.c_str(), NULL};
int ret = execve(eargv[0], (char* const*)eargv, (char* const*)eenvp);
ASSERT_TRUE(ret == 0);
}
}
else
{
wait(NULL);
sleep(1);
}
};
captured_event_callback_t callback = [&](const callback_param& param)
{
sinsp_evt* e = param.m_evt;
uint16_t type = e->get_type();
if (type == PPME_SYSCALL_EXECVE_19_E || type == PPME_SYSCALL_EXECVE_18_E)
{
++callnum;
string filename = e->get_param_value_str("filename");
if (callnum == 1)
{
EXPECT_EQ(filename, non_existing_binary);
}
else if (callnum == 3)
{
EXPECT_EQ(filename, existing_binary);
}
else
{
FAIL();
}
}
else if (type == PPME_SYSCALL_EXECVE_19_X || type == PPME_SYSCALL_EXECVE_18_X)
{
++callnum;
string exe = e->get_param_value_str("exe");
string args = e->get_param_value_str("args");
if (callnum == 2)
{
// This is the failed execve. exe and
// args will be available, but env
// will not.
EXPECT_EQ(exe, non_existing_binary.c_str());
EXPECT_EQ(args,
buf.substr(0, driver_truncation_size - non_existing_binary.length() - 2) + ".");
}
else if (callnum == 4)
{
string env = e->get_param_value_str("env");
EXPECT_EQ(exe, existing_binary);
EXPECT_EQ(
args,
buf.substr(0, driver_truncation_size - existing_binary.length() - 2) + ".");
EXPECT_EQ(env, buf.substr(0, driver_truncation_size - 1) + ".");
}
else
{
FAIL();
}
}
};
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); });
EXPECT_EQ(4, callnum);
}
#ifdef __x86_64__
TEST_F(sys_call_test32, failing_execve)
@ -1771,4 +1975,537 @@ TEST_F(sys_call_test32, failing_execve)
EXPECT_EQ(10, callnum);
}
TEST_F(sys_call_test32, mmap)
{
int callnum = 0;
int errno2;
proc_started_filter ps_filter;
//
// FILTER
//
event_filter_t filter = [&](sinsp_evt* evt)
{
auto tinfo = evt->get_thread_info(false);
return tinfo && tinfo->m_comm == "test_helper_32"
&& ps_filter(evt);
};
uint64_t p = 0;
//
// TEST CODE
//
run_callback_t test = [&](concurrent_object_handle<sinsp> inspector_handle)
{
subprocess handle(LIBSINSP_TEST_PATH "/test_helper_32",
{"mmap_test",});
std::stringstream tmp;
handle.out();
tmp << handle.out();
errno2 = std::stoi(tmp.str());
tmp.clear();
tmp.str("");
tmp << handle.out();
p = (uint64_t)std::stoul(tmp.str());
tmp.clear();
tmp.str("");
handle.wait();
};
uint32_t enter_vmsize;
uint32_t enter_vmrss;
uint32_t exit_vmsize;
uint32_t exit_vmrss;
//
// OUTPUT VALDATION
//
captured_event_callback_t callback = [&](const callback_param& param)
{
sinsp_evt* e = param.m_evt;
uint16_t type = e->get_type();
if (type == PPME_SYSCALL_MUNMAP_E)
{
callnum++;
enter_vmsize = e->get_thread_info(false)->m_vmsize_kb;
enter_vmrss = e->get_thread_info(false)->m_vmrss_kb;
switch (callnum)
{
case 1:
EXPECT_EQ("50", e->get_param_value_str("addr"));
EXPECT_EQ("300", e->get_param_value_str("length"));
break;
case 7:
{
uint64_t addr = 0;
memcpy(&addr, e->get_param_by_name("addr")->m_val, sizeof(uint64_t));
#ifdef __LP64__
EXPECT_EQ((uint64_t)p, addr);
#else
EXPECT_EQ(((uint32_t)p), addr);
#endif
EXPECT_EQ("1003520", e->get_param_value_str("length"));
break;
}
default:
callnum--;
}
}
else if (type == PPME_SYSCALL_MUNMAP_X)
{
callnum++;
memcpy(&exit_vmsize, e->get_param_by_name("vm_size")->m_val, sizeof(uint32_t));
memcpy(&exit_vmrss, e->get_param_by_name("vm_rss")->m_val, sizeof(uint32_t));
EXPECT_EQ(e->get_thread_info(false)->m_vmsize_kb, exit_vmsize);
EXPECT_EQ(e->get_thread_info(false)->m_vmrss_kb, exit_vmrss);
switch (callnum)
{
case 2:
EXPECT_EQ("EINVAL", e->get_param_value_str("res"));
EXPECT_EQ("-22", e->get_param_value_str("res", false));
break;
case 8:
EXPECT_EQ("0", e->get_param_value_str("res"));
EXPECT_GT(enter_vmsize, exit_vmsize + 500);
EXPECT_GE(enter_vmrss, enter_vmrss);
break;
default:
callnum--;
}
}
else if (type == PPME_SYSCALL_MMAP_E || type == PPME_SYSCALL_MMAP2_E)
{
callnum++;
enter_vmsize = e->get_thread_info(false)->m_vmsize_kb;
enter_vmrss = e->get_thread_info(false)->m_vmrss_kb;
switch (callnum)
{
case 3:
EXPECT_EQ("0", e->get_param_value_str("addr"));
EXPECT_EQ("0", e->get_param_value_str("length"));
EXPECT_EQ("PROT_READ|PROT_WRITE|PROT_EXEC", e->get_param_value_str("prot"));
EXPECT_EQ("MAP_SHARED|MAP_PRIVATE|MAP_ANONYMOUS|MAP_DENYWRITE",
e->get_param_value_str("flags"));
EXPECT_EQ("-1", e->get_param_value_str("fd", false));
if (type == PPME_SYSCALL_MMAP_E)
{
EXPECT_EQ("0", e->get_param_value_str("offset"));
}
else
{
EXPECT_EQ("0", e->get_param_value_str("pgoffset"));
}
break;
case 5:
EXPECT_EQ("0", e->get_param_value_str("addr"));
EXPECT_EQ("1003520", e->get_param_value_str("length"));
EXPECT_EQ("PROT_READ|PROT_WRITE", e->get_param_value_str("prot"));
EXPECT_EQ("MAP_PRIVATE|MAP_ANONYMOUS", e->get_param_value_str("flags"));
EXPECT_EQ("-1", e->get_param_value_str("fd", false));
if (type == PPME_SYSCALL_MMAP_E)
{
EXPECT_EQ("0", e->get_param_value_str("offset"));
}
else
{
EXPECT_EQ("0", e->get_param_value_str("pgoffset"));
}
break;
default:
callnum--;
}
}
else if (type == PPME_SYSCALL_MMAP_X || type == PPME_SYSCALL_MMAP2_X)
{
callnum++;
memcpy(&exit_vmsize, e->get_param_by_name("vm_size")->m_val, sizeof(uint32_t));
memcpy(&exit_vmrss, e->get_param_by_name("vm_rss")->m_val, sizeof(uint32_t));
EXPECT_EQ(e->get_thread_info(false)->m_vmsize_kb, exit_vmsize);
EXPECT_EQ(e->get_thread_info(false)->m_vmrss_kb, exit_vmrss);
switch (callnum)
{
case 4:
{
uint64_t res = 0;
memcpy(&res, e->get_param_by_name("res")->m_val, sizeof(uint64_t));
EXPECT_EQ(-errno2, (int64_t)res);
break;
}
case 6:
{
uint64_t res = 0;
memcpy(&res, e->get_param_by_name("res")->m_val, sizeof(uint64_t));
EXPECT_EQ((uint64_t)p, res);
EXPECT_GT(exit_vmsize, enter_vmsize + 500);
EXPECT_GE(exit_vmrss, enter_vmrss);
break;
}
default:
callnum--;
}
}
};
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); });
EXPECT_EQ(8, callnum);
}
TEST_F(sys_call_test32, ppoll_timeout)
{
int callnum = 0;
event_filter_t filter = [&](sinsp_evt* evt)
{
auto tinfo = evt->get_thread_info(false);
return (evt->get_type() == PPME_SYSCALL_PPOLL_E ||
evt->get_type() == PPME_SYSCALL_PPOLL_X) &&
tinfo != nullptr && tinfo->m_comm == "test_helper_32";
};
std::string my_pipe[2];
run_callback_t test = [&](concurrent_object_handle<sinsp> inspector)
{
subprocess handle(LIBSINSP_TEST_PATH "/test_helper_32",
{"ppoll_timeout",});
std::stringstream ss;
ss << handle.out();
my_pipe[0] = ss.str();
ss.clear();
ss.str(" ");
ss << handle.out();
my_pipe[1] = ss.str();
ss.clear();
ss.str(" ");
handle.wait();
};
captured_event_callback_t callback = [&](const callback_param& param)
{
sinsp_evt* e = param.m_evt;
uint16_t type = e->get_type();
if (type == PPME_SYSCALL_PPOLL_E)
{
//
// stdin and stdout can be a file or a fifo depending
// on how the tests are invoked
//
std::string fds = e->get_param_value_str("fds");
std::string expected_fds = my_pipe[0] + ":p1 " +
my_pipe[1] + ":p4";
EXPECT_EQ(expected_fds, fds);
EXPECT_EQ("1000000", e->get_param_value_str("timeout", false));
EXPECT_EQ("SIGHUP SIGCHLD", e->get_param_value_str("sigmask", false));
callnum++;
}
else if (type == PPME_SYSCALL_PPOLL_X)
{
int64_t res = std::stol(e->get_param_value_str("res"));
EXPECT_GT(res, 0);
EXPECT_LE(res, 2);
string fds = e->get_param_value_str("fds");
std::string expected_fds = my_pipe[0] + ":p0 " +
my_pipe[1] + ":p4";
switch (res)
{
case 1:
EXPECT_EQ(expected_fds, fds);
break;
case 2:
//
// On EC2 called from jenkins stdin returns POLLHUP
//
EXPECT_EQ(expected_fds, fds);
break;
default:
FAIL();
}
callnum++;
}
};
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); });
EXPECT_EQ(2, callnum);
}
TEST_F(sys_call_test32, fs_preadv)
{
int callnum = 0;
int fd = 0;
int fd1 = 0;
bool pwritev64_succeeded;
bool pwritev64_succeeded2;
proc_started_filter test_started_filter;
//
// FILTER
//
event_filter_t filter = [&](sinsp_evt* evt)
{
auto tinfo = evt->get_thread_info(false);
if (tinfo && tinfo->m_comm == "test_helper_32")
{
auto type = evt->get_type();
return (type == PPME_SYSCALL_PREADV_E
|| type == PPME_SYSCALL_PREADV_X
|| type == PPME_SYSCALL_PWRITEV_E
|| type == PPME_SYSCALL_PWRITEV_X);
}
return false;
};
//
// TEST CODE
//
run_callback_t test = [&](concurrent_object_handle<sinsp> inspector)
{
subprocess test_proc(LIBSINSP_TEST_PATH "/test_helper_32", {"preadv_pwritev"});
fd = std::stoi(test_proc.out());
int bool_n = std::stoi(test_proc.out());
pwritev64_succeeded = (bool_n == 1);
bool_n = std::stoi(test_proc.out());
pwritev64_succeeded2 = (bool_n == 1);
fd1 = std::stoi(test_proc.out());
test_proc.wait();
};
int pwrite1_res = 0;
int pwrite2_res = 0;
//
// OUTPUT VALDATION
//
captured_event_callback_t callback = [&](const callback_param& param)
{
sinsp_evt* e = param.m_evt;
uint16_t type = e->get_type();
if (type == PPME_SYSCALL_PWRITEV_E)
{
if (callnum == 0)
{
EXPECT_EQ(fd, std::stoll(e->get_param_value_str("fd", false)));
EXPECT_EQ(987654321, std::stoll(e->get_param_value_str("pos")));
EXPECT_EQ(15, std::stoll(e->get_param_value_str("size")));
callnum++;
}
else
{
EXPECT_EQ(fd, std::stoll(e->get_param_value_str("fd", false)));
EXPECT_EQ(10, std::stoll(e->get_param_value_str("pos")));
EXPECT_EQ(15, std::stoll(e->get_param_value_str("size")));
callnum++;
}
}
else if (type == PPME_SYSCALL_PWRITEV_X)
{
if (callnum == 1)
{
pwrite1_res = std::stoi(e->get_param_value_str("res", false));
EXPECT_EQ("aaaaabbbbbccccc", e->get_param_value_str("data"));
callnum++;
}
else
{
pwrite2_res = std::stoi(e->get_param_value_str("res", false));
EXPECT_EQ("aaaaabbbbbccccc", e->get_param_value_str("data"));
callnum++;
}
}
else if (type == PPME_SYSCALL_PREADV_E)
{
if (callnum == 4)
{
EXPECT_EQ(fd1, std::stoll(e->get_param_value_str("fd", false)));
EXPECT_EQ(987654321, std::stoll(e->get_param_value_str("pos")));
callnum++;
}
else
{
EXPECT_EQ(fd1, std::stoll(e->get_param_value_str("fd", false)));
EXPECT_EQ(10, std::stoll(e->get_param_value_str("pos")));
callnum++;
}
}
else if (type == PPME_SYSCALL_PREADV_X)
{
if (callnum == 3)
{
EXPECT_EQ(15, std::stoi(e->get_param_value_str("res", false)));
EXPECT_EQ("aaaaabbbbb", e->get_param_value_str("data"));
EXPECT_EQ(30, std::stoll(e->get_param_value_str("size")));
callnum++;
}
}
};
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); });
EXPECT_EQ(6, callnum);
if (pwritev64_succeeded)
{
EXPECT_EQ(15, pwrite1_res);
}
else
{
EXPECT_GT(0, pwrite1_res);
}
if (pwritev64_succeeded2)
{
EXPECT_EQ(15, pwrite2_res);
}
else
{
EXPECT_EQ(-22, pwrite2_res);
}
}
#endif
extern "C"
{
int32_t scap_proc_read_thread(struct scap_linux_platform* linux_platform,
char* procdirname,
uint64_t tid,
struct scap_threadinfo* tinfo,
char* error,
bool scan_sockets);
}
TEST_F(sys_call_test, thread_lookup_static)
{
char err_buf[SCAP_LASTERR_SIZE];
scap_threadinfo scap_tinfo;
char proc[] = LIBSINSP_TEST_RESOURCES_PATH "/_proc";
struct stat s = {};
if (stat(proc, &s) != 0)
{
fprintf(stderr, "%s not found, skipping test\n", proc);
FAIL();
}
event_filter_t filter = [&](sinsp_evt* evt)
{ return evt->get_type() != PPME_PROCEXIT_1_E && evt->get_tid() > 0; };
run_callback_t test = [&](concurrent_object_handle<sinsp> inspector) {return;};
captured_event_callback_t callback = [&](const callback_param& param) {return;};
scap_linux_platform *platform;
before_close_t before_close = [&](sinsp* inspector)
{
platform = (scap_linux_platform*)inspector->get_scap_platform();
};
ASSERT_NO_FATAL_FAILURE(
{ event_capture::run(test, callback, filter, event_capture::do_nothing, before_close); });
ASSERT_EQ(SCAP_SUCCESS,
scap_proc_read_thread(platform, proc, 1, &scap_tinfo, err_buf, false));
EXPECT_EQ(1, scap_tinfo.tid);
EXPECT_EQ(1, scap_tinfo.pid);
EXPECT_EQ(1, scap_tinfo.vtid);
EXPECT_EQ(0, scap_tinfo.ptid);
ASSERT_EQ(SCAP_SUCCESS,
scap_proc_read_thread(platform, proc, 62725, &scap_tinfo, err_buf, false));
EXPECT_EQ(62725, scap_tinfo.tid);
EXPECT_EQ(62725, scap_tinfo.pid);
EXPECT_EQ(62725, scap_tinfo.vtid);
EXPECT_EQ(1, scap_tinfo.ptid);
ASSERT_EQ(SCAP_SUCCESS,
scap_proc_read_thread(platform, proc, 62727, &scap_tinfo, err_buf, false));
EXPECT_EQ(62727, scap_tinfo.tid);
EXPECT_EQ(62725, scap_tinfo.pid);
EXPECT_EQ(62727, scap_tinfo.vtid);
EXPECT_EQ(1, scap_tinfo.ptid);
}
TEST_F(sys_call_test, thread_lookup_live)
{
char err_buf[SCAP_LASTERR_SIZE];
scap_threadinfo scap_tinfo;
char proc[] = "/proc";
std::unordered_set<int64_t> seen_tids;
event_filter_t filter = [&](sinsp_evt* evt)
{ return evt->get_type() != PPME_PROCEXIT_1_E && evt->get_tid() > 0; };
run_callback_t test = [&](concurrent_object_handle<sinsp> inspector)
{
// a very short sleep to gather some events,
// we'll take much longer than this to process them all
usleep(1000);
};
captured_event_callback_t callback = [&](const callback_param& param)
{
sinsp_evt* e = param.m_evt;
auto tid = e->get_tid();
if (!seen_tids.insert(tid).second)
{
return;
}
fprintf(stderr, "looking up tid %ld in /proc\n", tid);
// In some cases scap_proc_read_thread can return SCAP_SUCCESS without
// filling in scap_tinfo
if (scap_proc_read_thread((scap_linux_platform*)param.m_inspector->get_scap_platform(),
proc, tid, &scap_tinfo, err_buf, false) == SCAP_SUCCESS)
{
auto tinfo = e->get_thread_info(false);
if (!tinfo)
{
return;
}
EXPECT_NE(0, scap_tinfo.tid);
EXPECT_NE(0, scap_tinfo.pid);
EXPECT_NE(0, scap_tinfo.vtid);
EXPECT_EQ(tinfo->m_tid, scap_tinfo.tid);
EXPECT_EQ(tinfo->m_pid, scap_tinfo.pid);
EXPECT_EQ(tinfo->m_vtid, scap_tinfo.vtid);
// Not testing scap_tinfo.ptid because it can change in between event and lookup
}
};
scap_linux_platform *platform;
before_close_t before_close = [&](sinsp* inspector)
{
// close scap to maintain the num_consumers at exit == 0 assertion
//close_capture(scap, platform);
platform = (scap_linux_platform*)inspector->get_scap_platform();
};
ASSERT_NO_FATAL_FAILURE(
{ event_capture::run(test, callback, filter, event_capture::do_nothing, before_close); });
ASSERT_EQ(SCAP_SUCCESS,
scap_proc_read_thread(platform, proc, getpid(),
&scap_tinfo, err_buf, false));
EXPECT_EQ(getpid(), scap_tinfo.tid);
EXPECT_EQ(getpid(), scap_tinfo.pid);
EXPECT_EQ(getpid(), scap_tinfo.vtid);
EXPECT_EQ(getppid(), scap_tinfo.ptid);
ASSERT_EQ(SCAP_SUCCESS,
scap_proc_read_thread(platform, proc, 1,
&scap_tinfo, err_buf, false));
EXPECT_EQ(1, scap_tinfo.tid);
EXPECT_EQ(1, scap_tinfo.pid);
EXPECT_EQ(1, scap_tinfo.vtid);
EXPECT_EQ(0, scap_tinfo.ptid);
}

View File

@ -159,8 +159,7 @@ void runtest(iotype iot,
if (evt->get_fd_info()->m_type != SCAP_FD_IPV4_SOCK)
{
//
// Skip non-tcp sockets. Python opens unix sockets
// to god knows what.
// Skip non-tcp sockets. Python opens unix sockets.
//
return;
}
@ -308,7 +307,10 @@ void runtest(iotype iot,
//
// OUTPUT VALDATION
//
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); });
ASSERT_NO_FATAL_FAILURE({event_capture::run(test, callback, filter, event_capture::do_nothing,
event_capture::do_nothing, event_capture::always_continue, 131072,
(uint64_t)60 * 1000 * 1000 * 1000, (uint64_t)60 * 1000 * 1000 * 1000,
SINSP_MODE_LIVE, 3, false); });
ASSERT_GT(callnum,0);
}
@ -406,7 +408,10 @@ TEST_F(sys_call_test, tcp_client_server_with_connection_before_capturing_starts)
server.wait_till_ready();
client.wait_till_ready();
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); });
ASSERT_NO_FATAL_FAILURE({event_capture::run(test, callback, filter, event_capture::do_nothing,
event_capture::do_nothing, event_capture::always_continue, 131072,
(uint64_t)60 * 1000 * 1000 * 1000, (uint64_t)60 * 1000 * 1000 * 1000,
SINSP_MODE_LIVE, 3, false); });
ASSERT_EQ(1, state);
}

View File

@ -670,7 +670,10 @@ void runtest_ipv4m(iotype iot,
}
};
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); });
ASSERT_NO_FATAL_FAILURE({event_capture::run(test, callback, filter, event_capture::do_nothing,
event_capture::do_nothing, event_capture::always_continue, 131072,
(uint64_t)60 * 1000 * 1000 * 1000, (uint64_t)60 * 1000 * 1000 * 1000,
SINSP_MODE_LIVE, 3, false); });
}
TEST_F(sys_call_test, tcp_client_server_ipv4m)

View File

@ -43,6 +43,7 @@ limitations under the License.
#include <poll.h>
#include <signal.h>
#include <stdarg.h>
#include <sys/quota.h>
#include <sys/wait.h>
@ -68,6 +69,10 @@ void mmap_test(const vector<string>& args)
{
int errno2;
void* p;
printf("STARTED\n");
fflush(stdout);
munmap((void*)0x50, 300);
p = mmap(0,
0,
@ -78,7 +83,10 @@ void mmap_test(const vector<string>& args)
errno2 = errno;
p = mmap(NULL, 1003520, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
munmap(p, 1003520);
cout << errno2 << " " << p << endl;
printf("%d\n", errno2);
fflush(stdout);
printf("%u\n", p);
fflush(stdout);
}
bool str_to_bool(const string& s)
@ -182,6 +190,8 @@ void preadv_pwritev(const vector<string>& args)
//
bool pwritev64_succeeded = bytes_sent > 0;
cout << fd << endl;
cout << (pwritev64_succeeded ? 1 : 0) << endl;
bytes_sent = pwritev(fd, wv, wv_count, 10);
@ -192,6 +202,8 @@ void preadv_pwritev(const vector<string>& args)
auto fd1 = open(FILENAME, O_CREAT | O_RDONLY, S_IRWXU);
cout << fd1 << endl;
wv[0].iov_len = sizeof(msg1);
wv[1].iov_len = sizeof(msg2);
wv[2].iov_len = sizeof(msg3);
@ -223,11 +235,12 @@ void quotactl_ok(const vector<string>& args)
{
struct dqblk mydqblk;
struct dqinfo mydqinfo;
std::string caddr = args[0] + "/aquota.user";
quotactl(QCMD(Q_QUOTAON, USRQUOTA),
"/dev/loop0",
args[1].c_str(),
2,
(caddr_t) "/tmp/testquotamnt/aquota.user"); // 2 => QFMT_VFS_V0
quotactl(QCMD(Q_GETQUOTA, USRQUOTA), "/dev/loop0", 0, (caddr_t)&mydqblk); // 0 => root user
(caddr_t)caddr.c_str()); // 2 => QFMT_VFS_V0
quotactl(QCMD(Q_GETQUOTA, USRQUOTA), args[1].c_str(), 0, (caddr_t)&mydqblk); // 0 => root user
fwrite(&mydqblk.dqb_bhardlimit, 1, sizeof(uint64_t), stdout);
fwrite(&mydqblk.dqb_bsoftlimit, 1, sizeof(uint64_t), stdout);
fwrite(&mydqblk.dqb_curspace, 1, sizeof(uint64_t), stdout);
@ -235,10 +248,33 @@ void quotactl_ok(const vector<string>& args)
fwrite(&mydqblk.dqb_isoftlimit, 1, sizeof(uint64_t), stdout);
fwrite(&mydqblk.dqb_btime, 1, sizeof(uint64_t), stdout);
fwrite(&mydqblk.dqb_itime, 1, sizeof(uint64_t), stdout);
quotactl(QCMD(Q_GETINFO, USRQUOTA), "/dev/loop0", 0, (caddr_t)&mydqinfo);
quotactl(QCMD(Q_GETINFO, USRQUOTA), args[1].c_str(), 0, (caddr_t)&mydqinfo);
fwrite(&mydqinfo.dqi_bgrace, 1, sizeof(uint64_t), stdout);
fwrite(&mydqinfo.dqi_igrace, 1, sizeof(uint64_t), stdout);
quotactl(QCMD(Q_QUOTAOFF, USRQUOTA), "/dev/loop0", 0, NULL);
quotactl(QCMD(Q_QUOTAOFF, USRQUOTA), args[1].c_str(), 0, NULL);
}
void poll_timeout(const vector<string>& args)
{
int my_pipe[2];
auto ret = pipe(my_pipe);
if (ret != 0)
{
return;
}
struct pollfd ufds[2];
ufds[0].fd = my_pipe[0];
ufds[0].events = POLLIN;
ufds[1].fd = my_pipe[1];
ufds[1].events = POLLOUT;
poll(ufds, 2, 20);
printf("%d\n", my_pipe[0]);
fflush(stdout);
printf("%d\n", my_pipe[1]);
fflush(stdout);
}
void ppoll_timeout(const vector<string>& args)
@ -265,6 +301,11 @@ void ppoll_timeout(const vector<string>& args)
sigaddset(&sigs, SIGHUP);
sigaddset(&sigs, SIGCHLD);
ppoll(ufds, 2, &timeout, &sigs);
printf("%d\n", my_pipe[0]);
fflush(stdout);
printf("%d\n", my_pipe[1]);
fflush(stdout);
}
void pgid_test(const vector<string>& args)
@ -710,6 +751,7 @@ const unordered_map<string, function<void(const vector<string>&)>> func_map = {
{"preadv_pwritev", preadv_pwritev},
{"quotactl_ko", quotactl_ko},
{"quotactl_ok", quotactl_ok},
{"poll_timeout", poll_timeout},
{"ppoll_timeout", ppoll_timeout},
{"pgid_test", pgid_test},
{"custom_container", custom_container},

View File

@ -0,0 +1,278 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright (C) 2024 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "event_capture.h"
#include "subprocess.h"
#include "sys_call_test.h"
#include <gtest/gtest.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <libsinsp/event.h>
#include <libsinsp/sinsp.h>
#include <libsinsp/sinsp_int.h>
#include <netinet/in.h>
#include <poll.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/un.h>
#include <algorithm>
#include <cassert>
#include <list>
#include <tuple>
#include <libsinsp/sinsp_int.h>
#define NAME "/tmp/python_unix_sockets_example"
#define PAYLOAD "0123456789QWERTYUIOPASDFGHJKLZXCVBNM"
#define PAYLOAD "0123456789QWERTYUIOPASDFGHJKLZXCVBNM"
#define BUFFER_LENGTH (sizeof(PAYLOAD) - 1)
#define FALSE 0
inline void parse_tuple(const std::string& tuple,
std::string& srcstr,
std::string& dststr, bool shift = false)
{
std::string token;
std::stringstream ss(tuple);
std::vector<std::string> tst;
int base = shift? 1 : 0;
while (std::getline(ss, token, '>')) {
tst.push_back(token);
}
int size = shift? 3 : 2;
EXPECT_EQ(size, (int)tst.size());
srcstr = tst[base].substr(0, tst[base].size() - 1);
dststr = tst[base+1];
}
inline bool ends_with(const std::string& value, const std::string& ending)
{
if (ending.size() > value.size()) return false;
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
}
TEST_F(sys_call_test, unix_client_server)
{
int32_t callnum = 0;
bool first_connect_or_accept_seen = true;
std::string sport;
std::string src_addr;
std::string dest_addr;
//
// FILTER
//
event_filter_t filter = [&](sinsp_evt* evt)
{
sinsp_threadinfo* ti = evt->get_thread_info(false);
if (ti)
{
if (ti->get_comm() == "python2" && ti->m_args.size() >= 1)
{
return ends_with(ti->m_args[0],"unix_client_server.py") ||
ends_with(ti->m_args[0],"unix_client_server.py");
}
}
return false;
};
//
// INITIALIZATION
//
run_callback_t test = [](concurrent_object_handle<sinsp> inspector)
{
subprocess server("python2", {LIBSINSP_TEST_RESOURCES_PATH "/unix_client_server.py", "server"});
server.wait_for_start();
subprocess client("python2", {LIBSINSP_TEST_RESOURCES_PATH "/unix_client_server.py", "client"});
server.wait();
client.wait();
};
//
// OUTPUT VALIDATION
//
captured_event_callback_t callback = [&](const callback_param& param)
{
sinsp_evt* evt = param.m_evt;
//std::cout << evt->get_name() << std::endl;
if (evt->get_type() == PPME_SOCKET_CONNECT_X)
{
std::string tuple = evt->get_param_value_str("tuple");
std::string addrs = tuple.substr(0, tuple.find(" "));
std::string file = tuple.substr(tuple.find(" ") + 1);
EXPECT_EQ(NAME, file);
std::string srcstr;
std::string dststr;
parse_tuple(tuple, srcstr, dststr);
EXPECT_NE("0000000000000000", srcstr);
EXPECT_NE("0000000000000000", dststr);
//
// connect() and accept() can return
// in a different order
//
if (first_connect_or_accept_seen)
{
first_connect_or_accept_seen = false;
src_addr = srcstr.substr(1);
dest_addr = dststr;
}
else
{
EXPECT_EQ(src_addr, srcstr.substr(1));
EXPECT_EQ(dest_addr, dststr);
}
callnum++;
}
else if ((evt->get_type() == PPME_SOCKET_ACCEPT_5_X) ||
(evt->get_type() == PPME_SOCKET_ACCEPT4_6_X))
{
std::string tuple = evt->get_param_value_str("tuple");
std::string addrs = tuple.substr(0, tuple.find(" "));
std::string file = tuple.substr(tuple.find(" ") + 1);
EXPECT_EQ(NAME, file);
std::string srcstr;
std::string dststr;
parse_tuple(tuple, srcstr, dststr);
EXPECT_NE("0000000000000000", srcstr);
EXPECT_NE("0000000000000000", dststr);
//
// connect() and accept() can return
// in a different order
//
if (first_connect_or_accept_seen)
{
first_connect_or_accept_seen = false;
src_addr = srcstr.substr(1);
dest_addr = dststr;
}
else
{
EXPECT_EQ(src_addr, srcstr.substr(1));
EXPECT_EQ(dest_addr, dststr);
}
std::string fdtuple = evt->get_param_value_str("tuple");
std::string fdaddrs = fdtuple.substr(0, fdtuple.find(" "));
std::string fdfile = fdtuple.substr(fdtuple.find(" ") + 1);
EXPECT_EQ(NAME, fdfile);
std::string fdsrcstr;
std::string fddststr;
parse_tuple(tuple, fdsrcstr, fddststr);
EXPECT_NE("0000000000000000", fdsrcstr);
EXPECT_NE("0000000000000000", fddststr);
callnum++;
}
if (callnum < 1)
{
return;
}
//
// 32bit (and s390x) uses send() and recv(), while 64bit
// uses sendto() and recvfrom() and sets the address to NULL
//
if (evt->get_type() == PPME_SOCKET_SEND_E || evt->get_type() == PPME_SOCKET_RECV_E ||
evt->get_type() == PPME_SOCKET_SENDTO_E || evt->get_type() == PPME_SOCKET_RECVFROM_E)
{
if (((evt->get_type() == PPME_SOCKET_RECVFROM_X) ||
(evt->get_type() == PPME_SOCKET_RECVFROM_X)) &&
(evt->get_param_value_str("tuple") != ""))
{
EXPECT_EQ("NULL", evt->get_param_value_str("tuple"));
}
std::string fdtuple = evt->get_param_value_str("fd");
std::string fdaddrs = fdtuple.substr(0, fdtuple.find(" "));
std::string fdfile = fdtuple.substr(fdtuple.find(" ") + 1);
EXPECT_EQ(NAME, fdfile);
std::string fdsrcstr;
std::string fddststr;
parse_tuple(fdtuple, fdsrcstr, fddststr, true);
EXPECT_NE("0", fdsrcstr);
EXPECT_NE("0", fddststr);
callnum++;
}
else if ((evt->get_type() == PPME_SOCKET_RECV_X) ||
(evt->get_type() == PPME_SOCKET_RECVFROM_X))
{
if (evt->get_type() == PPME_SOCKET_RECVFROM_X)
{
if (callnum == 5)
{
std::string tuple = evt->get_param_value_str("tuple");
std::string addrs = tuple.substr(0, tuple.find(" "));
std::string file = tuple.substr(tuple.find(" ") + 1);
EXPECT_EQ(NAME, file);
std::string srcstr;
std::string dststr;
parse_tuple(tuple, srcstr, dststr);
EXPECT_NE("0000000000000000", srcstr);
EXPECT_NE("0000000000000000", dststr);
}
}
EXPECT_EQ(PAYLOAD, evt->get_param_value_str("data"));
callnum++;
}
};
//
// OUTPUT VALDATION
//
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); });
EXPECT_FALSE(first_connect_or_accept_seen);
EXPECT_EQ(8, callnum);
}