Skip to content

Commit c94935a

Browse files
bmiddhaCopilot
andcommitted
Rewrite fstrace on eBPF (Rust + Aya) with full feature parity
Replace the C/ptrace implementation with an eBPF tracer written in Rust on top of Aya. Tracing now happens in-kernel via fexit probes on the traced syscalls plus sched_process_{fork,exec,exit} tracepoints and a ring buffer, instead of stopping the target on every syscall from userspace. No features are dropped: full syscall coverage, path resolution (cwd/dirfd/AT_FDCWD + normalization), the fd->path cache, process-tree scoping, and exit/signal/ env passthrough are all preserved, alongside filters, debounce, and per-process logging. Architecture (privileged/unprivileged split) - fstrace-daemon: the privileged half. Loads and attaches the embedded eBPF object, owns the ring buffer and the traced-pid map, and serves multiple clients over a Unix socket. It routes events purely by root-namespace pid, so it works correctly from inside its own pid+mount namespace and needs no --pid=host. When tracefs is absent (e.g. a scratch container) it mounts tracefs itself before loading. - fstrace: the unprivileged client. Runs as the invoking user with no sudo, forks the target, registers it with the daemon, and streams reports on fd 3. Exit code, signals, and environment are passed through untouched. - fstrace-common: shared #[repr(C)] POD event structs and typed enums (Syscall/AccessType/FileType) used by both the eBPF and userspace sides. - fstrace-ebpf: the eBPF programs (fexit per traced syscall + fork/exec/exit tracepoints writing into a ring buffer). The fork tracepoint's child_pid offset is read from tracefs at load time rather than hardcoded, tracking the ~6.16 __data_loc layout change. Packaging & deployment - systemd service + socket units (socket-activation friendly), a SysV init.d script, and man pages for both binaries. - A FROM-scratch Dockerfile for the fully static (musl, static-PIE) daemon; bind-mount the socket dir + /sys/kernel/btf and run --privileged to trace host commands across namespaces. Rootful runtime required (rootless cannot create BPF maps). - npm distribution package retained under node/fstrace. Tooling & lifecycle (xtask) - The entire repo lifecycle runs through `cargo xtask`: fmt, lint, test, check, build, clean, dist (cross-compile), docker-build/docker-test, and vm-test. All former shell scripts are ported to Rust. - Cross-compilation for {x86_64, aarch64} x {gnu, musl} produces release tarballs. Testing - Host-side unit tests cover path normalization, filters, debounce, classification, and resolution. - The privileged loader and full syscall coverage run end-to-end inside a throwaway QEMU VM so eBPF never loads on the host. The harness (all Rust) downloads prebuilt upstream kernels from the Ubuntu mainline archive, builds an initramfs from a Docker image rootfs (Ubuntu and Alpine/musl), and direct-boots QEMU, parsing a console result sentinel. A thin fstrace-scenario binary issues the raw syscalls under trace. Coverage includes 23 syscall/behavioural tests (incl. near-PATH_MAX paths, multi-client isolation, passthrough), namespace isolation with a containerized daemon, and systemd socket-activation. Kernel support - Verified across LTS {5.10, 5.15, 6.1, 6.6, 6.12, 6.18}, stable 7.1, and mainline 7.2-rc. Feature floor is the ring buffer (5.8); 5.4 and older lack it. Requirements and the support matrix are documented in docs/kernel-support.md. Docs - README plus focused docs/ pages: architecture, building, testing, deployment, reports-and-fd3, and kernel-support. CI - Three jobs, all driven by the cargo xtask alias: format/clippy/host tests, a vm-e2e matrix (8 kernels x {ubuntu, alpine}), and a cross-dist matrix (4 targets) uploading release tarballs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent a7a2b40 commit c94935a

69 files changed

Lines changed: 8831 additions & 3497 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cargo/config.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Cross-compilation linkers for the supported release targets.
2+
#
3+
# aarch64 builds need a cross linker; install it with:
4+
# apt-get install -y gcc-aarch64-linux-gnu
5+
#
6+
# x86_64-unknown-linux-musl links self-contained via rust-lld and needs no
7+
# extra toolchain. See `cargo xtask dist` and docs/building.md.
8+
9+
[alias]
10+
xtask = "run --package xtask --"
11+
12+
[target.aarch64-unknown-linux-gnu]
13+
linker = "aarch64-linux-gnu-gcc"
14+
15+
[target.aarch64-unknown-linux-musl]
16+
linker = "aarch64-linux-gnu-gcc"

