Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 14 additions & 20 deletions .github/actions/install-zig/action.yml
Original file line number Diff line number Diff line change
@@ -1,48 +1,42 @@
name: 'install-zig'
description: 'Install zig compiler and make it available in PATH.'

inputs:
zig_download_retries:
description: 'Number of retries for the download'
required: false
default: 20

runs:
using: "composite"
steps:
- name: Store zig version as local output
shell: bash
id: store
env:
ZIG_VERSION: '0.14.0-dev.2591+5333d2443'
ZIG_VERSION: '0.14.1'
run: |
echo "zig_version=${ZIG_VERSION}" >> "$GITHUB_OUTPUT"

# TODO: this is only needed because we are using a development version of zig,
# since we need https://github.com/ziglang/zig/pull/21253 to be included.
# Development versions of zig are not kept alive forever, but get overridden.
# We cache it to keep it alive.
- name: Download zig (cached)
id: cache-zig
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
with:
path: zig
key: zig-${{ runner.os }}-${{ runner.arch }}-${{ steps.store.outputs.zig_version }}

- name: Download zig
if: steps.cache-zig.outputs.cache-hit != 'true'
shell: bash
run: |
curl -L -o zig.tar.xz https://ziglang.org/builds/zig-linux-$(uname -m)-${{ steps.store.outputs.zig_version }}.tar.xz
wget --tries=${{ inputs.zig_download_retries }} -O zig.tar.xz https://ziglang.org/download/${{ steps.store.outputs.zig_version }}/zig-$(uname -m)-linux-${{ steps.store.outputs.zig_version }}.tar.xz
tar -xvf zig.tar.xz

cat > zig-linux-$(uname -m)-${{ steps.store.outputs.zig_version }}/zig-cc <<EOF
cat > zig-$(uname -m)-linux-${{ steps.store.outputs.zig_version }}/zig-cc <<EOF
#!/bin/bash
exec zig cc -target $(uname -m)-linux-gnu.2.17 -mcpu=baseline "\$@"
EOF
chmod +x zig-linux-$(uname -m)-${{ steps.store.outputs.zig_version }}/zig-cc
chmod +x zig-$(uname -m)-linux-${{ steps.store.outputs.zig_version }}/zig-cc

cat > zig-linux-$(uname -m)-${{ steps.store.outputs.zig_version }}/zig-c++ <<EOF
cat > zig-$(uname -m)-linux-${{ steps.store.outputs.zig_version }}/zig-c++ <<EOF
#!/bin/bash
exec zig c++ -target $(uname -m)-linux-gnu.2.17 -mcpu=baseline "\$@"
EOF
chmod +x zig-linux-$(uname -m)-${{ steps.store.outputs.zig_version }}/zig-c++
chmod +x zig-$(uname -m)-linux-${{ steps.store.outputs.zig_version }}/zig-c++

mv zig-linux-$(uname -m)-${{ steps.store.outputs.zig_version }}/ zig
mv zig-$(uname -m)-linux-${{ steps.store.outputs.zig_version }}/ zig

- name: Setup zig
shell: bash
Expand All @@ -52,4 +46,4 @@ runs:
echo "CC=zig-cc" >> $GITHUB_ENV
echo "CXX=zig-c++" >> $GITHUB_ENV
echo "AR=zig ar" >> $GITHUB_ENV
echo "RANLIB=zig ranlib" >> $GITHUB_ENV
echo "RANLIB=zig ranlib" >> $GITHUB_ENV
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ jobs:
steps:
- name: Install deps ⛓️
run: |
# Use 20250630T203427Z debian apt snapshot as it still contains support for buster.
printf "deb http://snapshot.debian.org/archive/debian/20250630T203427Z buster main\ndeb http://snapshot.debian.org/archive/debian-security/20250630T203427Z buster/updates main\ndeb http://snapshot.debian.org/archive/debian/20250630T203427Z buster-updates main" > /etc/apt/sources.list
apt update && apt install -y --no-install-recommends curl ca-certificates build-essential git clang llvm pkg-config autoconf automake libtool libelf-dev wget libc-ares-dev libcurl4-openssl-dev libssl-dev libtbb-dev libjq-dev libjsoncpp-dev libgrpc++-dev protobuf-compiler-grpc libgtest-dev libprotobuf-dev linux-headers-${{ matrix.arch }}

