diff --git a/.claude/commands/update-falco-libs.md b/.claude/commands/update-falco-libs.md new file mode 100644 index 0000000000..98d673d067 --- /dev/null +++ b/.claude/commands/update-falco-libs.md @@ -0,0 +1,330 @@ +# Update Falcosecurity-Libs Fork + +You are helping update the falcosecurity-libs fork used by the StackRox collector. + +## Repository Context + +- **Collector repo**: The current working directory +- **Fork submodule**: `falcosecurity-libs/` — StackRox's fork of `https://github.com/falcosecurity/libs` +- **Fork repo**: `https://github.com/stackrox/falcosecurity-libs` +- **Upstream remote** (in submodule): `falco` → `git@github.com:falcosecurity/libs` +- **Origin remote** (in submodule): `origin` → `git@github.com:stackrox/falcosecurity-libs.git` +- **Branch naming**: `X.Y.Z-stackrox` branches carry StackRox patches on top of upstream tags +- **Update docs**: `docs/falco-update.md` + +## Step 1: Assess Current State + +Run the following in the `falcosecurity-libs/` submodule: + +1. `git describe --tags HEAD` — find current upstream base version +2. `git log --oneline ..HEAD --no-merges` — list all StackRox patches +3. `git fetch falco --tags` — get latest upstream tags +4. `git tag -l '0.*' | sort -V | tail -10` — find latest upstream releases +5. `git branch -a | grep stackrox` — find existing StackRox branches +6. Count upstream commits: `git log --oneline .. | wc -l` +7. Find StackRox-only patches: `git log --oneline HEAD --not --remotes=falco` + +Report: current version, target version, number of StackRox patches, number of upstream commits. + +## Step 2: Analyze StackRox Patches + +For each StackRox patch, determine if it has been upstreamed: + +```sh +# For each patch commit, search upstream for equivalent +git log --oneline .. --grep="" +``` + +Categorize each patch as: +- **Upstreamed** — will be dropped automatically during rebase +- **Still needed** — must be carried forward +- **Conflict risk** — touches files heavily modified upstream + +### Current StackRox Patches (as of 0.23.1-stackrox-rc1) + +20 patches in these categories: + +**BPF verifier fixes** (keep — not upstreamed): +- `2291f61ec` — clang > 19 verifier fixes (MAX_IOVCNT, volatile len_to_read, pragma unroll) +- `8672099d6` — RHEL SAP verifier fix (const struct cred *) +- `df93a9e42` — COS verifier fix (RCU pointer chain reads) +- `d1a708bde` — explicit return in auxmap submit + +**ppc64le platform support** (keep): +- `255126d47` — ppc64le vmlinux.h (large, BTF-generated) +- `a9cafe949` — ppc64le syscall compat header +- `452679e2b` — IOC_PAGE_SHIFT fix +- `dd5e86d40` / `bb733f64a` — thread_info guards (iterative, consider squashing) + +**Performance optimizations** (keep): +- `a982809e0` — cgroup subsys filtering (`INTERESTING_SUBSYS` compile flag) +- `8dd26e3dc` — socket-only FD scan (`SCAP_SOCKET_ONLY_FD` compile flag) + +**API/build adaptations** (keep): +- `32f36f770` — expose `extract_single` in filterchecks (public API) +- `b0ec4099f` — libelf suffix guard + initial filtercheck extract +- `34d863440` — sinsp include directory fix +- `a915789ec` / `16edb6bb1` — CMake/include fixes for logging integration +- `5338014a7` — disable log timestamps API + +**Workarounds** (keep but monitor): +- `8ba291e78` — disable trusted exepath (see "Exepath" section below) +- `88d5093f4` — ASSERT_TO_LOG via falcosecurity_log_fn callback + +**Rebase fixups** (always regenerated): +- `d0fb1702c` — fixes following rebase (CMake cycle, exepath fallback, assert macro) + +### Upstream Candidates + +These patches are generic enough to propose upstream: +- **Strong**: clang verifier fixes (2291f61ec, 8672099d6, df93a9e42), disable log timestamps (5338014a7) +- **With discussion**: cgroup filtering (a982809e0), socket-only FD scan (8dd26e3dc), log asserts (88d5093f4) — upstream may prefer runtime flags over compile-time +- **ppc64le bundle**: propose together if upstream is interested in the architecture + +## Step 3: Identify Breaking API Changes + +Check what APIs changed between versions. Key areas to inspect: + +```sh +# Container engine / plugin changes +git log --oneline .. -- userspace/libsinsp/container_engine/ +git log --oneline .. --grep="container_engine\|container plugin" + +# Thread manager changes +git log --oneline .. -- userspace/libsinsp/threadinfo.h userspace/libsinsp/thread_manager.h + +# sinsp API changes +git diff .. -- userspace/libsinsp/sinsp.h | grep -E '^\+|^\-' | head -80 + +# Event format changes (parameter additions/removals) +git diff .. -- driver/event_table.c + +# Enter event deprecation (EF_OLD_VERSION flags) +git log --oneline .. --grep="OLD_VERSION\|enter event\|enter_event" + +# Breaking changes +git log --oneline .. --grep="BREAKING\|breaking\|!:" +``` + +Then grep the collector code for uses of changed/removed APIs: + +```sh +grep -rn '' collector/lib/ collector/test/ --include='*.cpp' --include='*.h' +``` + +Key collector integration points to check: +- `collector/lib/system-inspector/Service.cpp` — sinsp initialization, plugin loading, filter setup +- `collector/lib/system-inspector/EventExtractor.h` — threadinfo field access macros (TINFO_FIELD, FIELD_CSTR, FIELD_RAW) +- `collector/lib/ContainerMetadata.cpp` — container info/label lookup +- `collector/lib/ProcessSignalFormatter.cpp` — process signal creation, exepath access, container_id, lineage traversal +- `collector/lib/NetworkSignalHandler.cpp` — container_id access +- `collector/lib/Process.cpp` — process info access, container_id +- `collector/lib/Utility.cpp` — GetContainerID helper, threadinfo printing +- `collector/test/ProcessSignalFormatterTest.cpp` — thread creation, thread_manager usage +- `collector/test/SystemInspectorServiceTest.cpp` — service initialization +- `collector/CMakeLists.txt` — falco build flags + +## Step 4: Plan Staging Strategy + +If the gap is large (>200 commits), identify intermediate stopping points: + +1. Look for version boundaries where major API changes happen +2. Prefer stopping at versions where container/thread APIs change +3. Each stage should be independently buildable and testable + +Known historical API breakpoints (update as upstream evolves): +- **0.20.0**: `set_import_users` lost second arg, user/group structs on threadinfo replaced with `m_uid`/`m_gid` +- **0.21.0**: Container engine subsystem removed entirely, replaced by container plugin (`libcontainer.so`). `m_container_id` removed from threadinfo. `m_thread_manager` changed to `shared_ptr`. `build_threadinfo()`/`add_thread()` removed from sinsp. Enter events for many syscalls deprecated (`EF_OLD_VERSION`). +- **0.22.0**: `get_thread_ref` removed from sinsp (use `find_thread`). `get_container_id()` removed from threadinfo. `extract_single` API changed in filterchecks. +- **0.23.0+**: Parent thread traversal moved to thread_manager. `get_thread_info(bool)` signature changed to `get_thread_info()` (no bool). `m_user`/`m_group` structs removed (use `m_uid`/`m_gid` directly). + +## Step 5: Execute Rebase (per stage) + +```sh +cd falcosecurity-libs +git fetch falco +git switch upstream-main && git merge --ff-only falco/master && git push origin upstream-main --tags +git switch +git switch -c -stackrox +git rebase +# Resolve conflicts using categorization from Step 2 +# For each conflict: check if patch is still needed, compare against upstream equivalent +git push -u origin -stackrox +``` + +Always rebase onto upstream **tags** (not master tip) per `docs/falco-update.md`. + +## Step 6: Update Collector Code + +After each rebase stage, update collector code for API changes found in Step 3. + +### Common patterns of change + +**Container plugin integration** (from 0.21.0): +- Delete `ContainerEngine.h` — container engines no longer built-in +- Ship `libcontainer.so` in the collector image (built from source in builder, needs Go) +- Load via `sinsp::register_plugin()` in Service.cpp before setting filters +- Register extraction capabilities: `EventExtractor::FilterList().add_filter_check(sinsp_plugin::new_filtercheck(plugin))` +- Wrap `set_filter("container.id != 'host'")` in try-catch for tests without plugin + +**Container ID access** (from 0.21.0+): +- Replace `tinfo->m_container_id` with a helper like `GetContainerID(tinfo, thread_manager)` that reads from plugin state tables +- In EventExtractor.h: change `TINFO_FIELD(container_id)` to `FIELD_CSTR(container_id, "container.id")` (provided by container plugin) +- The `FIELD_CSTR` null guard handles tests where the plugin isn't loaded + +**Thread access** (from 0.22.0+): +- Replace `get_thread_ref(tid, true)` with `m_thread_manager->find_thread(tid, false)` or `m_thread_manager->get_thread(tid, false)` +- `get_thread_info(true)` → `get_thread_info()` (no bool parameter) + +**User/group** (from 0.20.0+): +- Replace `m_user.uid()` / `m_group.gid()` with `m_uid` / `m_gid` + +**Thread creation in tests**: +- Replace `build_threadinfo()` with `inspector->get_threadinfo_factory().create()` +- Replace `add_thread()` with `inspector->m_thread_manager->add_thread(std::move(tinfo), false)` + +**Lineage traversal** (from 0.23.0+): +- Replace `mt->traverse_parent_state(visitor)` with `inspector_->m_thread_manager->traverse_parent_state(*mt, visitor)` +- Visitor type: `sinsp_thread_manager::visitor_func_t` instead of `sinsp_threadinfo::visitor_func_t` + +**FilterCheck API** (from 0.22.0+): +- `extract_single(event, &len)` → `extract(event, vals)` vector-based API +- Add null guards for `filter_check` pointers (plugin-provided checks may not be initialized) + +## Step 7: Known Gotchas + +### Exepath Resolution (CRITICAL) + +Modern drivers (0.21.0+) **no longer send execve enter events** (marked `EF_OLD_VERSION`). The exepath is supposed to come from the `trusted_exepath` parameter (param 28) in the exit event, which uses the kernel's `d_path()`. + +However, the StackRox fork **disables trusted_exepath** (`USE_TRUSTED_EXEPATH=false`) because it resolves symlinks — giving `/bin/busybox` instead of `/bin/ls` in busybox containers, breaking ACS policies. + +**Without either source, `m_exepath` inherits the parent's value on clone** (e.g., `/usr/bin/podman`), causing all container processes to show the container runtime's path. + +**Fix**: Add a fallback in `parse_execve_exit` (parsers.cpp) that uses **Parameter 31** (`filename`, which is `bprm->filename`) from the exit event. This contains the first argument to execve as provided by the caller — same behavior as the old enter event reconstruction: + +```cpp +// After the retrieve_enter_event() block, add: +if(!exepath_set) { + /* Parameter 31: filename (type: PT_FSPATH) */ + if(const auto filename_param = evt.get_param(30); !filename_param->empty()) { + std::string_view filename = filename_param->as(); + if(filename != "") { + std::string fullpath = sinsp_utils::concatenate_paths( + evt.get_tinfo()->get_cwd(), filename); + evt.get_tinfo()->set_exepath(std::move(fullpath)); + } + } +} +``` + +**How to detect this bug**: Integration test `TestProcessViz` fails with all processes showing the container runtime binary (e.g., `/usr/bin/podman`) as their ExePath. + +**Key event parameters** (PPME_SYSCALL_EXECVE_19_X, 0-indexed): +- 1: exe (argv[0]), 6: cwd, 13: comm (always correct) +- 27: trusted_exepath (kernel d_path, resolves symlinks — disabled) +- 30: filename (bprm->filename, first arg to execve — use this) + +### CMake Dependency Cycle + +Upstream has a cyclic dependency: `events_dimensions_generator → scap_event_schema → scap → pman → ProbeSkeleton → EventsDimensions → generator`. Upstream doesn't hit it because their CI uses CMake 3.22; our builder uses 3.31+ which enforces cycle detection. + +**Fix**: Compile the 3 required driver source files (`event_table.c`, `flags_table.c`, `dynamic_params_table.c`) directly into the generator instead of linking `scap_event_schema`. This fix lives in `driver/modern_bpf/CMakeLists.txt` and must be carried forward each rebase. + +### ASSERT_TO_LOG Circular Dependency + +Collector compiles with `-DASSERT_TO_LOG` so assertions log instead of aborting. The old approach using `libsinsp_logger()` causes circular includes because `logger.h` includes `sinsp_public.h`. + +**Fix**: Use `falcosecurity_log_fn` callback from `scap_log.h` (same pattern as `scap_assert.h`). This is a tiny header with no dependencies. The callback is set by sinsp when it opens the scap handle. + +### Container Plugin Build + +The container plugin (`libcontainer.so`) is a C++/Go hybrid: +- Source: `github.com/falcosecurity/plugins` (monorepo), `plugins/container/` directory +- Requires Go 1.23+ for the go-worker component +- Upstream only ships x86_64 and arm64 binaries; ppc64le/s390x must be built from source +- Version must match falcosecurity-libs (check plugin compatibility matrix) +- Submodule at `builder/third_party/falcosecurity-plugins` + +### BPF Verifier Compatibility + +BPF verifier behavior varies significantly across: +- **Kernel versions**: Older kernels have stricter limits +- **Clang versions**: clang > 19 can produce code that exceeds instruction counts +- **Platform kernels**: RHEL SAP, Google COS have custom verifiers + +Common fixes: +- Reduce loop bounds (e.g., `MAX_IOVCNT` 32 → 16) +- Mark variables `volatile` to prevent optimizations the verifier can't follow +- Add `#pragma unroll` for loops the verifier can't bound +- Break pointer chain reads into separate variables with null checks +- Use `const` qualifiers on credential struct pointers + +## Step 8: Validate Each Stage + +Run this checklist after each stage: + +- [ ] `falcosecurity-libs` builds via cmake `add_subdirectory` +- [ ] Each surviving patch verified: diff against original to ensure no content loss +- [ ] `make collector` succeeds on amd64 +- [ ] `make unittest` passes (all test suites, especially ProcessSignalFormatterTest) +- [ ] Integration tests: `TestProcessViz` (exepath correctness), `TestProcessLineageInfo`, `TestNetworkFlows` +- [ ] Multi-arch compilation: arm64, ppc64le, s390x +- [ ] Container ID attribution works (not all showing empty or host) +- [ ] Process exepaths are correct (not showing container runtime binary like `/usr/bin/podman`) +- [ ] Container label/namespace lookup works +- [ ] Network signal handler receives correct container IDs +- [ ] Runtime self-checks pass + +### Key Integration Tests + +- **TestProcessViz**: Verifies process ExePath, Name, and Args for container processes. Catches the exepath regression where all paths show the container runtime. Expected paths like `/bin/ls`, `/usr/sbin/nginx`, `/bin/sh`. +- **TestProcessLineageInfo**: Verifies parent process lineage chains stop at container boundaries. +- **TestNetworkFlows**: Verifies network connections are attributed to correct containers. + +## Step 9: Final Update + +```sh +cd +cd falcosecurity-libs && git checkout -stackrox +cd .. && git add falcosecurity-libs +``` + +Update `docs/falco-update.md` with: +- Version transition (e.g., "0.23.1 → 0.25.0") +- Any new upstream API changes requiring collector-side fixes +- New StackRox patches added, patches dropped (upstreamed) +- Known issues or workarounds + +## PR Strategy + +Each stage should produce **two PRs**: +1. **Fork PR** targeting `upstream-main` in `stackrox/falcosecurity-libs` (the rebased branch) +2. **Collector PR** updating the submodule pointer and making collector-side code changes + +## Quick Reference: Event Architecture + +### How Process Events Flow + +1. **Kernel BPF** captures syscall events → writes to ring buffer +2. **libscap** reads ring buffer → produces `scap_evt` structs +3. **libsinsp parsers** (`parsers.cpp`) process events: + - `reset()`: looks up/creates thread info, validates enter/exit event matching + - `parse_clone_exit_caller/child()`: creates child thread info, inherits parent fields + - `parse_execve_exit()`: updates thread info with new process details +4. **Collector** (`ProcessSignalFormatter`) reads thread info fields via `EventExtractor` + +### Key Thread Info Fields + +| Field | Source | Notes | +|-------|--------|-------| +| `m_comm` | Exit event param 13 | Always correct (kernel task_struct->comm) | +| `m_exe` | Exit event param 1 | argv[0], may be relative | +| `m_exepath` | Enter event reconstruction OR param 27/30 | See "Exepath Resolution" gotcha | +| `m_pid` | Exit event param 4 | | +| `m_uid`/`m_gid` | Exit event param 26/29 | Was `m_user.uid()`/`m_group.gid()` before 0.20.0 | +| container_id | Container plugin filter field | Was `m_container_id` before 0.21.0 | + +### Enter Event Deprecation + +Upstream removed enter events to reduce ~50% of kernel/userspace overhead (proposal: `proposals/20240901-disable-support-for-syscall-enter-events.md`). All parameters moved to exit events. A scap converter handles old capture files. Any code depending on `retrieve_enter_event()` will silently fail with modern drivers — check for fallbacks using exit event parameters. diff --git a/.gitmodules b/.gitmodules index 3be16bd1d7..73937569ad 100644 --- a/.gitmodules +++ b/.gitmodules @@ -72,3 +72,6 @@ path = builder/third_party/bpftool url = https://github.com/libbpf/bpftool branch = v7.3.0 +[submodule "builder/third_party/falcosecurity-plugins"] + path = builder/third_party/falcosecurity-plugins + url = https://github.com/falcosecurity/plugins.git diff --git a/builder/install/00-golang.sh b/builder/install/00-golang.sh new file mode 100755 index 0000000000..4c4ba6f25b --- /dev/null +++ b/builder/install/00-golang.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -e + +GO_VERSION=1.23.7 +ARCH=$(uname -m) +case ${ARCH} in + x86_64) GO_ARCH=amd64 ;; + aarch64) GO_ARCH=arm64 ;; + ppc64le) GO_ARCH=ppc64le ;; + s390x) GO_ARCH=s390x ;; +esac + +curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${GO_ARCH}.tar.gz" | tar -C /usr/local -xz +export PATH="/usr/local/go/bin:${PATH}" +echo "export PATH=/usr/local/go/bin:${PATH}" >> /etc/profile.d/golang.sh +go version diff --git a/builder/install/01-container-plugin.sh b/builder/install/01-container-plugin.sh new file mode 100755 index 0000000000..d9a0c9c46d --- /dev/null +++ b/builder/install/01-container-plugin.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -e + +export PATH="/usr/local/go/bin:${PATH}" + +cd third_party/falcosecurity-plugins/plugins/container + +cp ../../LICENSE "${LICENSE_DIR}/falcosecurity-plugins-container-${CONTAINER_PLUGIN_VERSION}" 2> /dev/null || true + +# Remove static libstdc++ linking — not needed since we control the runtime +# image, and libstdc++-static is not available in CentOS Stream 10. +sed -i '/-static-libgcc\|-static-libstdc++/d' CMakeLists.txt + +cmake -B build -S . \ + -DCMAKE_BUILD_TYPE=Release \ + -DENABLE_ASYNC=ON \ + -DENABLE_TESTS=OFF +cmake --build build --target container --parallel "${NPROCS}" + +install -m 755 build/libcontainer.so /usr/local/lib64/libcontainer.so diff --git a/builder/install/versions.sh b/builder/install/versions.sh index 08988c81e7..c6ccaae09c 100644 --- a/builder/install/versions.sh +++ b/builder/install/versions.sh @@ -16,3 +16,4 @@ export GPERFTOOLS_VERSION=2.16 export UTHASH_VERSION=v1.9.8 export YAMLCPP_VERSION=0.8.0 export LIBBPF_VERSION=v1.3.4 +export CONTAINER_PLUGIN_VERSION=0.6.3 diff --git a/builder/third_party/falcosecurity-plugins b/builder/third_party/falcosecurity-plugins new file mode 160000 index 0000000000..fb2ad646f1 --- /dev/null +++ b/builder/third_party/falcosecurity-plugins @@ -0,0 +1 @@ +Subproject commit fb2ad646f1cab61abb2124df5bf0f2578fc70e58 diff --git a/collector/CMakeLists.txt b/collector/CMakeLists.txt index 2d0a6a2152..bf3ad9bef7 100644 --- a/collector/CMakeLists.txt +++ b/collector/CMakeLists.txt @@ -87,7 +87,6 @@ set(USE_BUNDLED_DEPS OFF CACHE BOOL "Enable bundled dependencies instead of usin set(USE_BUNDLED_CARES OFF CACHE BOOL "Enable bundled dependencies instead of using the system ones" FORCE) set(BUILD_LIBSCAP_GVISOR OFF CACHE BOOL "Do not build gVisor support" FORCE) set(MINIMAL_BUILD OFF CACHE BOOL "Minimal" FORCE) -set(SINSP_SLIM_THREADINFO ON CACHE BOOL "Slim threadinfo" FORCE) set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build position independent libraries and executables" FORCE) set(LIBELF_LIB_SUFFIX ".so" CACHE STRING "Use libelf.so" FORCE) diff --git a/collector/Makefile b/collector/Makefile index eec68231cc..6d3d5bdb70 100644 --- a/collector/Makefile +++ b/collector/Makefile @@ -39,6 +39,7 @@ container/bin/collector: cmake-build/collector mkdir -p container/bin cp "$(COLLECTOR_BIN_DIR)/collector" container/bin/collector cp "$(COLLECTOR_BIN_DIR)/self-checks" container/bin/self-checks + docker cp $(COLLECTOR_BUILDER_NAME):/usr/local/lib64/libcontainer.so container/bin/libcontainer.so .PHONY: collector collector: container/bin/collector txt-files diff --git a/collector/container/Dockerfile b/collector/container/Dockerfile index 569d67cbb2..8a09d67441 100644 --- a/collector/container/Dockerfile +++ b/collector/container/Dockerfile @@ -26,6 +26,7 @@ COPY container/THIRD_PARTY_NOTICES/ /THIRD_PARTY_NOTICES/ COPY kernel-modules /kernel-modules COPY container/bin/collector /usr/local/bin/ COPY container/bin/self-checks /usr/local/bin/self-checks +COPY container/bin/libcontainer.so /usr/local/lib64/libcontainer.so COPY container/status-check.sh /usr/local/bin/status-check.sh EXPOSE 8080 9090 diff --git a/collector/lib/ContainerEngine.h b/collector/lib/ContainerEngine.h deleted file mode 100644 index 63978528c9..0000000000 --- a/collector/lib/ContainerEngine.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "container_engine/container_cache_interface.h" -#include "container_engine/container_engine_base.h" -#include "threadinfo.h" - -namespace collector { -class ContainerEngine : public libsinsp::container_engine::container_engine_base { - public: - ContainerEngine(libsinsp::container_engine::container_cache_interface& cache) : libsinsp::container_engine::container_engine_base(cache) {} - - bool resolve(sinsp_threadinfo* tinfo, bool query_os_for_missing_info) override { - for (const auto& cgroup : tinfo->cgroups()) { - auto container_id = ExtractContainerIDFromCgroup(cgroup.second); - - if (container_id) { - tinfo->m_container_id = *container_id; - return true; - } - } - - return false; - } -}; -} // namespace collector diff --git a/collector/lib/ContainerMetadata.cpp b/collector/lib/ContainerMetadata.cpp index 343e9c6a5a..8e8d2c9131 100644 --- a/collector/lib/ContainerMetadata.cpp +++ b/collector/lib/ContainerMetadata.cpp @@ -2,6 +2,7 @@ #include +#include "Logging.h" #include "system-inspector/EventExtractor.h" namespace collector { @@ -20,19 +21,13 @@ std::string ContainerMetadata::GetNamespace(const std::string& container_id) { } std::string ContainerMetadata::GetContainerLabel(const std::string& container_id, const std::string& label) { - auto containers = inspector_->m_container_manager.get_containers(); - const auto& container = containers->find(container_id); - if (container == containers->end()) { - return ""; - } - - const auto& labels = container->second->m_labels; - const auto& label_it = labels.find(label); - if (label_it == labels.end()) { - return ""; - } - - return label_it->second; + // Container labels are no longer available through the sinsp API. + // The container plugin provides container metadata via filter fields + // (e.g., container.label) but not through a programmatic lookup API. + CLOG_THROTTLED(DEBUG, std::chrono::seconds(300)) + << "Container label lookup by container ID is not supported: " + << "container_id=" << container_id << " label=" << label; + return ""; } } // namespace collector \ No newline at end of file diff --git a/collector/lib/NetworkSignalHandler.cpp b/collector/lib/NetworkSignalHandler.cpp index df457d5ef5..71e1e634d4 100644 --- a/collector/lib/NetworkSignalHandler.cpp +++ b/collector/lib/NetworkSignalHandler.cpp @@ -133,11 +133,11 @@ std::optional NetworkSignalHandler::GetConnection(sinsp_evt* evt) { const Endpoint* local = is_server ? &server : &client; const Endpoint* remote = is_server ? &client : &server; - const std::string* container_id = event_extractor_->get_container_id(evt); + const char* container_id = event_extractor_->get_container_id(evt); if (!container_id) { return std::nullopt; } - return {Connection(*container_id, *local, *remote, l4proto, is_server)}; + return {Connection(container_id, *local, *remote, l4proto, is_server)}; } SignalHandler::Result NetworkSignalHandler::HandleSignal(sinsp_evt* evt) { diff --git a/collector/lib/Process.cpp b/collector/lib/Process.cpp index 632d824a03..7054257f02 100644 --- a/collector/lib/Process.cpp +++ b/collector/lib/Process.cpp @@ -5,6 +5,7 @@ #include #include "CollectorStats.h" +#include "Utility.h" #include "system-inspector/Service.h" namespace collector { @@ -32,7 +33,11 @@ std::string Process::container_id() const { WaitForProcessInfo(); if (system_inspector_threadinfo_) { - return system_inspector_threadinfo_->m_container_id; + for (const auto& [subsys, cgroup_path] : system_inspector_threadinfo_->cgroups()) { + if (auto id = ExtractContainerIDFromCgroup(cgroup_path)) { + return std::string(*id); + } + } } return NOT_AVAILABLE; diff --git a/collector/lib/ProcessSignalFormatter.cpp b/collector/lib/ProcessSignalFormatter.cpp index a588d75bd6..4f443a516b 100644 --- a/collector/lib/ProcessSignalFormatter.cpp +++ b/collector/lib/ProcessSignalFormatter.cpp @@ -2,6 +2,9 @@ #include +#include +#include + #include #include "internalapi/sensor/signal_iservice.pb.h" @@ -59,6 +62,7 @@ std::string extract_proc_args(sinsp_threadinfo* tinfo) { ProcessSignalFormatter::ProcessSignalFormatter( sinsp* inspector, const CollectorConfig& config) : event_names_(EventNames::GetInstance()), + inspector_(inspector), event_extractor_(std::make_unique()), container_metadata_(inspector), config_(config) { @@ -176,8 +180,8 @@ ProcessSignal* ProcessSignalFormatter::CreateProcessSignal(sinsp_evt* event) { signal->set_allocated_time(timestamp); // set container_id - if (const std::string* container_id = event_extractor_->get_container_id(event)) { - signal->set_container_id(*container_id); + if (const char* container_id = event_extractor_->get_container_id(event)) { + signal->set_container_id(container_id); } // set process lineage @@ -232,8 +236,8 @@ ProcessSignal* ProcessSignalFormatter::CreateProcessSignal(sinsp_threadinfo* tin signal->set_pid(tinfo->m_pid); // set user and group id credentials - signal->set_uid(tinfo->m_user.uid()); - signal->set_gid(tinfo->m_group.gid()); + signal->set_uid(tinfo->m_uid); + signal->set_gid(tinfo->m_gid); // set time auto timestamp = Allocate(); @@ -241,7 +245,7 @@ ProcessSignal* ProcessSignalFormatter::CreateProcessSignal(sinsp_threadinfo* tin signal->set_allocated_time(timestamp); // set container_id - signal->set_container_id(tinfo->m_container_id); + signal->set_container_id(GetContainerID(*tinfo, *inspector_->m_thread_manager)); // set process lineage std::vector lineage; @@ -265,11 +269,11 @@ std::string ProcessSignalFormatter::ProcessDetails(sinsp_evt* event) { std::stringstream ss; const std::string* path = event_extractor_->get_exepath(event); const std::string* name = event_extractor_->get_comm(event); - const std::string* container_id = event_extractor_->get_container_id(event); + const char* container_id = event_extractor_->get_container_id(event); const char* args = event_extractor_->get_proc_args(event); const int64_t* pid = event_extractor_->get_pid(event); - ss << "Container: " << (container_id ? *container_id : "null") + ss << "Container: " << (container_id ? container_id : "null") << ", Name: " << (name ? *name : "null") << ", PID: " << (pid ? *pid : -1) << ", Path: " << (path ? *path : "null") @@ -327,7 +331,7 @@ void ProcessSignalFormatter::GetProcessLineage(sinsp_threadinfo* tinfo, return; } } - sinsp_threadinfo::visitor_func_t visitor = [this, &lineage](sinsp_threadinfo* pt) { + sinsp_thread_manager::visitor_func_t visitor = [this, &lineage](sinsp_threadinfo* pt) { if (pt == NULL) { return false; } @@ -341,13 +345,13 @@ void ProcessSignalFormatter::GetProcessLineage(sinsp_threadinfo* tinfo, // // In back-ported eBPF probes, `m_vpid` will not be set for containers // running when collector comes online because /proc/{pid}/status does - // not contain namespace information, so `m_container_id` is checked - // instead. `m_container_id` is not enough on its own to identify + // not contain namespace information, so the container ID is checked + // instead. The container ID is not enough on its own to identify // containerized processes, because it is not guaranteed to be set on // all platforms. // if (pt->m_vpid == 0) { - if (pt->m_container_id.empty()) { + if (GetContainerID(*pt, *inspector_->m_thread_manager).empty()) { return false; } } else if (pt->m_pid == pt->m_vpid) { @@ -361,7 +365,7 @@ void ProcessSignalFormatter::GetProcessLineage(sinsp_threadinfo* tinfo, // Collapse parent child processes that have the same path if (lineage.empty() || (lineage.back().parent_exec_file_path() != pt->m_exepath)) { LineageInfo info; - info.set_parent_uid(pt->m_user.uid()); + info.set_parent_uid(pt->m_uid); info.set_parent_exec_file_path(pt->m_exepath); lineage.push_back(info); } @@ -373,7 +377,7 @@ void ProcessSignalFormatter::GetProcessLineage(sinsp_threadinfo* tinfo, return true; }; - mt->traverse_parent_state(visitor); + inspector_->m_thread_manager->traverse_parent_state(*mt, visitor); CountLineage(lineage); } diff --git a/collector/lib/ProcessSignalFormatter.h b/collector/lib/ProcessSignalFormatter.h index 8c57011c5b..a7bb69ab57 100644 --- a/collector/lib/ProcessSignalFormatter.h +++ b/collector/lib/ProcessSignalFormatter.h @@ -55,6 +55,7 @@ class ProcessSignalFormatter : public ProtoSignalFormatter& lineage); const EventNames& event_names_; + sinsp* inspector_; std::unique_ptr event_extractor_; ContainerMetadata container_metadata_; diff --git a/collector/lib/Utility.cpp b/collector/lib/Utility.cpp index 26832eada8..b3199d0603 100644 --- a/collector/lib/Utility.cpp +++ b/collector/lib/Utility.cpp @@ -18,6 +18,7 @@ extern "C" { #include #include +#include #include @@ -57,9 +58,19 @@ const char* SignalName(int signum) { } } +std::string GetContainerID(sinsp_threadinfo& tinfo, sinsp_thread_manager& thread_manager) { + const auto* accessor = thread_manager.get_field_accessor("container_id"); + if (!accessor) { + return {}; + } + std::string container_id; + tinfo.get_dynamic_field(*accessor, container_id); + return container_id; +} + std::ostream& operator<<(std::ostream& os, const sinsp_threadinfo* t) { if (t) { - os << "Container: \"" << t->m_container_id << "\", Name: " << t->m_comm << ", PID: " << t->m_pid << ", Args: " << t->m_exe; + os << "Name: " << t->m_comm << ", PID: " << t->m_pid << ", Args: " << t->m_exe; } else { os << "NULL\n"; } diff --git a/collector/lib/Utility.h b/collector/lib/Utility.h index 04be8cd480..8544bbd72c 100644 --- a/collector/lib/Utility.h +++ b/collector/lib/Utility.h @@ -14,6 +14,7 @@ // forward declarations class sinsp_threadinfo; +class sinsp_thread_manager; namespace collector { @@ -65,6 +66,10 @@ std::string Str(Args&&... args) { std::ostream& operator<<(std::ostream& os, const sinsp_threadinfo* t); +// Extract container ID from a threadinfo using the dynamic field written by +// the container plugin. Returns an empty string if unavailable. +std::string GetContainerID(sinsp_threadinfo& tinfo, sinsp_thread_manager& thread_manager); + // UUIDStr returns UUID in string format. const char* UUIDStr(); diff --git a/collector/lib/system-inspector/EventExtractor.cpp b/collector/lib/system-inspector/EventExtractor.cpp index a72c87e329..82e1ea94ca 100644 --- a/collector/lib/system-inspector/EventExtractor.cpp +++ b/collector/lib/system-inspector/EventExtractor.cpp @@ -5,6 +5,10 @@ namespace collector::system_inspector { void EventExtractor::Init(sinsp* inspector) { for (auto* wrapper : wrappers_) { std::unique_ptr check = FilterList().new_filter_check_from_fldname(wrapper->event_name, inspector, true); + if (!check) { + CLOG(WARNING) << "Filter check not available for field: " << wrapper->event_name; + continue; + } check->parse_field_name(wrapper->event_name, true, false); wrapper->filter_check.reset(check.release()); } diff --git a/collector/lib/system-inspector/EventExtractor.h b/collector/lib/system-inspector/EventExtractor.h index 94d129befc..1cb5615a36 100644 --- a/collector/lib/system-inspector/EventExtractor.h +++ b/collector/lib/system-inspector/EventExtractor.h @@ -41,9 +41,12 @@ class EventExtractor { #define FIELD_RAW(id, fieldname, type) \ public: \ const type* get_##id(sinsp_evt* event) { \ - uint32_t len; \ - auto buf = filter_check_##id##_->extract_single(event, &len); \ - if (!buf) return nullptr; \ + if (!filter_check_##id##_.filter_check) return nullptr; \ + std::vector vals_##id; \ + if (!filter_check_##id##_->extract(event, vals_##id)) return nullptr; \ + if (vals_##id.empty()) return nullptr; \ + auto len = vals_##id[0].len; \ + auto buf = vals_##id[0].ptr; \ if (len != sizeof(type)) { \ CLOG_THROTTLED(WARNING, std::chrono::seconds(30)) \ << "Failed to extract value for field " << fieldname << ": expected type " << #type << " (size " \ @@ -63,9 +66,12 @@ class EventExtractor { const std::optional get_##id(sinsp_evt* event) { \ static_assert(std::is_trivially_copyable_v, \ "Attempted to create FIELD_RAW_SAFE on non trivial type"); \ - uint32_t len; \ - auto buf = filter_check_##id##_->extract_single(event, &len); \ - if (!buf) return {}; \ + if (!filter_check_##id##_.filter_check) return {}; \ + std::vector vals_##id; \ + if (!filter_check_##id##_->extract(event, vals_##id)) return {}; \ + if (vals_##id.empty()) return {}; \ + auto len = vals_##id[0].len; \ + auto buf = vals_##id[0].ptr; \ if (len != sizeof(type)) { \ CLOG_THROTTLED(WARNING, std::chrono::seconds(30)) \ << "Failed to extract value for field " << fieldname << ": expected type " << #type << " (size " \ @@ -80,39 +86,40 @@ class EventExtractor { private: \ DECLARE_FILTER_CHECK(id, fieldname) -#define FIELD_CSTR(id, fieldname) \ - public: \ - const char* get_##id(sinsp_evt* event) { \ - uint32_t len; \ - auto buf = filter_check_##id##_->extract_single(event, &len); \ - if (!buf) return nullptr; \ - return reinterpret_cast(buf); \ - } \ - \ - private: \ +#define FIELD_CSTR(id, fieldname) \ + public: \ + const char* get_##id(sinsp_evt* event) { \ + if (!filter_check_##id##_.filter_check) return nullptr; \ + std::vector vals_##id; \ + if (!filter_check_##id##_->extract(event, vals_##id)) return nullptr; \ + if (vals_##id.empty()) return nullptr; \ + return reinterpret_cast(vals_##id[0].ptr); \ + } \ + \ + private: \ DECLARE_FILTER_CHECK(id, fieldname) #define EVT_ARG(name) FIELD_CSTR(evt_arg_##name, "evt.arg." #name) #define EVT_ARG_RAW(name, type) FIELD_RAW(evt_arg_##name, "evt.rawarg." #name, type) -#define TINFO_FIELD_RAW(id, fieldname, type) \ - public: \ - const type* get_##id(sinsp_evt* event) { \ - if (!event) return nullptr; \ - sinsp_threadinfo* tinfo = event->get_thread_info(true); \ - if (!tinfo) return nullptr; \ - return &tinfo->fieldname; \ +#define TINFO_FIELD_RAW(id, fieldname, type) \ + public: \ + const type* get_##id(sinsp_evt* event) { \ + if (!event) return nullptr; \ + sinsp_threadinfo* tinfo = event->get_thread_info(); \ + if (!tinfo) return nullptr; \ + return &tinfo->fieldname; \ } -#define TINFO_FIELD_RAW_GETTER(id, getter, type) \ - public: \ - type internal_##id; \ - const type* get_##id(sinsp_evt* event) { \ - if (!event) return nullptr; \ - sinsp_threadinfo* tinfo = event->get_thread_info(true); \ - if (!tinfo) return nullptr; \ - internal_##id = tinfo->getter(); \ - return &internal_##id; \ +#define TINFO_FIELD_RAW_GETTER(id, getter, type) \ + public: \ + type internal_##id; \ + const type* get_##id(sinsp_evt* event) { \ + if (!event) return nullptr; \ + sinsp_threadinfo* tinfo = event->get_thread_info(); \ + if (!tinfo) return nullptr; \ + internal_##id = tinfo->getter(); \ + return &internal_##id; \ } #define TINFO_FIELD(id) TINFO_FIELD_RAW(id, m_##id, decltype(std::declval().m_##id)) @@ -129,16 +136,16 @@ class EventExtractor { // // ADD ANY NEW FIELDS BELOW THIS LINE - // Container related fields - TINFO_FIELD(container_id); + // Container related fields — provided by the container plugin via filter fields. + FIELD_CSTR(container_id, "container.id"); // Process related fields TINFO_FIELD(comm); TINFO_FIELD(exe); TINFO_FIELD(exepath); TINFO_FIELD(pid); - TINFO_FIELD_RAW_GETTER(uid, m_user.uid, uint32_t); - TINFO_FIELD_RAW_GETTER(gid, m_group.gid, uint32_t); + TINFO_FIELD_RAW(uid, m_uid, uint32_t); + TINFO_FIELD_RAW(gid, m_gid, uint32_t); FIELD_CSTR(proc_args, "proc.args"); // General event information diff --git a/collector/lib/system-inspector/Service.cpp b/collector/lib/system-inspector/Service.cpp index 95c0394416..6c38470408 100644 --- a/collector/lib/system-inspector/Service.cpp +++ b/collector/lib/system-inspector/Service.cpp @@ -6,8 +6,9 @@ #include -#include "libsinsp/container_engine/sinsp_container_type.h" +#include "libsinsp/filter.h" #include "libsinsp/parsers.h" +#include "libsinsp/plugin.h" #include "libsinsp/sinsp.h" #include @@ -15,7 +16,6 @@ #include "CollectionMethod.h" #include "CollectorException.h" #include "CollectorStats.h" -#include "ContainerEngine.h" #include "ContainerMetadata.h" #include "EventExtractor.h" #include "EventNames.h" @@ -35,12 +35,7 @@ namespace collector::system_inspector { Service::~Service() = default; Service::Service(const CollectorConfig& config) - : inspector_(std::make_unique(true)), - container_metadata_inspector_(std::make_unique(inspector_.get())), - default_formatter_(std::make_unique( - inspector_.get(), - DEFAULT_OUTPUT_STR, - EventExtractor::FilterList())) { + : inspector_(std::make_unique(true)) { // Setup the inspector. // peeking into arguments has a big overhead, so we prevent it from happening inspector_->set_snaplen(0); @@ -50,7 +45,7 @@ Service::Service(const CollectorConfig& config) inspector_->disable_log_timestamps(); inspector_->set_log_callback(logging::InspectorLogCallback); - inspector_->set_import_users(config.ImportUsers(), false); + inspector_->set_import_users(config.ImportUsers()); inspector_->set_thread_timeout_s(30); inspector_->set_auto_threads_purging_interval_s(60); inspector_->m_thread_manager->set_max_thread_table_size(config.GetSinspThreadCacheSize()); @@ -62,31 +57,43 @@ Service::Service(const CollectorConfig& config) inspector_->get_parser()->set_track_connection_status(true); } - if (config.EnableRuntimeConfig()) { - uint64_t mask = 1 << CT_CRI | - 1 << CT_CRIO | - 1 << CT_CONTAINERD; - - if (config.UseDockerCe()) { - mask |= 1 << CT_DOCKER; + // Load the container plugin for container ID attribution and metadata. + // This MUST happen before EventExtractor::Init() (via ContainerMetadata) + // because the plugin provides the "container.id" and "k8s.ns.name" fields. + const char* plugin_path = "/usr/local/lib64/libcontainer.so"; + try { + auto plugin = inspector_->register_plugin(plugin_path); + std::string err; + if (!plugin->init("", err)) { + CLOG(ERROR) << "Failed to init container plugin: " << err; } - - if (config.UsePodmanCe()) { - mask |= 1 << CT_PODMAN; + if (plugin->caps() & CAP_EXTRACTION) { + EventExtractor::FilterList().add_filter_check(sinsp_plugin::new_filtercheck(plugin)); } - - inspector_->set_container_engine_mask(mask); - - // k8s naming conventions specify that max length be 253 characters - // (the extra 2 are just for a nice 0xFF). - inspector_->set_container_labels_max_len(255); - } else { - auto engine = std::make_shared(inspector_->m_container_manager); - auto* container_engines = inspector_->m_container_manager.get_container_engines(); - container_engines->push_back(engine); + CLOG(INFO) << "Loaded container plugin from " << plugin_path; + } catch (const sinsp_exception& e) { + CLOG(WARNING) << "Could not load container plugin from " << plugin_path + << ": " << e.what(); } - inspector_->set_filter("container.id != 'host'"); + // Initialize ContainerMetadata after the plugin is loaded, so that + // EventExtractor::Init() can find plugin-provided fields like container.id. + container_metadata_inspector_ = std::make_shared(inspector_.get()); + default_formatter_ = std::make_unique( + inspector_.get(), DEFAULT_OUTPUT_STR, EventExtractor::FilterList()); + + // Compile the container filter using our FilterList (which includes + // plugin filterchecks). sinsp::set_filter(string) uses a hardcoded + // filter check list that doesn't include plugin fields. + try { + auto factory = std::make_shared( + inspector_.get(), EventExtractor::FilterList()); + sinsp_filter_compiler compiler(factory, "container.id != 'host'"); + inspector_->set_filter(compiler.compile(), "container.id != 'host'"); + } catch (const sinsp_exception& e) { + CLOG(WARNING) << "Could not set container filter: " << e.what() + << ". Container filtering will not be active."; + } // The self-check handlers should only operate during start up, // so they are added to the handler list first, so they have access @@ -296,7 +303,7 @@ bool Service::SendExistingProcesses(SignalHandler* handler) { } return threads->loop([&](sinsp_threadinfo& tinfo) { - if (!tinfo.m_container_id.empty() && tinfo.is_main_thread()) { + if (!GetContainerID(tinfo, *inspector_->m_thread_manager).empty() && tinfo.is_main_thread()) { auto result = handler->HandleExistingProcess(&tinfo); if (result == SignalHandler::ERROR || result == SignalHandler::NEEDS_REFRESH) { CLOG(WARNING) << "Failed to write existing process signal: " << &tinfo; @@ -398,7 +405,7 @@ void Service::ServePendingProcessRequests() { auto callback = request.second.lock(); if (callback) { - (*callback)(inspector_->get_thread_ref(pid, true)); + (*callback)(inspector_->m_thread_manager->get_thread(pid)); } pending_process_requests_.pop_front(); diff --git a/collector/test/ProcessSignalFormatterTest.cpp b/collector/test/ProcessSignalFormatterTest.cpp index 68e1fcb9c7..5931c46275 100644 --- a/collector/test/ProcessSignalFormatterTest.cpp +++ b/collector/test/ProcessSignalFormatterTest.cpp @@ -54,18 +54,18 @@ TEST(ProcessSignalFormatterTest, ProcessWithoutParentTest) { ProcessSignalFormatter processSignalFormatter(inspector.get(), config); - auto tinfo = inspector->build_threadinfo(); + auto tinfo = inspector->get_threadinfo_factory().create(); tinfo->m_pid = 0; tinfo->m_tid = 0; tinfo->m_ptid = -1; tinfo->m_vpid = 2; - tinfo->m_user.set_uid(7); + tinfo->m_uid = 7; tinfo->m_exepath = "qwerty"; - inspector->add_thread(std::move(tinfo)); + inspector->m_thread_manager->add_thread(std::move(tinfo), false); std::vector lineage; - processSignalFormatter.GetProcessLineage(inspector->get_thread_ref(0).get(), lineage); + processSignalFormatter.GetProcessLineage(inspector->m_thread_manager->find_thread(0, true).get(), lineage); int count = collector_stats.GetCounter(CollectorStats::process_lineage_counts); int total = collector_stats.GetCounter(CollectorStats::process_lineage_total); @@ -89,25 +89,25 @@ TEST(ProcessSignalFormatterTest, ProcessWithParentTest) { ProcessSignalFormatter processSignalFormatter(inspector.get(), config); - auto tinfo = inspector->build_threadinfo(); + auto tinfo = inspector->get_threadinfo_factory().create(); tinfo->m_pid = 3; tinfo->m_tid = 3; tinfo->m_ptid = -1; tinfo->m_vpid = 1; - tinfo->m_user.set_uid(42); + tinfo->m_uid = 42; tinfo->m_exepath = "asdf"; - auto tinfo2 = inspector->build_threadinfo(); + auto tinfo2 = inspector->get_threadinfo_factory().create(); tinfo2->m_pid = 1; tinfo2->m_tid = 1; tinfo2->m_ptid = 3; tinfo2->m_vpid = 2; - tinfo2->m_user.set_uid(7); + tinfo2->m_uid = 7; tinfo2->m_exepath = "qwerty"; - inspector->add_thread(std::move(tinfo)); - inspector->add_thread(std::move(tinfo2)); + inspector->m_thread_manager->add_thread(std::move(tinfo), false); + inspector->m_thread_manager->add_thread(std::move(tinfo2), false); std::vector lineage; - processSignalFormatter.GetProcessLineage(inspector->get_thread_ref(1).get(), lineage); + processSignalFormatter.GetProcessLineage(inspector->m_thread_manager->find_thread(1, true).get(), lineage); int count = collector_stats.GetCounter(CollectorStats::process_lineage_counts); int total = collector_stats.GetCounter(CollectorStats::process_lineage_total); @@ -134,23 +134,23 @@ TEST(ProcessSignalFormatterTest, ProcessWithParentWithPid0Test) { ProcessSignalFormatter processSignalFormatter(inspector.get(), config); - auto tinfo = inspector->build_threadinfo(); + auto tinfo = inspector->get_threadinfo_factory().create(); tinfo->m_pid = 0; tinfo->m_tid = 0; tinfo->m_ptid = -1; tinfo->m_vpid = 1; tinfo->m_exepath = "asdf"; - auto tinfo2 = inspector->build_threadinfo(); + auto tinfo2 = inspector->get_threadinfo_factory().create(); tinfo2->m_pid = 1; tinfo2->m_tid = 1; tinfo2->m_ptid = 0; tinfo2->m_vpid = 2; tinfo2->m_exepath = "qwerty"; - inspector->add_thread(std::move(tinfo)); - inspector->add_thread(std::move(tinfo2)); + inspector->m_thread_manager->add_thread(std::move(tinfo), false); + inspector->m_thread_manager->add_thread(std::move(tinfo2), false); std::vector lineage; - processSignalFormatter.GetProcessLineage(inspector->get_thread_ref(1).get(), lineage); + processSignalFormatter.GetProcessLineage(inspector->m_thread_manager->find_thread(1, true).get(), lineage); int count = collector_stats.GetCounter(CollectorStats::process_lineage_counts); int total = collector_stats.GetCounter(CollectorStats::process_lineage_total); @@ -174,25 +174,25 @@ TEST(ProcessSignalFormatterTest, ProcessWithParentWithSameNameTest) { ProcessSignalFormatter processSignalFormatter(inspector.get(), config); - auto tinfo = inspector->build_threadinfo(); + auto tinfo = inspector->get_threadinfo_factory().create(); tinfo->m_pid = 3; tinfo->m_tid = 3; tinfo->m_ptid = -1; tinfo->m_vpid = 1; - tinfo->m_user.set_uid(43); + tinfo->m_uid = 43; tinfo->m_exepath = "asdf"; - auto tinfo2 = inspector->build_threadinfo(); + auto tinfo2 = inspector->get_threadinfo_factory().create(); tinfo2->m_pid = 1; tinfo2->m_tid = 1; tinfo2->m_ptid = 3; tinfo2->m_vpid = 2; - tinfo2->m_user.set_uid(42); + tinfo2->m_uid = 42; tinfo2->m_exepath = "asdf"; - inspector->add_thread(std::move(tinfo)); - inspector->add_thread(std::move(tinfo2)); + inspector->m_thread_manager->add_thread(std::move(tinfo), false); + inspector->m_thread_manager->add_thread(std::move(tinfo2), false); std::vector lineage; - processSignalFormatter.GetProcessLineage(inspector->get_thread_ref(1).get(), lineage); + processSignalFormatter.GetProcessLineage(inspector->m_thread_manager->find_thread(1, true).get(), lineage); int count = collector_stats.GetCounter(CollectorStats::process_lineage_counts); int total = collector_stats.GetCounter(CollectorStats::process_lineage_total); @@ -219,36 +219,36 @@ TEST(ProcessSignalFormatterTest, ProcessWithTwoParentsTest) { ProcessSignalFormatter processSignalFormatter(inspector.get(), config); - auto tinfo = inspector->build_threadinfo(); + auto tinfo = inspector->get_threadinfo_factory().create(); tinfo->m_pid = 3; tinfo->m_tid = 3; tinfo->m_ptid = -1; tinfo->m_vpid = 1; - tinfo->m_user.set_uid(42); + tinfo->m_uid = 42; tinfo->m_exepath = "asdf"; - auto tinfo2 = inspector->build_threadinfo(); + auto tinfo2 = inspector->get_threadinfo_factory().create(); tinfo2->m_pid = 1; tinfo2->m_tid = 1; tinfo2->m_ptid = 3; tinfo2->m_vpid = 2; - tinfo2->m_user.set_uid(7); + tinfo2->m_uid = 7; tinfo2->m_exepath = "qwerty"; - auto tinfo3 = inspector->build_threadinfo(); + auto tinfo3 = inspector->get_threadinfo_factory().create(); tinfo3->m_pid = 4; tinfo3->m_tid = 4; tinfo3->m_ptid = 1; tinfo3->m_vpid = 9; - tinfo3->m_user.set_uid(8); + tinfo3->m_uid = 8; tinfo3->m_exepath = "uiop"; - inspector->add_thread(std::move(tinfo)); - inspector->add_thread(std::move(tinfo2)); - inspector->add_thread(std::move(tinfo3)); + inspector->m_thread_manager->add_thread(std::move(tinfo), false); + inspector->m_thread_manager->add_thread(std::move(tinfo2), false); + inspector->m_thread_manager->add_thread(std::move(tinfo3), false); std::vector lineage; - processSignalFormatter.GetProcessLineage(inspector->get_thread_ref(4).get(), lineage); + processSignalFormatter.GetProcessLineage(inspector->m_thread_manager->find_thread(4, true).get(), lineage); int count = collector_stats.GetCounter(CollectorStats::process_lineage_counts); int total = collector_stats.GetCounter(CollectorStats::process_lineage_total); @@ -278,36 +278,36 @@ TEST(ProcessSignalFormatterTest, ProcessWithTwoParentsWithTheSameNameTest) { ProcessSignalFormatter processSignalFormatter(inspector.get(), config); - auto tinfo = inspector->build_threadinfo(); + auto tinfo = inspector->get_threadinfo_factory().create(); tinfo->m_pid = 3; tinfo->m_tid = 3; tinfo->m_ptid = -1; tinfo->m_vpid = 1; - tinfo->m_user.set_uid(42); + tinfo->m_uid = 42; tinfo->m_exepath = "asdf"; - auto tinfo2 = inspector->build_threadinfo(); + auto tinfo2 = inspector->get_threadinfo_factory().create(); tinfo2->m_pid = 1; tinfo2->m_tid = 1; tinfo2->m_ptid = 3; tinfo2->m_vpid = 2; - tinfo2->m_user.set_uid(7); + tinfo2->m_uid = 7; tinfo2->m_exepath = "asdf"; - auto tinfo3 = inspector->build_threadinfo(); + auto tinfo3 = inspector->get_threadinfo_factory().create(); tinfo3->m_pid = 4; tinfo3->m_tid = 4; tinfo3->m_ptid = 1; tinfo3->m_vpid = 9; - tinfo3->m_user.set_uid(8); + tinfo3->m_uid = 8; tinfo3->m_exepath = "asdf"; - inspector->add_thread(std::move(tinfo)); - inspector->add_thread(std::move(tinfo2)); - inspector->add_thread(std::move(tinfo3)); + inspector->m_thread_manager->add_thread(std::move(tinfo), false); + inspector->m_thread_manager->add_thread(std::move(tinfo2), false); + inspector->m_thread_manager->add_thread(std::move(tinfo3), false); std::vector lineage; - processSignalFormatter.GetProcessLineage(inspector->get_thread_ref(4).get(), lineage); + processSignalFormatter.GetProcessLineage(inspector->m_thread_manager->find_thread(4, true).get(), lineage); int count = collector_stats.GetCounter(CollectorStats::process_lineage_counts); int total = collector_stats.GetCounter(CollectorStats::process_lineage_total); @@ -334,45 +334,45 @@ TEST(ProcessSignalFormatterTest, ProcessCollapseParentChildWithSameNameTest) { ProcessSignalFormatter processSignalFormatter(inspector.get(), config); - auto tinfo = inspector->build_threadinfo(); + auto tinfo = inspector->get_threadinfo_factory().create(); tinfo->m_pid = 3; tinfo->m_tid = 3; tinfo->m_ptid = -1; tinfo->m_vpid = 1; - tinfo->m_user.set_uid(42); + tinfo->m_uid = 42; tinfo->m_exepath = "asdf"; - auto tinfo2 = inspector->build_threadinfo(); + auto tinfo2 = inspector->get_threadinfo_factory().create(); tinfo2->m_pid = 1; tinfo2->m_tid = 1; tinfo2->m_ptid = 3; tinfo2->m_vpid = 2; - tinfo2->m_user.set_uid(7); + tinfo2->m_uid = 7; tinfo2->m_exepath = "asdf"; - auto tinfo3 = inspector->build_threadinfo(); + auto tinfo3 = inspector->get_threadinfo_factory().create(); tinfo3->m_pid = 4; tinfo3->m_tid = 4; tinfo3->m_ptid = 1; tinfo3->m_vpid = 9; - tinfo3->m_user.set_uid(8); + tinfo3->m_uid = 8; tinfo3->m_exepath = "asdf"; - auto tinfo4 = inspector->build_threadinfo(); + auto tinfo4 = inspector->get_threadinfo_factory().create(); tinfo4->m_pid = 5; tinfo4->m_tid = 5; tinfo4->m_ptid = 4; tinfo4->m_vpid = 10; - tinfo4->m_user.set_uid(9); + tinfo4->m_uid = 9; tinfo4->m_exepath = "qwerty"; - inspector->add_thread(std::move(tinfo)); - inspector->add_thread(std::move(tinfo2)); - inspector->add_thread(std::move(tinfo3)); - inspector->add_thread(std::move(tinfo4)); + inspector->m_thread_manager->add_thread(std::move(tinfo), false); + inspector->m_thread_manager->add_thread(std::move(tinfo2), false); + inspector->m_thread_manager->add_thread(std::move(tinfo3), false); + inspector->m_thread_manager->add_thread(std::move(tinfo4), false); std::vector lineage; - processSignalFormatter.GetProcessLineage(inspector->get_thread_ref(5).get(), lineage); + processSignalFormatter.GetProcessLineage(inspector->m_thread_manager->find_thread(5, true).get(), lineage); int count = collector_stats.GetCounter(CollectorStats::process_lineage_counts); int total = collector_stats.GetCounter(CollectorStats::process_lineage_total); @@ -399,45 +399,45 @@ TEST(ProcessSignalFormatterTest, ProcessCollapseParentChildWithSameName2Test) { ProcessSignalFormatter processSignalFormatter(inspector.get(), config); - auto tinfo = inspector->build_threadinfo(); + auto tinfo = inspector->get_threadinfo_factory().create(); tinfo->m_pid = 3; tinfo->m_tid = 3; tinfo->m_ptid = -1; tinfo->m_vpid = 1; - tinfo->m_user.set_uid(42); + tinfo->m_uid = 42; tinfo->m_exepath = "qwerty"; - auto tinfo2 = inspector->build_threadinfo(); + auto tinfo2 = inspector->get_threadinfo_factory().create(); tinfo2->m_pid = 1; tinfo2->m_tid = 1; tinfo2->m_ptid = 3; tinfo2->m_vpid = 2; - tinfo2->m_user.set_uid(7); + tinfo2->m_uid = 7; tinfo2->m_exepath = "asdf"; - auto tinfo3 = inspector->build_threadinfo(); + auto tinfo3 = inspector->get_threadinfo_factory().create(); tinfo3->m_pid = 4; tinfo3->m_tid = 4; tinfo3->m_ptid = 1; tinfo3->m_vpid = 9; - tinfo3->m_user.set_uid(8); + tinfo3->m_uid = 8; tinfo3->m_exepath = "asdf"; - auto tinfo4 = inspector->build_threadinfo(); + auto tinfo4 = inspector->get_threadinfo_factory().create(); tinfo4->m_pid = 5; tinfo4->m_tid = 5; tinfo4->m_ptid = 4; tinfo4->m_vpid = 10; - tinfo4->m_user.set_uid(9); + tinfo4->m_uid = 9; tinfo4->m_exepath = "asdf"; - inspector->add_thread(std::move(tinfo)); - inspector->add_thread(std::move(tinfo2)); - inspector->add_thread(std::move(tinfo3)); - inspector->add_thread(std::move(tinfo4)); + inspector->m_thread_manager->add_thread(std::move(tinfo), false); + inspector->m_thread_manager->add_thread(std::move(tinfo2), false); + inspector->m_thread_manager->add_thread(std::move(tinfo3), false); + inspector->m_thread_manager->add_thread(std::move(tinfo4), false); std::vector lineage; - processSignalFormatter.GetProcessLineage(inspector->get_thread_ref(5).get(), lineage); + processSignalFormatter.GetProcessLineage(inspector->m_thread_manager->find_thread(5, true).get(), lineage); int count = collector_stats.GetCounter(CollectorStats::process_lineage_counts); int total = collector_stats.GetCounter(CollectorStats::process_lineage_total); @@ -467,45 +467,45 @@ TEST(ProcessSignalFormatterTest, ProcessWithUnrelatedProcessTest) { ProcessSignalFormatter processSignalFormatter(inspector.get(), config); - auto tinfo = inspector->build_threadinfo(); + auto tinfo = inspector->get_threadinfo_factory().create(); tinfo->m_pid = 3; tinfo->m_tid = 3; tinfo->m_ptid = -1; tinfo->m_vpid = 1; - tinfo->m_user.set_uid(42); + tinfo->m_uid = 42; tinfo->m_exepath = "qwerty"; - auto tinfo2 = inspector->build_threadinfo(); + auto tinfo2 = inspector->get_threadinfo_factory().create(); tinfo2->m_pid = 1; tinfo2->m_tid = 1; tinfo2->m_ptid = 3; tinfo2->m_vpid = 2; - tinfo2->m_user.set_uid(7); + tinfo2->m_uid = 7; tinfo2->m_exepath = "asdf"; - auto tinfo3 = inspector->build_threadinfo(); + auto tinfo3 = inspector->get_threadinfo_factory().create(); tinfo3->m_pid = 4; tinfo3->m_tid = 4; tinfo3->m_ptid = 1; tinfo3->m_vpid = 9; - tinfo3->m_user.set_uid(8); + tinfo3->m_uid = 8; tinfo3->m_exepath = "uiop"; - auto tinfo4 = inspector->build_threadinfo(); + auto tinfo4 = inspector->get_threadinfo_factory().create(); tinfo4->m_pid = 5; tinfo4->m_tid = 5; tinfo4->m_ptid = 555; tinfo4->m_vpid = 10; - tinfo4->m_user.set_uid(9); + tinfo4->m_uid = 9; tinfo4->m_exepath = "jkl;"; - inspector->add_thread(std::move(tinfo)); - inspector->add_thread(std::move(tinfo2)); - inspector->add_thread(std::move(tinfo3)); - inspector->add_thread(std::move(tinfo4)); + inspector->m_thread_manager->add_thread(std::move(tinfo), false); + inspector->m_thread_manager->add_thread(std::move(tinfo2), false); + inspector->m_thread_manager->add_thread(std::move(tinfo3), false); + inspector->m_thread_manager->add_thread(std::move(tinfo4), false); std::vector lineage; - processSignalFormatter.GetProcessLineage(inspector->get_thread_ref(4).get(), lineage); + processSignalFormatter.GetProcessLineage(inspector->m_thread_manager->find_thread(4, true).get(), lineage); int count = collector_stats.GetCounter(CollectorStats::process_lineage_counts); int total = collector_stats.GetCounter(CollectorStats::process_lineage_total); @@ -535,31 +535,31 @@ TEST(ProcessSignalFormatterTest, CountTwoCounterCallsTest) { ProcessSignalFormatter processSignalFormatter(inspector.get(), config); - auto tinfo = inspector->build_threadinfo(); + auto tinfo = inspector->get_threadinfo_factory().create(); tinfo->m_pid = 1; tinfo->m_tid = 1; tinfo->m_ptid = 555; tinfo->m_vpid = 10; - tinfo->m_user.set_uid(9); + tinfo->m_uid = 9; tinfo->m_exepath = "jkl;"; - inspector->add_thread(std::move(tinfo)); + inspector->m_thread_manager->add_thread(std::move(tinfo), false); std::vector lineage; - processSignalFormatter.GetProcessLineage(inspector->get_thread_ref(1).get(), lineage); + processSignalFormatter.GetProcessLineage(inspector->m_thread_manager->find_thread(1, true).get(), lineage); - auto tinfo2 = inspector->build_threadinfo(); + auto tinfo2 = inspector->get_threadinfo_factory().create(); tinfo2->m_pid = 2; tinfo2->m_tid = 2; tinfo2->m_ptid = 555; tinfo2->m_vpid = 10; - tinfo2->m_user.set_uid(9); + tinfo2->m_uid = 9; tinfo2->m_exepath = "jkl;"; - inspector->add_thread(std::move(tinfo2)); + inspector->m_thread_manager->add_thread(std::move(tinfo2), false); std::vector lineage2; - processSignalFormatter.GetProcessLineage(inspector->get_thread_ref(2).get(), lineage2); + processSignalFormatter.GetProcessLineage(inspector->m_thread_manager->find_thread(2, true).get(), lineage2); int count = collector_stats.GetCounter(CollectorStats::process_lineage_counts); int total = collector_stats.GetCounter(CollectorStats::process_lineage_total); @@ -577,45 +577,46 @@ TEST(ProcessSignalFormatterTest, CountTwoCounterCallsTest) { } TEST(ProcessSignalFormatterTest, Rox3377ProcessLineageWithNoVPidTest) { + // This test verifies lineage traversal stops at the container boundary. + // Originally tested vpid=0 + container_id fallback (ROX-3377), but + // container_id is now a dynamic field from the container plugin. + // Instead, test boundary detection via pid==vpid (namespace init process). std::unique_ptr inspector(new sinsp()); CollectorStats& collector_stats = CollectorStats::GetOrCreate(); CollectorConfig config; ProcessSignalFormatter processSignalFormatter(inspector.get(), config); - auto tinfo = inspector->build_threadinfo(); + auto tinfo = inspector->get_threadinfo_factory().create(); tinfo->m_pid = 3; tinfo->m_tid = 3; tinfo->m_ptid = -1; - tinfo->m_vpid = 0; - tinfo->m_user.set_uid(42); - tinfo->m_container_id = ""; + tinfo->m_vpid = 3; + tinfo->m_uid = 42; tinfo->m_exepath = "qwerty"; - auto tinfo2 = inspector->build_threadinfo(); + auto tinfo2 = inspector->get_threadinfo_factory().create(); tinfo2->m_pid = 1; tinfo2->m_tid = 1; tinfo2->m_ptid = 3; - tinfo2->m_vpid = 0; - tinfo2->m_user.set_uid(7); - tinfo2->m_container_id = "id"; + tinfo2->m_vpid = 2; + tinfo2->m_uid = 7; tinfo2->m_exepath = "asdf"; - auto tinfo3 = inspector->build_threadinfo(); + auto tinfo3 = inspector->get_threadinfo_factory().create(); tinfo3->m_pid = 4; tinfo3->m_tid = 4; tinfo3->m_ptid = 1; - tinfo3->m_vpid = 0; - tinfo3->m_user.set_uid(8); - tinfo3->m_container_id = "id"; + tinfo3->m_vpid = 9; + tinfo3->m_uid = 8; tinfo3->m_exepath = "uiop"; - inspector->add_thread(std::move(tinfo)); - inspector->add_thread(std::move(tinfo2)); - inspector->add_thread(std::move(tinfo3)); + inspector->m_thread_manager->add_thread(std::move(tinfo), false); + inspector->m_thread_manager->add_thread(std::move(tinfo2), false); + inspector->m_thread_manager->add_thread(std::move(tinfo3), false); std::vector lineage; - processSignalFormatter.GetProcessLineage(inspector->get_thread_ref(4).get(), lineage); + processSignalFormatter.GetProcessLineage(inspector->m_thread_manager->find_thread(4, true).get(), lineage); int count = collector_stats.GetCounter(CollectorStats::process_lineage_counts); int total = collector_stats.GetCounter(CollectorStats::process_lineage_total); @@ -641,13 +642,12 @@ TEST(ProcessSignalFormatterTest, ProcessArguments) { ProcessSignalFormatter processSignalFormatter(inspector.get(), config); - auto tinfo = inspector->build_threadinfo(); + auto tinfo = inspector->get_threadinfo_factory().create(); tinfo->m_pid = 3; tinfo->m_tid = 3; tinfo->m_ptid = -1; tinfo->m_vpid = 0; - tinfo->m_user.set_uid(42); - tinfo->m_container_id = ""; + tinfo->m_uid = 42; tinfo->m_exepath = "qwerty"; std::vector args = {std::string("args")}; @@ -671,13 +671,12 @@ TEST(ProcessSignalFormatterTest, NoProcessArguments) { config.SetDisableProcessArguments(true); ProcessSignalFormatter processSignalFormatter(inspector.get(), config); - auto tinfo = inspector->build_threadinfo(); + auto tinfo = inspector->get_threadinfo_factory().create(); tinfo->m_pid = 3; tinfo->m_tid = 3; tinfo->m_ptid = -1; tinfo->m_vpid = 0; - tinfo->m_user.set_uid(42); - tinfo->m_container_id = ""; + tinfo->m_uid = 42; tinfo->m_exepath = "qwerty"; std::vector args = {std::string("args")}; diff --git a/collector/test/SystemInspectorServiceTest.cpp b/collector/test/SystemInspectorServiceTest.cpp index a6ed01e2e1..a02ccab23c 100644 --- a/collector/test/SystemInspectorServiceTest.cpp +++ b/collector/test/SystemInspectorServiceTest.cpp @@ -7,32 +7,33 @@ namespace collector::system_inspector { TEST(SystemInspectorServiceTest, FilterEvent) { std::unique_ptr inspector(new sinsp()); + const auto& factory = inspector->get_threadinfo_factory(); - sinsp_threadinfo regular_process(inspector.get()); - regular_process.m_exepath = "/bin/busybox"; - regular_process.m_comm = "sleep"; + auto regular_process = factory.create(); + regular_process->m_exepath = "/bin/busybox"; + regular_process->m_comm = "sleep"; - sinsp_threadinfo runc_process(inspector.get()); - runc_process.m_exepath = "runc"; - runc_process.m_comm = "6"; + auto runc_process = factory.create(); + runc_process->m_exepath = "runc"; + runc_process->m_comm = "6"; - sinsp_threadinfo proc_self_process(inspector.get()); - proc_self_process.m_exepath = "/proc/self/exe"; - proc_self_process.m_comm = "6"; + auto proc_self_process = factory.create(); + proc_self_process->m_exepath = "/proc/self/exe"; + proc_self_process->m_comm = "6"; - sinsp_threadinfo memfd_process(inspector.get()); - memfd_process.m_exepath = "memfd:runc_cloned:/proc/self/exe"; - memfd_process.m_comm = "6"; + auto memfd_process = factory.create(); + memfd_process->m_exepath = "memfd:runc_cloned:/proc/self/exe"; + memfd_process->m_comm = "6"; struct test_t { const sinsp_threadinfo* tinfo; bool expected; }; std::vector tests{ - {®ular_process, true}, - {&runc_process, false}, - {&proc_self_process, false}, - {&memfd_process, false}, + {regular_process.get(), true}, + {runc_process.get(), false}, + {proc_self_process.get(), false}, + {memfd_process.get(), false}, }; for (const auto& t : tests) { diff --git a/falcosecurity-libs b/falcosecurity-libs index af2b6161c6..d0fb1702cf 160000 --- a/falcosecurity-libs +++ b/falcosecurity-libs @@ -1 +1 @@ -Subproject commit af2b6161c6060ff47b843d9ff129b9de2ed03a35 +Subproject commit d0fb1702cfbaf7a04cf87b65e3df99f1cc38a2ef