.clang-format

Lines changed: 0 additions & 7 deletions
This file was deleted.

.devcontainer/devcontainer.json

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
{
2-
"image": "mcr.microsoft.com/devcontainers/cpp:1-ubuntu-24.04",
2+
"image": "mcr.microsoft.com/devcontainers/rust:1-bookworm",
33
"features": {
44
"ghcr.io/devcontainers/features/node:2": {
5-
"nodeGypDependencies": true,
6-
"version": "18",
7-
"pnpmVersion": "latest",
8-
"nvmVersion": "latest"
5+
"version": "18"
96
}
107
},
8+
"onCreateCommand": "rustup show && rustup component add rust-src clippy rustfmt && cargo install bpf-linker --locked",
119
"customizations": {
1210
"vscode": {
1311
"extensions": [
14-
"ms-vscode.cmake-tools",
15-
"ms-vscode.cpptools"
12+
"rust-lang.rust-analyzer",
13+
"vadimcn.vscode-lldb"
1614
]
1715
}
1816
}
19-
}
17+
}

.github/workflows/ci.yml

Lines changed: 94 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,100 @@ name: ci
33
on: [push, pull_request]
44

55
jobs:
6-
cmake-release:
7-
name: CMake Build and Test
6+
lint-and-unit:
7+
name: Format, Clippy & Host Unit Tests
88
runs-on: ubuntu-latest
9+
steps:
10+
- name: Checkout code
11+
uses: actions/checkout@v4
12+
13+
- name: Install Rust toolchain
14+
run: |
15+
rustup show
16+
rustup component add rustfmt clippy rust-src
17+
cargo install bpf-linker --locked
18+
19+
- name: Format check
20+
run: cargo xtask fmt --check
21+
22+
- name: Clippy
23+
run: cargo xtask lint
24+
25+
- name: Build (release)
26+
run: cargo xtask build
27+
28+
- name: Host unit tests
29+
run: cargo xtask test
30+
31+
vm-e2e:
32+
name: VM e2e (${{ matrix.image }} on ${{ matrix.release }})
33+
runs-on: ubuntu-latest
34+
needs: lint-and-unit
35+
strategy:
36+
fail-fast: false
37+
matrix:
38+
release: ["5.10", "5.15", "6.1", "6.6", "6.12", "6.18", "7.1", "7.2-rc"]
39+
image:
40+
- docker.io/library/ubuntu:latest
41+
- docker.io/library/alpine:latest
42+
steps:
43+
- name: Checkout code
44+
uses: actions/checkout@v4
945