- name: Install a recent version of CMake ⛓️
Expand Down
32 changes: 32 additions & 0 deletions driver/bpf/configure/KERNFS_NODE_PARENT/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/*

Copyright (C) 2025 The Falco Authors.

This file is dual licensed under either the MIT or GPL 2. See MIT.txt
or GPL2.txt for full copies of the license.

*/

/*
* Check that kernfs_node's field `parent` exists.
* See 6.15 kernel commit it is named __parent:
* https://github.com/torvalds/linux/commit/633488947ef66b194377411322dc9e12aab79b65
*/

#include "../../quirks.h"
#include "../../ppm_events_public.h"
#include "../../types.h"

// struct kernfs_node declaration
#include <linux/kernfs.h>

BPF_PROBE("signal/", signal_deliver, signal_deliver_args) {
struct kernfs_node *parent;
struct kernfs_node node;

parent = node.parent;
return 0;
}

char __license[] __bpf_section("license") = "Dual MIT/GPL";
4 changes: 4 additions & 0 deletions driver/bpf/fillers.h
Original file line number Diff line number Diff line change
Expand Up @@ -1810,7 +1810,11 @@ static __always_inline int __bpf_append_cgroup(struct css_set *cgroups,
for(int k = 0; k < MAX_CGROUP_PATHS; ++k) {
if(kn) {
cgroup_path[k] = (char *)_READ(kn->name);
#ifdef HAS_KERNFS_NODE_PARENT
kn = _READ(kn->parent);
#else
kn = _READ(kn->__parent);
#endif
} else {
cgroup_path[k] = NULL;
}
Expand Down
4 changes: 4 additions & 0 deletions driver/modern_bpf/definitions/struct_flavors.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ struct inode___v6_11 {
uint32_t i_ctime_nsec;
};

struct kernfs_node___v6_15 {
struct kernfs_node *__parent;
};

#ifndef BPF_NO_PRESERVE_ACCESS_INDEX
#pragma clang attribute pop
#endif
Expand Down
7 changes: 6 additions & 1 deletion driver/modern_bpf/helpers/store/auxmap_store_params.h
Original file line number Diff line number Diff line change
Expand Up @@ -1296,7 +1296,12 @@ static __always_inline uint16_t store_cgroup_subsys(struct auxiliary_map *auxmap
}
path_components++;
BPF_CORE_READ_INTO(&cgroup_path_pointers[k], kn, name);
BPF_CORE_READ_INTO(&kn, kn, parent);
if(bpf_core_field_exists(kn->parent)) {
BPF_CORE_READ_INTO(&kn, kn, parent);
} else {
struct kernfs_node___v6_15 *kn_v6_15 = (void *)kn;
BPF_CORE_READ_INTO(&kn, kn_v6_15, __parent);
}
}

/* Reconstruct the path in reverse, using previously collected pointers.
Expand Down
67 changes: 47 additions & 20 deletions test/drivers/event_class/event_class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,16 +287,17 @@ void event_test::client_reuse_address_port(int32_t socketfd) {
sizeof(option_value)),
NOT_EQUAL,
-1);
assert_syscall_state(SYSCALL_SUCCESS,
"setsockopt (client port)",
syscall(__NR_setsockopt,
socketfd,
SOL_SOCKET,
SO_REUSEPORT,
&option_value,
sizeof(option_value)),
NOT_EQUAL,
-1);

// Commit https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git/commit/?id=5b0af621c3f6
// restricts SO_REUSEPORT socket option to inet sockets: this means that the following call is
// going to fail on unix sockets, and we have no way to prevent it. For this reason, simply
// ignore its return value and hope any subsequent call to bind is going to succeed.
syscall(__NR_setsockopt,
socketfd,
SOL_SOCKET,
SO_REUSEPORT,
&option_value,
sizeof(option_value));
}

void event_test::server_reuse_address_port(int32_t socketfd) {
Expand All @@ -312,16 +313,17 @@ void event_test::server_reuse_address_port(int32_t socketfd) {
sizeof(option_value)),
NOT_EQUAL,
-1);
assert_syscall_state(SYSCALL_SUCCESS,
"setsockopt (server port)",
syscall(__NR_setsockopt,
socketfd,
SOL_SOCKET,
SO_REUSEPORT,
&option_value,
sizeof(option_value)),
NOT_EQUAL,
-1);

// Commit https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git/commit/?id=5b0af621c3f6
// restricts SO_REUSEPORT socket option to inet sockets: this means that the following call is
// going to fail on unix sockets, and we have no way to prevent it. For this reason, simply
// ignore its return value and hope any subsequent call to bind is going to succeed.
syscall(__NR_setsockopt,
socketfd,
SOL_SOCKET,
SO_REUSEPORT,
&option_value,
sizeof(option_value));
}

void event_test::client_fill_sockaddr_in(sockaddr_in* sockaddr,
Expand Down Expand Up @@ -1104,6 +1106,31 @@ void event_test::assert_charbuf_param(int param_num, const char* param) {
<< VALUE_NOT_CORRECT << m_current_param << std::endl;
}

void event_test::assert_charbuf_param_any_of(const int param_num,
const std::vector<const char*>& candidates) {
assert_param_boundaries(param_num);
ASSERT_GT(candidates.size(), 0);
const auto& [param_value, param_size] = m_event_params[m_current_param];
for(const auto& candidate : candidates) {
// 'strlen()' does not include the terminating null byte while drivers adds it.
if(const auto candidate_size = strlen(candidate) + 1; param_size != candidate_size) {
continue;
}
if(!strncmp(param_value, candidate, param_size)) {
return;
}
}

// The parameter didn't match any of the provided candidates. Fail with a useful error message.
std::ostringstream oss;
oss << VALUE_NOT_CORRECT << m_current_param << ", value = \"" << param_value
<< "\". It must be equal to any of the provided candidates:\n";
for(const auto& value : candidates) {
oss << "- \"" << std::string{value} << "\"\n";
}
FAIL() << oss.str();
}

void event_test::assert_charbuf_array_param(int param_num, const char** param) {
assert_param_boundaries(param_num);
uint16_t total_len = 0;
Expand Down
2 changes: 2 additions & 0 deletions test/drivers/event_class/event_class.h
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,8 @@ class event_test {
*/
void assert_charbuf_param(int param_num, const char* param);

void assert_charbuf_param_any_of(int param_num, const std::vector<const char*>& candidates);

/**
* @brief Assert that the parameter is a `charbuf` array and
* compare element per element the array with the one passed as a parameter `param`.
Expand Down
23 changes: 15 additions & 8 deletions test/drivers/test_suites/syscall_exit_suite/execveat_x.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -528,8 +528,8 @@ TEST(SyscallExit, execveatX_execve_exit_comm_equal_to_fd) {
/*=============================== TRIGGER SYSCALL ===========================*/

/* Prepare the execve args */
const char *exe_path = "/usr/bin/echo";
int dirfd = open(exe_path, O_RDONLY);
const std::string exe_path{"/usr/bin/echo"};
int dirfd = open(exe_path.c_str(), O_RDONLY);
if(dirfd < 0) {
FAIL() << "failed to open the file\n";
}
Expand Down Expand Up @@ -601,14 +601,21 @@ TEST(SyscallExit, execveatX_execve_exit_comm_equal_to_fd) {
evt_test->assert_charbuf_array_param(3, &argv[1]);

/* Parameter 14: comm (type: PT_CHARBUF) */
// This is exactly the behavior that we obtain using the `AT_EMPTY_PATH` flag
// https://github.com/torvalds/linux/blob/master/fs/exec.c#L1600
// https://github.com/torvalds/linux/blob/master/fs/exec.c#L1425
std::string comm = std::to_string(dirfd);
evt_test->assert_charbuf_param(14, comm.c_str());
// For comm parameter, we expect a different value depending on the kernel version. For kernel
// versions lower than 6.14, if the `AT_EMPTY_PATH` flag is specified while invoking execveat,
// we expect the comm value to be set to the dirfd numeric value. Starting from 6.14
// (https://github.com/torvalds/linux/commit/543841d1806029889c2f69f040e88b247aba8e22), this
// strange behaviour has been fixed, and the exact same execveat invocation results in the comm
// value to be correctly set to the dentry's filename value. For this reason, we must account
// for both scenarios.
const std::vector comm_candidates{
std::to_string(dirfd).c_str(), // valid for kernel < 6.14
exe_path.substr(exe_path.find_last_of('/') + 1).c_str(), // valid since kernel >= 6.14
};
evt_test->assert_charbuf_param_any_of(14, comm_candidates);

/* Parameter 28: trusted_exepath (type: PT_FSPATH) */
evt_test->assert_charbuf_param(28, exe_path);
evt_test->assert_charbuf_param(28, exe_path.c_str());

/*=============================== ASSERT PARAMETERS ===========================*/

Expand Down
Loading
Loading