Skip to content

Commit c2f31a8

Browse files
committed
feat(ebpf): use bpf_loop for local d_path implementation
Part of ROX-32059. The local implementation of d_path now uses bpf_loop, which enables it to iterate further than the previously implemented loop that did at most 16 levels of path.
1 parent 37d5783 commit c2f31a8

File tree

2 files changed

+110
-67
lines changed

2 files changed

+110
-67
lines changed

fact-ebpf/src/bpf/d_path.h

Lines changed: 78 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,74 @@
11
#pragma once
22

33
// clang-format off
4-
#include "maps.h"
54
#include "vmlinux.h"
65

6+
#include "maps.h"
7+
78
#include <bpf/bpf_helpers.h>
89
#include <bpf/bpf_core_read.h>
910
// clang-format on
1011

12+
struct d_path_ctx {
13+
struct helper_t* helper;
14+
struct path root;
15+
struct mount* mnt;
16+
struct dentry* dentry;
17+
int offset;
18+
int buflen;
19+
bool success;
20+
};
21+
22+
static long __d_path_inner(uint32_t index, void* _ctx) {
23+
struct d_path_ctx* ctx = (struct d_path_ctx*)_ctx;
24+
struct dentry* dentry = ctx->dentry;
25+
struct dentry* parent = BPF_CORE_READ(dentry, d_parent);
26+
struct mount* mnt = ctx->mnt;
27+
struct dentry* mnt_root = BPF_CORE_READ(mnt, mnt.mnt_root);
28+
29+
if (dentry == mnt_root) {
30+
struct mount* m = BPF_CORE_READ(mnt, mnt_parent);
31+
if (m != mnt) {
32+
ctx->dentry = BPF_CORE_READ(mnt, mnt_mountpoint);
33+
ctx->mnt = m;
34+
return 0;
35+
}
36+
ctx->success = true;
37+
return 1;
38+
}
39+
40+
if (dentry == parent) {
41+
return 1;
42+
}
43+
44+
struct qstr d_name;
45+
BPF_CORE_READ_INTO(&d_name, dentry, d_name);
46+
int len = d_name.len & (PATH_MAX - 1);
47+
if (len <= 0 || len >= ctx->buflen) {
48+
return 1;
49+
}
50+
51+
int offset = ctx->offset - len;
52+
if (offset <= 0) {
53+
return 1;
54+
}
55+
offset &= PATH_MAX - 1;
56+
57+
if (bpf_probe_read_kernel(&ctx->helper->buf[offset], len, d_name.name) != 0) {
58+
return 1;
59+
}
60+
61+
offset--;
62+
if (offset <= 0) {
63+
return 1;
64+
}
65+
ctx->helper->buf[offset] = '/';
66+
67+
ctx->offset = offset;
68+
ctx->dentry = parent;
69+
return 0;
70+
}
71+
1172
/**
1273
* Reimplementation of the kernel d_path function.
1374
*
@@ -19,66 +80,30 @@ __always_inline static long __d_path(const struct path* path, char* buf, int buf
1980
return -1;
2081
}
2182

22-
struct helper_t* helper = get_helper();
23-
if (helper == NULL) {
83+
struct d_path_ctx ctx = {
84+
.buflen = buflen,
85+
.helper = get_helper(),
86+
.offset = (buflen - 1) & (PATH_MAX - 1),
87+
};
88+
89+
if (ctx.helper == NULL) {
2490
return -1;
2591
}
2692

2793
struct task_struct* task = (struct task_struct*)bpf_get_current_task();
28-
int offset = (buflen - 1) & (PATH_MAX - 1);
29-
helper->buf[offset] = '\0'; // Ensure null termination
94+
ctx.helper->buf[ctx.offset & (PATH_MAX - 1)] = '\0'; // Ensure null termination
3095

31-
struct path root;
32-
BPF_CORE_READ_INTO(&root, task, fs, root);
33-
struct mount* mnt = container_of(path->mnt, struct mount, mnt);
34-
struct dentry* dentry;
35-
BPF_CORE_READ_INTO(&dentry, path, dentry);
36-
37-
for (int i = 0; i < 16 && (dentry != root.dentry || &mnt->mnt != root.mnt); i++) {
38-
struct dentry* parent = BPF_CORE_READ(dentry, d_parent);
39-
struct dentry* mnt_root = BPF_CORE_READ(mnt, mnt.mnt_root);
40-
41-
if (dentry == mnt_root) {
42-
struct mount* m = BPF_CORE_READ(mnt, mnt_parent);
43-
if (m != mnt) {
44-
dentry = BPF_CORE_READ(mnt, mnt_mountpoint);
45-
mnt = m;
46-
continue;
47-
}
48-
break;
49-
}
50-
51-
if (dentry == parent) {
52-
return -1;
53-
}
96+
BPF_CORE_READ_INTO(&ctx.root, task, fs, root);
97+
ctx.mnt = container_of(path->mnt, struct mount, mnt);
98+
BPF_CORE_READ_INTO(&ctx.dentry, path, dentry);
5499

55-
struct qstr d_name;
56-
BPF_CORE_READ_INTO(&d_name, dentry, d_name);
57-
int len = d_name.len;
58-
if (len <= 0 || len >= buflen) {
59-
return -1;
60-
}
61-
62-
offset -= len;
63-
if (offset <= 0) {
64-
return -1;
65-
}
66-
67-
if (bpf_probe_read_kernel(&helper->buf[offset], len, d_name.name) != 0) {
68-
return -1;
69-
}
70-
71-
offset--;
72-
if (offset <= 0) {
73-
return -1;
74-
}
75-
helper->buf[offset] = '/';
76-
77-
dentry = parent;
100+
long res = bpf_loop(PATH_MAX, __d_path_inner, &ctx, 0);
101+
if (res <= 0 || !ctx.success) {
102+
return -1;
78103
}
79104

80-
bpf_probe_read_str(buf, buflen, &helper->buf[offset]);
81-
return buflen - offset;
105+
bpf_probe_read_str(buf, buflen, &ctx.helper->buf[ctx.offset & (PATH_MAX - 1)]);
106+
return buflen - ctx.offset;
82107
}
83108

84109
__always_inline static long d_path(struct path* path, char* buf, int buflen, bool use_bpf_helper) {

fact/src/bpf/mod.rs

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -282,21 +282,39 @@ mod bpf_tests {
282282
NamedTempFile::new_in(monitored_path).expect("Failed to create temporary file");
283283
println!("Created {file:?}");
284284

285-
let expected = Event::new(
286-
file_activity_type_t::FILE_ACTIVITY_CREATION,
287-
host_info::get_hostname(),
288-
file.path().to_path_buf(),
289-
PathBuf::new(), // host path is resolved by HostScanner
290-
Process::current(),
291-
)
292-
.unwrap();
293-
294-
println!("Expected: {expected:?}");
285+
let current = Process::current();
286+
let file_path = file.path().to_path_buf();
287+
288+
let expected_events = [
289+
Event::new(
290+
file_activity_type_t::FILE_ACTIVITY_CREATION,
291+
host_info::get_hostname(),
292+
file_path.clone(),
293+
PathBuf::new(), // host path is resolved by HostScanner
294+
current.clone(),
295+
)
296+
.unwrap(),
297+
Event::new(
298+
file_activity_type_t::FILE_ACTIVITY_UNLINK,
299+
host_info::get_hostname(),
300+
file_path,
301+
PathBuf::new(), // host path is resolved by HostScanner
302+
current,
303+
)
304+
.unwrap(),
305+
];
306+
307+
// Close the file, removing it
308+
file.close().expect("Failed to close temp file");
309+
310+
println!("Expected: {expected_events:?}");
295311
let wait = timeout(Duration::from_secs(1), async move {
296-
while let Some(event) = rx.recv().await {
297-
println!("{event:?}");
298-
if event == expected {
299-
break;
312+
for expected in expected_events {
313+
while let Some(event) = rx.recv().await {
314+
println!("{event:?}");
315+
if event == expected {
316+
break;
317+
}
300318
}
301319
}
302320
});

0 commit comments

Comments
 (0)