46+
- name: Install Rust toolchain
47+
run: |
48+
rustup show
49+
rustup component add rust-src
50+
rustup target add x86_64-unknown-linux-musl
51+
cargo install bpf-linker --locked
52+
53+
- name: Install QEMU & tooling
54+
run: |
55+
sudo apt-get update
56+
sudo apt-get install -y qemu-system-x86 cpio binutils curl
57+
58+
- name: Enable KVM (best effort)
59+
run: sudo chmod 666 /dev/kvm || true
60+
61+
- name: Run VM e2e suite
62+
env:
63+
FSTRACE_VM_IMAGE: ${{ matrix.image }}
64+
run: cargo xtask vm-test --release ${{ matrix.release }}
65+
66+
cross-dist:
67+
name: Cross-Compile Release Artifacts
68+
runs-on: ubuntu-latest
69+
needs: lint-and-unit
70+
strategy:
71+
fail-fast: false
72+
matrix:
73+
target:
74+
- x86_64-unknown-linux-gnu
75+
- x86_64-unknown-linux-musl
76+
- aarch64-unknown-linux-gnu
77+
- aarch64-unknown-linux-musl
1078
steps:
11-
- name: Checkout code
12-
uses: actions/checkout@v4
13-
14-
- name: Build Project (Release)
15-
uses: threeal/cmake-action@v2.1.0
16-
with:
17-
options: |
18-
CMAKE_BUILD_TYPE=Release
19-
20-
- name: Test Project (Release)
21-
uses: threeal/ctest-action@v1.1.0
22-
23-
- name: Build Project (Debug)
24-
uses: threeal/cmake-action@v2.1.0
25-
with:
26-
options: |
27-
CMAKE_BUILD_TYPE=Debug
28-
29-
- name: Test Project (Debug)
30-
uses: threeal/ctest-action@v1.1.0
79+
- name: Checkout code
80+
uses: actions/checkout@v4
81+
82+
- name: Install Rust toolchain
83+
run: |
84+
rustup show
85+
rustup component add rust-src
86+
rustup target add ${{ matrix.target }}
87+
cargo install bpf-linker --locked
88+
89+
- name: Install aarch64 cross linker
90+
if: startsWith(matrix.target, 'aarch64')
91+
run: |
92+
sudo apt-get update
93+
sudo apt-get install -y gcc-aarch64-linux-gnu
94+
95+
- name: Build release tarball
96+
run: cargo xtask dist --target ${{ matrix.target }}
97+
98+
- name: Upload artifact
99+
uses: actions/upload-artifact@v4
100+
with:
101+
name: fstrace-${{ matrix.target }}
102+
path: dist/*.tar.gz

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
build/
1+
/target
2+
/dist
3+
tests/vm/.cache/

.vscode/c_cpp_properties.json

Lines changed: 0 additions & 22 deletions
This file was deleted.

.vscode/launch.json

Lines changed: 5 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -2,71 +2,13 @@
22
"version": "0.2.0",
33
"configurations": [
44
{
5-
"name": "(gdb) fstrace",
6-
"type": "cppdbg",
5+
"name": "Debug fstrace (lldb)",
6+
"type": "lldb",
77
"request": "launch",
8-
"program": "${workspaceFolder}/build/fstrace-debug",
8+
"program": "${workspaceFolder}/target/release/fstrace",
99
"args": ["/usr/bin/cat", "${workspaceFolder}/README.md"],
10-
"stopAtEntry": false,
11-
"cwd": "${fileDirname}",
12-
"environment": [],
13-
"externalConsole": false,
14-
"MIMode": "gdb",
15-
"setupCommands": [
16-
{
17-
"description": "Enable pretty-printing for gdb",
18-
"text": "-enable-pretty-printing",
19-
"ignoreFailures": true
20-
},
21-
{
22-
"description": "Set Disassembly Flavor to Intel",
23-
"text": "-gdb-set disassembly-flavor intel",
24-
"ignoreFailures": true
25-
}
26-
]
27-
},
28-
{
29-
"name": "(gdb) Attach",
30-
"type": "cppdbg",
31-
"request": "attach",
32-
"program": "${workspaceFolder}/build/fstrace",
33-
"MIMode": "gdb",
34-
"setupCommands": [
35-
{
36-
"description": "Enable pretty-printing for gdb",
37-
"text": "-enable-pretty-printing",
38-
"ignoreFailures": true
39-
},
40-
{
41-
"description": "Set Disassembly Flavor to Intel",
42-
"text": "-gdb-set disassembly-flavor intel",
43-
"ignoreFailures": true
44-
}
45-
]
46-
},
47-
{
48-
"name": "(gdb) test_fstrace",
49-
"type": "cppdbg",
50-
"request": "launch",
51-
"program": "${workspaceFolder}/build/test_fstrace",
52-
"stopAtEntry": true,
53-
"cwd": "${fileDirname}",
54-
"environment": [],
55-
"externalConsole": false,
56-
"MIMode": "gdb",
57-
"setupCommands": [
58-
{
59-
"description": "Enable pretty-printing for gdb",
60-
"text": "-enable-pretty-printing",
61-
"ignoreFailures": true
62-
},
63-
{
64-
"description": "Set Disassembly Flavor to Intel",
65-
"text": "-gdb-set disassembly-flavor intel",
66-
"ignoreFailures": true
67-
}
68-
],
69-
"preLaunchTask": "CMake: clean rebuild"
10+
"cwd": "${workspaceFolder}",
11+
"env": { "RUST_LOG": "fstrace=debug" }
7012
}
7113
]
7214
}

CMakeLists.txt

Lines changed: 0 additions & 75 deletions
This file was deleted.

0 commit comments

Comments
 (0)