From 4e8db3ddc20403bff0c02b55fa122772b82cdeab Mon Sep 17 00:00:00 2001 From: Andrea Terzolo Date: Sat, 14 Dec 2024 15:24:53 +0100 Subject: [PATCH] tests: add test for exit events Signed-off-by: Andrea Terzolo --- userspace/libsinsp/test/parsers/parse_brk.cpp | 84 +++++++++++++ .../libsinsp/test/parsers/parse_pread.cpp | 88 ++++++++++++++ .../libsinsp/test/parsers/parse_read.cpp | 84 +++++++++++++ .../libsinsp/test/sinsp_with_test_input.cpp | 112 ++++++++++++++++++ .../libsinsp/test/sinsp_with_test_input.h | 16 ++- 5 files changed, 382 insertions(+), 2 deletions(-) create mode 100644 userspace/libsinsp/test/parsers/parse_brk.cpp create mode 100644 userspace/libsinsp/test/parsers/parse_pread.cpp create mode 100644 userspace/libsinsp/test/parsers/parse_read.cpp diff --git a/userspace/libsinsp/test/parsers/parse_brk.cpp b/userspace/libsinsp/test/parsers/parse_brk.cpp new file mode 100644 index 0000000000..fcd3dc6182 --- /dev/null +++ b/userspace/libsinsp/test/parsers/parse_brk.cpp @@ -0,0 +1,84 @@ +// 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 + +TEST_F(sinsp_with_test_input, parse_brk_updated_prog_break) { + add_default_init_thread(); + open_inspector(); + + // if the program break is updated the res should be equal to `addr` + uint64_t res = 83983092; + uint32_t vm_size = 294; + uint32_t vm_rss = 295; + uint32_t vm_swap = 296; + + auto evt = add_event_advance_ts(increasing_ts(), + INIT_TID, + PPME_SYSCALL_BRK_4_X, + 4, + res, + vm_size, + vm_rss, + vm_swap); + + auto init_tinfo = m_inspector.get_thread_ref(INIT_TID, false, true).get(); + ASSERT_TRUE(init_tinfo); + ASSERT_EQ(init_tinfo->m_vmsize_kb, vm_size); + ASSERT_EQ(init_tinfo->m_vmrss_kb, vm_rss); + ASSERT_EQ(init_tinfo->m_vmswap_kb, vm_swap); + + assert_return_value(evt, res); + + ASSERT_EQ(get_field_as_string(evt, "evt.arg[1]"), std::to_string(vm_size)); + ASSERT_EQ(get_field_as_string(evt, "evt.rawarg.vm_size"), std::to_string(vm_size)); + + ASSERT_EQ(get_field_as_string(evt, "evt.arg[2]"), std::to_string(vm_rss)); + ASSERT_EQ(get_field_as_string(evt, "evt.rawarg.vm_rss"), std::to_string(vm_rss)); + + ASSERT_EQ(get_field_as_string(evt, "evt.arg[3]"), std::to_string(vm_swap)); + ASSERT_EQ(get_field_as_string(evt, "evt.rawarg.vm_swap"), std::to_string(vm_swap)); +} + +TEST_F(sinsp_with_test_input, parse_brk_no_update) { + add_default_init_thread(); + open_inspector(); + + // if the program break is different from `addr`. + uint64_t res = 83983090; + uint32_t vm_size = 294; + uint32_t vm_rss = 295; + uint32_t vm_swap = 296; + + auto evt = add_event_advance_ts(increasing_ts(), + INIT_TID, + PPME_SYSCALL_BRK_4_X, + 4, + res, + vm_size, + vm_rss, + vm_swap); + + auto init_tinfo = m_inspector.get_thread_ref(INIT_TID, false, true).get(); + ASSERT_TRUE(init_tinfo); + // We should always update the info + ASSERT_EQ(init_tinfo->m_vmsize_kb, vm_size); + ASSERT_EQ(init_tinfo->m_vmrss_kb, vm_rss); + ASSERT_EQ(init_tinfo->m_vmswap_kb, vm_swap); + + // BRK can update or not update the program break according to the value we provide. Today we + // don't consider a failure if the program break in not updated, we consider a failure only if + // the syscall sets an errno. + assert_return_value(evt, res); +} diff --git a/userspace/libsinsp/test/parsers/parse_pread.cpp b/userspace/libsinsp/test/parsers/parse_pread.cpp new file mode 100644 index 0000000000..211c75cc4f --- /dev/null +++ b/userspace/libsinsp/test/parsers/parse_pread.cpp @@ -0,0 +1,88 @@ + +// 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 + +TEST_F(sinsp_with_test_input, parse_pread_success) { + add_default_init_thread(); + open_inspector(); + + auto evt = generate_open_x_event(); + ASSERT_TRUE(evt->get_fd_info()); + + std::string data = "hello"; + uint32_t size = data.size(); + uint64_t pos = 0; + evt = add_event_advance_ts(increasing_ts(), + INIT_TID, + PPME_SYSCALL_PREAD_X, + 5, + (int64_t)size, + scap_const_sized_buffer{data.c_str(), size}, + sinsp_test_input::open_params::default_fd, + size, + pos); + + ASSERT_TRUE(evt->get_fd_info()); + assert_fd_fields(evt, + sinsp_test_input::fd_info_fields{ + .fd_num = sinsp_test_input::open_params::default_fd, + .fd_name = sinsp_test_input::open_params::default_path, + .fd_name_raw = sinsp_test_input::open_params::default_path, + .fd_directory = sinsp_test_input::open_params::default_directory, + .fd_filename = sinsp_test_input::open_params::default_filename}); + + assert_return_value(evt, size); + + ASSERT_EQ(get_field_as_string(evt, "evt.arg[1]"), data); + ASSERT_EQ(get_field_as_string(evt, "evt.rawarg.data"), data); + + ASSERT_EQ(get_field_as_string(evt, "evt.arg[2]"), + std::string("") + sinsp_test_input::open_params::default_path); + ASSERT_EQ(get_field_as_string(evt, "evt.rawarg.fd"), + std::to_string(sinsp_test_input::open_params::default_fd)); + + ASSERT_EQ(get_field_as_string(evt, "evt.arg[3]"), std::to_string(size)); + ASSERT_EQ(get_field_as_string(evt, "evt.rawarg.size"), std::to_string(size)); +} + +TEST_F(sinsp_with_test_input, parse_pread_failure) { + add_default_init_thread(); + open_inspector(); + + auto evt = generate_open_x_event(); + + std::string data = "hello"; + uint32_t size = data.size(); + int64_t errno_code = -3; + uint64_t pos = 0; + evt = add_event_advance_ts(increasing_ts(), + INIT_TID, + PPME_SYSCALL_PREAD_X, + 5, + errno_code, + scap_const_sized_buffer{data.c_str(), size}, + sinsp_test_input::open_params::default_fd, + size, + pos); + + // Check we have the correct fd info associated with the event + auto fdinfo = evt->get_fd_info(); + ASSERT_TRUE(fdinfo); + ASSERT_EQ(fdinfo->m_fd, sinsp_test_input::open_params::default_fd); + + // Assert return value filterchecks + assert_return_value(evt, errno_code); +} diff --git a/userspace/libsinsp/test/parsers/parse_read.cpp b/userspace/libsinsp/test/parsers/parse_read.cpp new file mode 100644 index 0000000000..3d5ed2da31 --- /dev/null +++ b/userspace/libsinsp/test/parsers/parse_read.cpp @@ -0,0 +1,84 @@ + +// 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 + +TEST_F(sinsp_with_test_input, parse_read_success) { + add_default_init_thread(); + open_inspector(); + + auto evt = generate_open_x_event(); + ASSERT_TRUE(evt->get_fd_info()); + + std::string data = "hello"; + uint32_t size = data.size(); + evt = add_event_advance_ts(increasing_ts(), + INIT_TID, + PPME_SYSCALL_READ_X, + 4, + (int64_t)size, + scap_const_sized_buffer{data.c_str(), size}, + sinsp_test_input::open_params::default_fd, + size); + + ASSERT_TRUE(evt->get_fd_info()); + assert_fd_fields(evt, + sinsp_test_input::fd_info_fields{ + .fd_num = sinsp_test_input::open_params::default_fd, + .fd_name = sinsp_test_input::open_params::default_path, + .fd_name_raw = sinsp_test_input::open_params::default_path, + .fd_directory = sinsp_test_input::open_params::default_directory, + .fd_filename = sinsp_test_input::open_params::default_filename}); + + assert_return_value(evt, size); + + ASSERT_EQ(get_field_as_string(evt, "evt.arg[1]"), data); + ASSERT_EQ(get_field_as_string(evt, "evt.rawarg.data"), data); + + ASSERT_EQ(get_field_as_string(evt, "evt.arg[2]"), + std::string("") + sinsp_test_input::open_params::default_path); + ASSERT_EQ(get_field_as_string(evt, "evt.rawarg.fd"), + std::to_string(sinsp_test_input::open_params::default_fd)); + + ASSERT_EQ(get_field_as_string(evt, "evt.arg[3]"), std::to_string(size)); + ASSERT_EQ(get_field_as_string(evt, "evt.rawarg.size"), std::to_string(size)); +} + +TEST_F(sinsp_with_test_input, parse_read_failure) { + add_default_init_thread(); + open_inspector(); + + auto evt = generate_open_x_event(); + + std::string data = "hello"; + uint32_t size = data.size(); + int64_t errno_code = -3; + evt = add_event_advance_ts(increasing_ts(), + INIT_TID, + PPME_SYSCALL_READ_X, + 4, + errno_code, + scap_const_sized_buffer{data.c_str(), size}, + sinsp_test_input::open_params::default_fd, + size); + + // Check we have the correct fd info associated with the event + auto fdinfo = evt->get_fd_info(); + ASSERT_TRUE(fdinfo); + ASSERT_EQ(fdinfo->m_fd, sinsp_test_input::open_params::default_fd); + + // Assert return value filterchecks + assert_return_value(evt, errno_code); +} diff --git a/userspace/libsinsp/test/sinsp_with_test_input.cpp b/userspace/libsinsp/test/sinsp_with_test_input.cpp index e6694a132e..e997b5265e 100644 --- a/userspace/libsinsp/test/sinsp_with_test_input.cpp +++ b/userspace/libsinsp/test/sinsp_with_test_input.cpp @@ -693,3 +693,115 @@ sinsp_evt* sinsp_with_test_input::next_event() { auto result = m_inspector.next(&evt); return result == SCAP_SUCCESS ? evt : nullptr; } + +void sinsp_with_test_input::assert_return_value(sinsp_evt* evt, int64_t expected_retval) { + // First the event we provide should have the return value + ASSERT_TRUE(evt->has_return_value()); + + // The raw value should be equal to what we expect + ASSERT_EQ(evt->get_syscall_return_value(), expected_retval); + + // We need to create the filtercheck name for the first parameter, usually `res`/`fd` but not + // always, could be also `addr` for example... + std::string evt_rawarg_name = std::string("evt.rawarg.") + std::string(evt->get_param_name(0)); + + // SUCCESS CASE: we consider success when retval >= 0 (so != errno) + if(expected_retval >= 0) { + ///////////// + // Res + ///////////// + ASSERT_EQ(get_field_as_string(evt, "evt.res"), "SUCCESS"); + ASSERT_EQ(get_field_as_string(evt, "evt.rawres"), std::to_string(expected_retval)); + ASSERT_EQ(get_field_as_string(evt, "evt.failed"), "false"); + + ///////////// + // First parameter (arg0) + ///////////// + auto arg0_info = evt->get_param_info(0); + ASSERT_TRUE(arg0_info); + // Default values + std::string arg0 = std::to_string(expected_retval); + std::string raw_arg0 = std::to_string(expected_retval); + + // Some cases in which we need to modify `arg0` or `raw_arg0` + if(arg0_info->type == PT_FD) { + // If the return value is a file descriptor the value of `evt.arg[0]` is the path + // prefixed by `` + auto fdinfo = evt->get_fd_info(); + ASSERT_TRUE(fdinfo); + arg0 = std::string("") + fdinfo->m_name; + + // raw_arg0 is ok! + } + + if(arg0_info->fmt == PF_HEX && arg0_info->type == PT_UINT64) { + // both `evt.arg[0]` and `evt.rawarg` become hexadecimal + char buffer[100]; + std::snprintf(buffer, sizeof(buffer), "%" PRIX64, expected_retval); + arg0 = buffer; + raw_arg0 = buffer; + } + + ASSERT_EQ(get_field_as_string(evt, "evt.arg[0]"), arg0); + ASSERT_EQ(get_field_as_string(evt, evt_rawarg_name), raw_arg0); + } else { + ///////////// + // Res + ///////////// + ASSERT_EQ(get_field_as_string(evt, "evt.res"), sinsp_utils::errno_to_str(expected_retval)); + ASSERT_EQ(get_field_as_string(evt, "evt.rawres"), std::to_string(expected_retval)); + ASSERT_EQ(get_field_as_string(evt, "evt.failed"), "true"); + + ///////////// + // First parameter (arg0) + ///////////// + ASSERT_EQ(get_field_as_string(evt, "evt.arg[0]"), get_field_as_string(evt, "evt.res")); + ASSERT_EQ(get_field_as_string(evt, evt_rawarg_name), + get_field_as_string(evt, "evt.rawres")); + } +} + +void sinsp_with_test_input::assert_fd_fields(sinsp_evt* evt, + sinsp_test_input::fd_info_fields fields) { + auto fdinfo = evt->get_fd_info(); + + // Right now we only assert if the fdinfo is present, but we want to be sure that for some + // events the fdinfo should be here + if((evt->creates_fd() || evt->uses_fd()) && evt->get_syscall_return_value() >= 0) { + ASSERT_TRUE(fdinfo); + } + + if(fdinfo) { + if(fields.fd_num.has_value()) { + ASSERT_EQ(fdinfo->m_fd, fields.fd_num.value()); + } + + if(fields.fd_name.has_value()) { + ASSERT_EQ(fdinfo->m_name, fields.fd_name.value()); + } + + if(fields.fd_name_raw.has_value()) { + ASSERT_EQ(fdinfo->m_name_raw, fields.fd_name_raw.value()); + } + } + + if(fields.fd_num.has_value()) { + ASSERT_EQ(get_field_as_string(evt, "fd.num"), std::to_string(fields.fd_num.value())); + } + + if(fields.fd_name.has_value()) { + ASSERT_EQ(get_field_as_string(evt, "fd.name"), fields.fd_name.value()); + } + + if(fields.fd_name_raw.has_value()) { + ASSERT_EQ(get_field_as_string(evt, "fd.nameraw"), fields.fd_name_raw.value()); + } + + if(fields.fd_directory.has_value()) { + ASSERT_EQ(get_field_as_string(evt, "fd.directory"), fields.fd_directory.value()); + } + + if(fields.fd_filename.has_value()) { + ASSERT_EQ(get_field_as_string(evt, "fd.filename"), fields.fd_filename.value()); + } +} diff --git a/userspace/libsinsp/test/sinsp_with_test_input.h b/userspace/libsinsp/test/sinsp_with_test_input.h index 5ec82aeffb..77e243edcf 100644 --- a/userspace/libsinsp/test/sinsp_with_test_input.h +++ b/userspace/libsinsp/test/sinsp_with_test_input.h @@ -36,19 +36,28 @@ limitations under the License. namespace sinsp_test_input { struct open_params { - static constexpr int32_t default_fd = 4; + static constexpr int64_t default_fd = 4; static constexpr const char* default_path = "/home/file.txt"; // Used for some filter checks static constexpr const char* default_directory = "/home"; static constexpr const char* default_filename = "file.txt"; - int32_t fd = default_fd; + int64_t fd = default_fd; const char* path = default_path; uint32_t flags = 0; uint32_t mode = 0; uint32_t dev = 0; uint64_t ino = 0; }; + +struct fd_info_fields { + std::optional fd_num = std::nullopt; + std::optional fd_name = std::nullopt; + std::optional fd_name_raw = std::nullopt; + std::optional fd_directory = std::nullopt; + std::optional fd_filename = std::nullopt; +}; + } // namespace sinsp_test_input class sinsp_with_test_input : public ::testing::Test { @@ -281,6 +290,9 @@ class sinsp_with_test_input : public ::testing::Test { bool filter_compiles(std::string_view filter_str); bool filter_compiles(std::string_view filter_str, filter_check_list&); + void assert_return_value(sinsp_evt* evt, int64_t expected_retval); + void assert_fd_fields(sinsp_evt* evt, sinsp_test_input::fd_info_fields fields = {}); + sinsp_evt* next_event(); scap_test_input_data m_test_data;