diff --git a/.github/workflows/e2e_ci.yml b/.github/workflows/e2e_ci.yml index c663706377..98dfaa4146 100644 --- a/.github/workflows/e2e_ci.yml +++ b/.github/workflows/e2e_ci.yml @@ -117,6 +117,11 @@ jobs: driver: [ {name: kmod, option: -k}, {name: bpf, option: -b}, {name: modern-bpf, option: -m} ] fail-fast: false steps: + - name: Checkout Libs ⤵️ + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + - name: Restore build id: cache uses: actions/cache/restore@v3 @@ -129,13 +134,34 @@ jobs: # Asan in llvm 14 provided in ubuntu 22.04 is incompatible with # high-entropy ASLR in much newer kernels that GitHub runners are # using leading to random crashes: https://reviews.llvm.org/D148280 - run: sudo sysctl vm.mmap_rnd_bits=28 + run: sudo sysctl vm.mmap_rnd_bits=28 - name: Install multilib if: matrix.arch == 'amd64' run: | sudo apt install -y --no-install-recommends gcc-multilib g++-multilib + - name: Install deps + run: | + sudo apt install -y --no-install-recommends clang gcc llvm build-essential cmake + + - name: Install kernel headers (actuated) + uses: self-actuated/get-kernel-sources@master + if: matrix.arch == 'arm64' + + - name: Install kernel headers and gcc + if: matrix.arch == 'amd64' + run: | + sudo apt install -y --no-install-recommends linux-headers-$(uname -r) gcc-multilib g++-multilib + + # We have no guarantees that the kernel version is the same for the + # different workers, so we rebuild the drivers. + - name: Rebuild drivers + run: | + cd build + make -B driver bpf + cd .. + - name: Run e2e tests with ${{ matrix.driver.name }} 🏎️ if: matrix.arch == 'amd64' env: diff --git a/driver/SCHEMA_VERSION b/driver/SCHEMA_VERSION index 21be78bdbc..417faf2358 100644 --- a/driver/SCHEMA_VERSION +++ b/driver/SCHEMA_VERSION @@ -1 +1 @@ -2.19.5 +2.19.6 diff --git a/driver/bpf/fillers.h b/driver/bpf/fillers.h index 414a0fcfd3..352cabd320 100644 --- a/driver/bpf/fillers.h +++ b/driver/bpf/fillers.h @@ -2381,44 +2381,45 @@ FILLER(proc_startupdate, true) args_len = 0; } - if (args_len > 0) { - int exe_len; + int exe_len; - exe_len = bpf_probe_read_kernel_str(&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF], - SCRATCH_SIZE_HALF, - &data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF]); + exe_len = bpf_probe_read_kernel_str(&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF], + SCRATCH_SIZE_HALF, + &data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF]); - if (exe_len < 0) - return PPM_FAILURE_INVALID_USER_MEMORY; + if (exe_len < 0) + { + return PPM_FAILURE_INVALID_USER_MEMORY; + } - /* - * exe - */ + /* + * exe + */ + if (exe_len == 0) + { + res = bpf_push_empty_param(data); + } + else + { data->curarg_already_on_frame = true; res = __bpf_val_to_ring(data, 0, exe_len, PT_CHARBUF, -1, false, KERNEL); - CHECK_RES(res); + } + CHECK_RES(res); - args_len -= exe_len; - if (args_len < 0) - return PPM_FAILURE_INVALID_USER_MEMORY; + args_len -= exe_len; - /* - * Args - */ - data->curarg_already_on_frame = true; - res = __bpf_val_to_ring(data, 0, args_len, PT_BYTEBUF, -1, false, KERNEL); - CHECK_RES(res); - } else { - /* - * exe - */ + /* + * Args + */ + if(args_len <= 0) + { res = bpf_push_empty_param(data); CHECK_RES(res); - - /* - * Args - */ - res = bpf_push_empty_param(data); + } + else + { + data->curarg_already_on_frame = true; + res = __bpf_val_to_ring(data, 0, args_len, PT_BYTEBUF, -1, false, KERNEL); CHECK_RES(res); } diff --git a/test/libsinsp_e2e/CMakeLists.txt b/test/libsinsp_e2e/CMakeLists.txt index 67f3cd6788..22b44f096b 100755 --- a/test/libsinsp_e2e/CMakeLists.txt +++ b/test/libsinsp_e2e/CMakeLists.txt @@ -43,6 +43,7 @@ add_executable(libsinsp_e2e_tests paths.cpp process.cpp subprocess.cpp + suppress_events.cpp sys_call_test.cpp tcp_client_server.cpp tcp_client_server_ipv4_mapped.cpp @@ -93,7 +94,8 @@ if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") endif("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/test_helper.sh ${CMAKE_CURRENT_BINARY_DIR}/test_helper.sh COPYONLY + ${CMAKE_CURRENT_SOURCE_DIR}/test_helper.sh.in + ${CMAKE_CURRENT_BINARY_DIR}/test_helper.sh ) file(COPY diff --git a/test/libsinsp_e2e/event_capture.cpp b/test/libsinsp_e2e/event_capture.cpp index 6726d30cfd..98782e991f 100644 --- a/test/libsinsp_e2e/event_capture.cpp +++ b/test/libsinsp_e2e/event_capture.cpp @@ -66,7 +66,6 @@ void event_capture::init_inspector() "couldn't open inspector (maybe driver hasn't been loaded yet?) err=" + get_inspector()->getlasterr() + " exception=" + e.what(); { - //std::unique_lock lock(m_mutex); m_capture_started = true; m_condition_started.notify_one(); } @@ -88,7 +87,15 @@ void event_capture::capture() if(!inspector_ok) { init_inspector(); - inspector_ok = true; + if(!m_start_failed) + { + inspector_ok = true; + } + else + { + std::cerr << m_start_failure_message << std::endl; + return; + } } m_param.m_inspector = get_inspector(); diff --git a/test/libsinsp_e2e/forking.cpp b/test/libsinsp_e2e/forking.cpp index 5438db4bbf..3b59587b09 100644 --- a/test/libsinsp_e2e/forking.cpp +++ b/test/libsinsp_e2e/forking.cpp @@ -336,6 +336,7 @@ static int clone_callback_1(void* arg) // using a weird clone() here something goes wrong with // recent version of glibc ctid = syscall(SYS_getpid); + fsync(cp->fd); close(cp->fd); return 0; } @@ -346,6 +347,7 @@ TEST_F(sys_call_test, forking_clone_fs) char bcwd[1024]; int prfd; int ptid; // parent tid + pid_t clone_tid; int child_tid; int parent_res; int flags = CLONE_FILES | CLONE_FS | CLONE_VM | CLONE_PARENT_SETTID; @@ -394,7 +396,7 @@ TEST_F(sys_call_test, forking_clone_fs) /* Create child; child commences execution in childFunc() */ - pid_t clone_tid = clone(clone_callback_1, stackTop, flags, &cp, + clone_tid = clone(clone_callback_1, stackTop, flags, &cp, &child_tid); if (clone_tid == -1) FAIL(); @@ -477,7 +479,12 @@ TEST_F(sys_call_test, forking_clone_fs) sinsp_fdinfo* fdi = ti->get_fd(prfd); if(fdi && fdi->tostring_clean().find(FILENAME) != std::string::npos) { - EXPECT_EQ(parent_res, res); + EXPECT_EQ(parent_res, res) << "filename: " + << fdi->tostring_clean() << std::endl + << "res: " << res << std::endl + << "parent tid: " << ptid << std::endl + << "child tid: " << child_tid << std::endl + << "clone tid: " << clone_tid << std::endl; } } else if (ti->m_tid == child_tid) diff --git a/test/libsinsp_e2e/suppress_events.cpp b/test/libsinsp_e2e/suppress_events.cpp new file mode 100644 index 0000000000..c9af42179c --- /dev/null +++ b/test/libsinsp_e2e/suppress_events.cpp @@ -0,0 +1,377 @@ +#include "event_capture.h" +#include "sys_call_test.h" + +#include + +#include +#include +#include + +#include +#include +#include + +extern sinsp_evttables g_infotables; + +struct test_helper_args +{ + bool start_before; + bool suppress_before; + bool spawn_with_bash; +}; + +static void test_helper_quotactl(test_helper_args& hargs) +{ + // We start the test_helper process before starting the + // capture, so the initial proc scan will see the pid. Once + // the capture has started we let the test_helper process + // perform its work. + pid_t pid = getpid(); + bool test_helper_done = false; + std::string bin = LIBSINSP_TEST_PATH "/test_helper"; + + if (hargs.spawn_with_bash) + { + bin = LIBSINSP_TEST_PATH "/test_helper.sh"; + } + + subprocess test_proc(bin, {"threaded", "quotactl_ko"}, false); + + // + // Access/modify inspector before opening + // + + before_open_t before_open = [&](sinsp* inspector) + { + inspector->clear_suppress_events_comm(); + inspector->clear_suppress_events_tid(); + + if (hargs.suppress_before) + { + inspector->suppress_events_comm( + std::string((hargs.spawn_with_bash ? "test_helper.sh" : "test_helper"))); + } + + if (hargs.start_before) + { + test_proc.start(); + } + }; + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) { + return (evt->get_type() == PPME_SYSCALL_QUOTACTL_X || evt->get_type() == PPME_PROCEXIT_1_E); + }; + + // + // TEST CODE + // + run_callback_t test = [&](concurrent_object_handle inspector_handle) + { + if (!hargs.suppress_before) + { + std::scoped_lock inspector_handle_lock(inspector_handle); + inspector_handle->suppress_events_comm( + std::string((hargs.spawn_with_bash ? "test_helper.sh" : "test_helper"))); + } + + if (!hargs.start_before) + { + test_proc.start(); + } + + // Wait for it to finish + test_proc.wait(); + + // Do a quotactl--when the callback loop sees this, + // it's an indication that all the relevant events + // have been received. + quotactl(QCMD(Q_QUOTAOFF, GRPQUOTA), "/dev/xxx", 0, NULL); + }; + + // + // OUTPUT VALDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* evt = param.m_evt; + + // make sure we don't add suppresed threads during initial /proc scan + if (param.m_inspector->check_suppressed(evt->get_tid())) + { + ASSERT_EQ(nullptr, param.m_inspector->get_thread_ref(evt->get_tid(), false, true)); + } + + switch (evt->get_type()) + { + case PPME_SYSCALL_QUOTACTL_X: + if (evt->get_tid() != pid) + { + FAIL() << "Should not have observed any quotactl event"; + } + else + { + test_helper_done = true; + } + break; + case PPME_PROCEXIT_1_E: + ASSERT_FALSE(param.m_inspector->check_suppressed(evt->get_tid())); + break; + } + }; + + capture_continue_t should_continue = [&]() { return (!test_helper_done); }; + + before_close_t before_close = [](sinsp* inspector) + { + scap_stats st; + + inspector->get_capture_stats(&st); + + ASSERT_GT(st.n_suppressed, 0u); + ASSERT_EQ(0u, st.n_tids_suppressed); + + inspector->clear_suppress_events_comm(); + inspector->clear_suppress_events_tid(); + }; + + ASSERT_NO_FATAL_FAILURE({ + event_capture::run(test, + callback, + filter, + before_open, + before_close, + should_continue, + 131072, + 6000, + 6000, + SINSP_MODE_LIVE, + 1000); + }); +} + +TEST_F(sys_call_test, suppress_new_process) +{ + test_helper_args hargs; + hargs.start_before = false; + hargs.suppress_before = true; + hargs.spawn_with_bash = false; + + test_helper_quotactl(hargs); +} + +TEST_F(sys_call_test, suppress_add_new_value_while_running) +{ + test_helper_args hargs; + hargs.start_before = false; + hargs.suppress_before = false; + hargs.spawn_with_bash = false; + + test_helper_quotactl(hargs); +} + +TEST_F(sys_call_test, suppress_grandchildren) +{ + test_helper_args hargs; + hargs.start_before = false; + hargs.suppress_before = true; + hargs.spawn_with_bash = true; + + test_helper_quotactl(hargs); +} + +class suppress_types : public sys_call_test +{ +protected: + static bool is_target_call(uint16_t type); + void do_syscalls(); + bool is_suppressed_evttype(uint16_t evttype) const; + void run_test(std::vector supp_syscalls); + + std::vector m_suppressed_syscalls; + std::vector m_suppressed_evttypes; + int m_expected_calls; +}; + +bool suppress_types::is_target_call(uint16_t type) +{ + switch (type) + { + case PPME_SYSCALL_FCNTL_E: + case PPME_SYSCALL_FCNTL_X: + case PPME_SYSCALL_GETRLIMIT_E: + case PPME_SYSCALL_GETRLIMIT_X: + return true; + break; + } + return false; +} + +void suppress_types::do_syscalls() +{ + struct rlimit limits; + // getrlimit called directly because libc likes prlimit() + syscall(SYS_getrlimit, RLIMIT_AS, &limits); + fcntl(1, F_GETFD); + + // enter+exit for each syscall + m_expected_calls = 4; + for (const auto ii : m_suppressed_evttypes) + { + if (is_target_call(ii)) + { + m_expected_calls--; + } + } +} + +bool suppress_types::is_suppressed_evttype(uint16_t type) const +{ + for (const auto ii : m_suppressed_evttypes) + { + if (type == ii) + { + return true; + } + } + + return false; +} + +void parse_syscall_names(const std::vector& supp_strs, + std::vector& supp_ids) +{ + supp_ids.clear(); + + for (auto sc = 0; sc < PPM_SC_MAX; sc++) + { + const char* name = scap_get_ppm_sc_name(static_cast(sc)); + + auto iter = std::find(supp_strs.begin(), supp_strs.end(), std::string(name)); + if (iter != supp_strs.end()) + { + supp_ids.push_back(static_cast(sc)); + } + } +} + +const char* event_name_by_id(uint16_t id) +{ + if (id >= PPM_EVENT_MAX) + { + ASSERT(false); + return "NA"; + } + return g_infotables.m_event_info[id].name; +} + +void parse_suppressed_types(const std::vector& supp_strs, + std::vector* supp_ids) +{ + for (auto ii = 0; ii < PPM_EVENT_MAX; ii++) + { + auto iter = std::find(supp_strs.begin(), supp_strs.end(), event_name_by_id(ii)); + if (iter != supp_strs.end()) + { + supp_ids->push_back(static_cast(ii)); + } + } +} + +void suppress_types::run_test(std::vector supp_syscalls) +{ + int callnum = 0; + + parse_syscall_names(supp_syscalls, m_suppressed_syscalls); + parse_suppressed_types(supp_syscalls, &m_suppressed_evttypes); + + // + // TEST CODE + // + run_callback_t test = [&](concurrent_object_handle inspector_handle) + { + for (auto sc : m_suppressed_syscalls) + { + bool expect_exception = (sc >= PPM_SC_MAX); + bool caught_exception = false; + + try + { + std::scoped_lock inspector_handle_lock(inspector_handle); + inspector_handle->mark_ppm_sc_of_interest(sc, false); + } + catch (sinsp_exception& e) + { + caught_exception = true; + } + + ASSERT_EQ(expect_exception, caught_exception); + } + + do_syscalls(); + + for (auto sc : m_suppressed_syscalls) + { + bool expect_exception = (sc >= PPM_SC_MAX); + bool caught_exception = false; + + try + { + std::scoped_lock inspector_handle_lock(inspector_handle); + inspector_handle->mark_ppm_sc_of_interest(sc, true); + } + catch (sinsp_exception& e) + { + caught_exception = true; + } + + ASSERT_EQ(expect_exception, caught_exception); + } + }; + + // + // OUTPUT VALDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + auto type = param.m_evt->get_type(); + EXPECT_FALSE(is_suppressed_evttype(type)); + if (is_target_call(type)) + { + callnum++; + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, m_tid_filter); }); + EXPECT_EQ(m_expected_calls, callnum); +} + +TEST_F(suppress_types, block_getrlimit) +{ + // PPME_SYSCALL_GETRLIMIT_(E|X) + ASSERT_NO_FATAL_FAILURE(run_test({"getrlimit"})); +} + +TEST_F(suppress_types, block_fcntl) +{ + // PPME_SYSCALL_FCNTL_(E|X) + ASSERT_NO_FATAL_FAILURE(run_test({"fcntl"})); +} + +TEST_F(suppress_types, block_getrlimit_and_fcntl) +{ + // PPME_SYSCALL_GETRLIMIT_(E|X) && PPME_SYSCALL_FCNTL_(E|X) + ASSERT_NO_FATAL_FAILURE(run_test({"getrlimit", "fcntl"})); +} + +TEST_F(suppress_types, block_none) +{ + ASSERT_NO_FATAL_FAILURE(run_test({})); +} + +TEST_F(suppress_types, block_nonexistent_call) +{ + ASSERT_NO_FATAL_FAILURE(run_test({"notarealname"})); +} diff --git a/test/libsinsp_e2e/sys_call_test.cpp b/test/libsinsp_e2e/sys_call_test.cpp index 3b0c3621c3..2bdf22bb44 100644 --- a/test/libsinsp_e2e/sys_call_test.cpp +++ b/test/libsinsp_e2e/sys_call_test.cpp @@ -1007,6 +1007,11 @@ TEST_F(sys_call_test, getsetuid_and_gid) static const uint32_t test_gid = 6566; int callnum = 0; + uint32_t orig_uid = getuid(); + uint32_t orig_euid = geteuid(); + uint32_t orig_gid = getgid(); + uint32_t orig_egid = getegid(); + event_filter_t filter = [&](sinsp_evt* evt) { return m_tid_filter(evt); }; run_callback_t test = [&](concurrent_object_handle inspector_handle) @@ -1073,7 +1078,22 @@ TEST_F(sys_call_test, getsetuid_and_gid) break; } }; + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); + + // This has to be done without a callback otherwise the test will not + // work. + int result = 0; + result += setuid(orig_uid); + result += seteuid(orig_euid); + result += setgid(orig_gid); + result += setegid(orig_egid); + + if(result != 0) + { + FAIL() << "Cannot restore initial id state."; + } + EXPECT_EQ(12, callnum); } @@ -1377,3 +1397,378 @@ TEST_F(sys_call_test, sendmsg_recvmsg_SCM_RIGHTS) ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); EXPECT_EQ(1, callnum); } + +TEST_F(sys_call_test, ppoll_timeout) +{ + int callnum = 0; + event_filter_t filter = [&](sinsp_evt* evt) + { + auto ti = evt->get_thread_info(false); + return (evt->get_type() == PPME_SYSCALL_PPOLL_E || + evt->get_type() == PPME_SYSCALL_PPOLL_X) && + ti->m_comm == "test_helper"; + }; + + run_callback_t test = [&](concurrent_object_handle inspector) + { + subprocess handle(LIBSINSP_TEST_PATH "/test_helper", {"ppoll_timeout"}); + 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 + // + string fds = e->get_param_value_str("fds"); + EXPECT_TRUE(fds == "3:p1 4:p4" || fds == "4:p1 5:p4"); + 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 = stoi(e->get_param_value_str("res")); + + EXPECT_EQ(res, 1); + + string fds = e->get_param_value_str("fds"); + + EXPECT_TRUE(fds == "3:p0 4:p4" || fds == "4:p0 5:p4"); + + callnum++; + } + }; + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); + EXPECT_EQ(2, callnum); +} + +TEST_F(sys_call_test, getsetresuid_and_gid) +{ + static const uint32_t test_uid = 5454; + static const uint32_t test_gid = 6565; + int callnum = 0; + uint32_t uids[3]; + uint32_t gids[3]; + + uint32_t orig_uids[3]; + uint32_t orig_gids[3]; + + bool setresuid_e_ok = false; + bool setresgid_e_ok = false; + + bool getresuid_e_ok = false; + bool getresgid_e_ok = false; + + bool getresuid_ok = false; + bool getresgid_ok = false; + + bool setresuid_ok = false; + bool setresgid_ok = false; + + getresuid(&orig_uids[0], &orig_uids[1], &orig_uids[2]); + getresgid(&orig_gids[0], &orig_gids[1], &orig_gids[2]); + + // Clean environment + int ret = system("userdel testsetresuid"); + ret = system("groupdel testsetresgid"); + usleep(200); + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) + { + auto type = evt->get_type(); + auto tinfo = evt->get_thread_info(true); + return tinfo->m_comm != "sudo" && + (type == PPME_USER_ADDED_E || type == PPME_USER_ADDED_X || + type == PPME_GROUP_ADDED_E || type == PPME_GROUP_ADDED_X || + type == PPME_SYSCALL_GETRESUID_E || type == PPME_SYSCALL_GETRESUID_X || + type == PPME_SYSCALL_GETRESGID_E || type == PPME_SYSCALL_GETRESGID_X || + type == PPME_SYSCALL_SETRESUID_E || type == PPME_SYSCALL_SETRESUID_X || + type == PPME_SYSCALL_SETRESGID_E || type == PPME_SYSCALL_SETRESGID_X); }; + + // + // TEST CODE + // + run_callback_t test = [&](concurrent_object_handle inspector) + { + char command[] = "useradd -u 5454 testsetresuid && " + "groupadd -g 6565 testsetresgid && " + "sudo -u testsetresuid echo && " + "sudo -g testsetresgid echo"; + ret = system(command); + ASSERT_EQ(0, ret); + + auto res = setresuid(test_uid, -1, -1); + EXPECT_EQ(0, res); + res = setresgid(test_gid, -1, -1); + EXPECT_EQ(0, res); + getresuid(uids, uids + 1, uids + 2); + getresgid(gids, gids + 1, gids + 2); + }; + + // + // OUTPUT VALIDATION + // + 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_SETRESUID_E && e->get_param_value_str("ruid", false) != "-1" && !setresuid_e_ok) + { + ++callnum; + EXPECT_EQ("5454", e->get_param_value_str("ruid", false)); + EXPECT_EQ("testsetresuid", e->get_param_value_str("ruid")); + EXPECT_EQ("-1", e->get_param_value_str("euid", false)); + EXPECT_EQ("", e->get_param_value_str("euid")); + EXPECT_EQ("-1", e->get_param_value_str("suid", false)); + EXPECT_EQ("", e->get_param_value_str("suid")); + setresuid_e_ok = true; + } + else if (type == PPME_SYSCALL_SETRESUID_X && !setresuid_ok) + { + ++callnum; + EXPECT_EQ("0", e->get_param_value_str("res", false)); + setresuid_ok = true; + } + else if (type == PPME_SYSCALL_SETRESGID_E && e->get_param_value_str("rgid", false) != "-1" && !setresgid_e_ok) + { + ++callnum; + EXPECT_EQ("6565", e->get_param_value_str("rgid", false)); + EXPECT_EQ("testsetresgid", e->get_param_value_str("rgid")); + EXPECT_EQ("-1", e->get_param_value_str("egid", false)); + EXPECT_EQ("", e->get_param_value_str("egid")); + EXPECT_EQ("-1", e->get_param_value_str("sgid", false)); + EXPECT_EQ("", e->get_param_value_str("sgid")); + setresgid_e_ok = true; + } + else if (type == PPME_SYSCALL_SETRESGID_X && !setresgid_ok) + { + ++callnum; + EXPECT_EQ("0", e->get_param_value_str("res", false)); + setresgid_ok = true; + } + else if (type == PPME_SYSCALL_GETRESUID_E && !getresuid_e_ok) + { + ++callnum; + getresuid_e_ok = true; + } + else if (type == PPME_SYSCALL_GETRESGID_E && !getresgid_e_ok) + { + ++callnum; + getresgid_e_ok = true; + } + else if (type == PPME_SYSCALL_GETRESUID_X && !getresuid_ok) + { + ++callnum; + EXPECT_EQ("0", e->get_param_value_str("res", false)); + EXPECT_EQ("5454", e->get_param_value_str("ruid", false)); + EXPECT_EQ("testsetresuid", e->get_param_value_str("ruid")); + EXPECT_EQ("0", e->get_param_value_str("euid", false)); + EXPECT_EQ("root", e->get_param_value_str("euid")); + EXPECT_EQ("0", e->get_param_value_str("suid", false)); + EXPECT_EQ("root", e->get_param_value_str("suid")); + getresuid_ok = true; + } + else if (type == PPME_SYSCALL_GETRESGID_X && !getresgid_ok) + { + ++callnum; + EXPECT_EQ("0", e->get_param_value_str("res", false)); + EXPECT_EQ("6565", e->get_param_value_str("rgid", false)); + EXPECT_EQ("testsetresgid", e->get_param_value_str("rgid")); + EXPECT_EQ("0", e->get_param_value_str("egid", false)); + EXPECT_EQ("root", e->get_param_value_str("egid")); + EXPECT_EQ("0", e->get_param_value_str("sgid", false)); + EXPECT_EQ("root", e->get_param_value_str("sgid")); + getresgid_ok = true; + } + }; + + before_close_t cleanup = [&](sinsp* inspector) + { + int result = 0; + + result += setresuid(orig_uids[0], orig_uids[1], orig_uids[2]); + result += setresgid(orig_gids[0], orig_gids[1], orig_gids[2]); + + if(result != 0) + { + FAIL() << "Cannot restore initial id state."; + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter, event_capture::do_nothing, cleanup); }); + EXPECT_EQ(8, callnum); +} + +TEST_F(sys_call_test, failing_execve) +{ + int callnum = 0; + + event_filter_t filter = [&](sinsp_evt* evt) { return m_tid_filter(evt); }; + + const char* eargv[] = {"/non/existent", "arg0", "arg1", "", "arg3", NULL}; + + const char* eenvp[] = {"env0", "env1", "", "env3", NULL}; + + // + // Touch the memory so it won't generate a PF in the driver + // + printf("%s %s %s %s %s\n", eargv[0], eargv[1], eargv[2], eargv[3], eargv[4]); + printf("%s %s %s %s\n", eenvp[0], eenvp[1], eenvp[2], eenvp[3]); + + run_callback_t test = [&](concurrent_object_handle inspector) + { + int ret = execve(eargv[0], (char* const*)eargv, (char* const*)eenvp); + ASSERT_TRUE(ret < 0); + }; + + 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"); + EXPECT_EQ(filename, eargv[0]); + } + else if (type == PPME_SYSCALL_EXECVE_19_X || type == PPME_SYSCALL_EXECVE_18_X) + { + ++callnum; + + string res = e->get_param_value_str("res"); + EXPECT_EQ(res, "ENOENT"); + + string exe = e->get_param_value_str("exe"); + EXPECT_EQ(exe, eargv[0]); + + string args = e->get_param_value_str("args"); + EXPECT_EQ(args, "arg0.arg1..arg3."); + + string env = e->get_param_value_str("env"); + EXPECT_EQ(env, "env0.env1..env3."); + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); + EXPECT_EQ(2, callnum); +} + +#ifdef __x86_64__ + +TEST_F(sys_call_test32, failing_execve) +{ + int callnum = 0; + + // INIT FILTER + std::unique_ptr is_subprocess_execve; + before_open_t before_open = [&](sinsp* inspector) + { + sinsp_filter_compiler compiler(inspector, + "evt.type=execve and proc.apid=" + std::to_string(getpid())); + is_subprocess_execve.reset(compiler.compile().release()); + }; + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) { return is_subprocess_execve->run(evt); }; + + // + // TEST CODE + // + run_callback_t test = [&](concurrent_object_handle inspector) + { + auto ret = system(LIBSINSP_TEST_RESOURCES_PATH "execve32_fail"); + ASSERT_TRUE(ret > 0); + ret = system(LIBSINSP_TEST_RESOURCES_PATH "execve32 ./fail"); + ASSERT_TRUE(ret > 0); + }; + + // + // OUTPUT VALIDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + auto tinfo = e->get_thread_info(true); + if (type == PPME_SYSCALL_EXECVE_19_E || type == PPME_SYSCALL_EXECVE_18_E || + type == PPME_SYSCALL_EXECVE_17_E) + { + ++callnum; + switch (callnum) + { + case 1: + EXPECT_EQ(tinfo->m_comm, "libsinsp_e2e_te"); + break; + case 3: + EXPECT_EQ(tinfo->m_comm, "sh"); + break; + case 5: + EXPECT_EQ(tinfo->m_comm, "libsinsp_e2e_te"); + break; + case 7: + EXPECT_EQ(tinfo->m_comm, "sh"); + break; + case 9: + EXPECT_EQ(tinfo->m_comm, "execve32"); + break; + default: + FAIL() << "Wrong execve entry callnum (" << callnum << ")"; + } + } + else if (type == PPME_SYSCALL_EXECVE_19_X || type == PPME_SYSCALL_EXECVE_18_X || + type == PPME_SYSCALL_EXECVE_17_X) + { + ++callnum; + + auto res = e->get_param_value_str("res", false); + auto comm = e->get_param_value_str("comm", false); + auto exe = e->get_param_value_str("exe", false); + switch (callnum) + { + case 2: + EXPECT_EQ("0", res); + EXPECT_EQ(comm, "sh"); + break; + case 4: + EXPECT_EQ("-2", res); + EXPECT_EQ(comm, "sh"); + EXPECT_EQ(exe, LIBSINSP_TEST_RESOURCES_PATH "execve32_fail"); + break; + case 6: + EXPECT_EQ("0", res); + EXPECT_EQ(comm, "sh"); + break; + case 8: + EXPECT_EQ("0", res); + EXPECT_EQ(comm, "execve32"); + EXPECT_EQ(exe, LIBSINSP_TEST_RESOURCES_PATH "execve32"); + break; + case 10: + EXPECT_EQ("-2", res); + EXPECT_EQ(comm, "execve32"); + EXPECT_EQ(exe, "./fail"); + break; + default: + FAIL() << "Wrong execve exit callnum (" << callnum << ")"; + } + } + }; + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter, before_open); }); + EXPECT_EQ(10, callnum); +} + +#endif diff --git a/test/libsinsp_e2e/test_helper.sh b/test/libsinsp_e2e/test_helper.sh deleted file mode 100755 index 26d5ae466f..0000000000 --- a/test/libsinsp_e2e/test_helper.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -./test_helper "$@" diff --git a/test/libsinsp_e2e/test_helper.sh.in b/test/libsinsp_e2e/test_helper.sh.in new file mode 100755 index 0000000000..d5b6e26f19 --- /dev/null +++ b/test/libsinsp_e2e/test_helper.sh.in @@ -0,0 +1,3 @@ +#!/bin/bash + +"${CMAKE_BINARY_DIR}/test/libsinsp_e2e/test_helper" "$@" diff --git a/userspace/libsinsp/sinsp.cpp b/userspace/libsinsp/sinsp.cpp index 92d2381a58..7fab81e416 100644 --- a/userspace/libsinsp/sinsp.cpp +++ b/userspace/libsinsp/sinsp.cpp @@ -1424,6 +1424,16 @@ bool sinsp::suppress_events_tid(int64_t tid) return true; } +void sinsp::clear_suppress_events_comm() +{ + m_suppress.clear_suppress_comm(); +} + +void sinsp::clear_suppress_events_tid() +{ + m_suppress.clear_suppress_tid(); +} + bool sinsp::check_suppressed(int64_t tid) const { return m_suppress.is_suppressed_tid(tid); diff --git a/userspace/libsinsp/sinsp.h b/userspace/libsinsp/sinsp.h index 794a16438a..6df7276a2e 100644 --- a/userspace/libsinsp/sinsp.h +++ b/userspace/libsinsp/sinsp.h @@ -912,6 +912,10 @@ class SINSP_PUBLIC sinsp : public capture_stats_source bool suppress_events_tid(int64_t tid); + void clear_suppress_events_comm(); + + void clear_suppress_events_tid(); + bool check_suppressed(int64_t tid) const; void set_docker_socket_path(std::string socket_path); diff --git a/userspace/libsinsp/sinsp_suppress.cpp b/userspace/libsinsp/sinsp_suppress.cpp index 63c8451f5d..763c41ff05 100644 --- a/userspace/libsinsp/sinsp_suppress.cpp +++ b/userspace/libsinsp/sinsp_suppress.cpp @@ -15,8 +15,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -#include +#include + +#include #include #include #include @@ -32,6 +34,16 @@ void libsinsp::sinsp_suppress::suppress_tid(uint64_t tid) m_suppressed_tids.emplace(tid); } +void libsinsp::sinsp_suppress::clear_suppress_comm() +{ + m_suppressed_comms.clear(); +} + +void libsinsp::sinsp_suppress::clear_suppress_tid() +{ + m_suppressed_tids.clear(); +} + bool libsinsp::sinsp_suppress::check_suppressed_comm(uint64_t tid, const std::string &comm) { if(m_suppressed_comms.find(comm) != m_suppressed_comms.end()) @@ -55,6 +67,9 @@ int32_t libsinsp::sinsp_suppress::process_event(scap_evt *e) // we need to check the comm, which might also update the set // of suppressed tids. + uint64_t tid; + memcpy(&tid, &e->tid, sizeof(uint64_t)); + switch(e->type) { case PPME_SYSCALL_CLONE_20_X: @@ -66,10 +81,11 @@ int32_t libsinsp::sinsp_suppress::process_event(scap_evt *e) { uint32_t j; const char *comm = nullptr; - uint64_t *ptid = nullptr; + uint64_t *ptid_ptr = nullptr; auto *lens = (uint16_t *)((char *)e + sizeof(ppm_evt_hdr)); char *valptr = (char *)lens + e->nparams * sizeof(uint16_t); + uint16_t scratch = 0; ASSERT(e->nparams >= 14); if(e->nparams < 14) @@ -85,14 +101,15 @@ int32_t libsinsp::sinsp_suppress::process_event(scap_evt *e) { if(j == 5) { - ptid = (uint64_t *)valptr; + ptid_ptr = (uint64_t *)valptr; } - valptr += lens[j]; + memcpy(&scratch, &lens[j], sizeof(uint16_t)); + valptr += scratch; } - ASSERT(ptid != nullptr); - if(ptid == nullptr) + ASSERT(ptid_ptr != nullptr); + if(ptid_ptr == nullptr) { // SCAP_SUCCESS means "do not suppress this event" return SCAP_SUCCESS; @@ -100,14 +117,16 @@ int32_t libsinsp::sinsp_suppress::process_event(scap_evt *e) comm = valptr; - if(is_suppressed_tid(*ptid)) + uint64_t ptid; + memcpy(&ptid, ptid_ptr, sizeof(uint64_t)); + if(is_suppressed_tid(ptid)) { - m_suppressed_tids.insert(e->tid); + m_suppressed_tids.insert(tid); m_num_suppressed_events++; return SCAP_FILTERED_EVENT; } - if(check_suppressed_comm(e->tid, comm)) + if(check_suppressed_comm(tid, comm)) { return SCAP_FILTERED_EVENT; } @@ -116,7 +135,7 @@ int32_t libsinsp::sinsp_suppress::process_event(scap_evt *e) } case PPME_PROCEXIT_1_E: { - auto it = m_suppressed_tids.find(e->tid); + auto it = m_suppressed_tids.find(tid); if (it != m_suppressed_tids.end()) { m_suppressed_tids.erase(it); @@ -130,7 +149,7 @@ int32_t libsinsp::sinsp_suppress::process_event(scap_evt *e) } default: - if (is_suppressed_tid(e->tid)) + if (is_suppressed_tid(tid)) { m_num_suppressed_events++; return SCAP_FILTERED_EVENT; diff --git a/userspace/libsinsp/sinsp_suppress.h b/userspace/libsinsp/sinsp_suppress.h index 5f1526392b..f5df45cff3 100644 --- a/userspace/libsinsp/sinsp_suppress.h +++ b/userspace/libsinsp/sinsp_suppress.h @@ -36,6 +36,10 @@ class sinsp_suppress void suppress_tid(uint64_t tid); + void clear_suppress_comm(); + + void clear_suppress_tid(); + bool check_suppressed_comm(uint64_t tid, const std::string& comm); int32_t process_event(scap_evt* e); @@ -53,4 +57,4 @@ class sinsp_suppress uint64_t m_num_suppressed_events = 0; }; -} \ No newline at end of file +}