Compare commits
28 Commits
Author | SHA1 | Date |
---|---|---|
|
c7276a354a | |
|
351acf09eb | |
|
27f484fcc7 | |
|
79bb161cd2 | |
|
6be9def07e | |
|
6d05cc5e8d | |
|
f4670cffea | |
|
4c9dcc0a0e | |
|
e1a2db6b78 | |
|
e4d4263a3d | |
|
9c6b325e84 | |
|
c8985e2289 | |
|
ec11f29c1c | |
|
b7e51eb669 | |
|
105659ad1b | |
|
d477a05906 | |
|
a9e4072d67 | |
|
9a5b7dc5fe | |
|
9248c1695e | |
|
ceadfcc7cd | |
|
32aa75e9f3 | |
|
d921af231d | |
|
9a6bd05498 | |
|
18dc06410b | |
|
e1765a9e89 | |
|
adbdec31b8 | |
|
019b361959 | |
|
f7a5a50a91 |
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -1 +1 @@
|
|||
2.20.0
|
||||
2.20.1
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
pytest==7.1.1
|
||||
pytest-html==3.1.1
|
||||
docker==6.0.0
|
||||
requests==2.31.0
|
||||
|
|
|
@ -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/
|
||||
)
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
|
@ -0,0 +1,3 @@
|
|||
FROM busybox
|
||||
RUN cp /bin/true /bin/ut-health-check
|
||||
HEALTHCHECK --interval=0.5s CMD ["/bin/ut-health-check"]
|
|
@ -0,0 +1,2 @@
|
|||
FROM busybox
|
||||
HEALTHCHECK --interval=0.5s CMD ["/bin/sh", "-c", "/bin/sleep 10"]
|
|
@ -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"
|
|
@ -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"
|
|
@ -0,0 +1,3 @@
|
|||
FROM busybox
|
||||
RUN cp /bin/true /bin/ut-health-check
|
||||
HEALTHCHECK --interval=0.5s CMD /bin/ut-health-check
|
|
@ -0,0 +1 @@
|
|||
FROM busybox:latest
|
|
@ -0,0 +1,2 @@
|
|||
FROM busybox:latest
|
||||
HEALTHCHECK NONE
|
Binary file not shown.
|
@ -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!"
|
|
@ -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++;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue