Skip to content

Commit 54874a6

Browse files
authored
refactor: clean up BPF hooks (#91)
* refactor: clean up BPF hooks This patch makes some refactorings to the LSM hooks to reduce duplicated code and overall simplify the complexity of them. * Add a submit_event function that handles reserving a ringbuffer event and submitting it. * Add path_write for reading struct path as string into path_cfg_helper_t type. * Add path_append_dentry for easily appending dentry names to a buffer. * Move length logic for LPM TRIE maps to the is_monitored function. * Add simple getter functions for infallible map lookups. * Add path_write_char for simple and safe access to write a single char in a path buffer. * Rename path_cfg_helper_t to bound_path_t Also moved some logic for interacting with bound_path_t to a separate bound_path.h header.
1 parent 3dc5ec0 commit 54874a6

File tree

5 files changed

+161
-144
lines changed

5 files changed

+161
-144
lines changed

fact-ebpf/src/bpf/bound_path.h

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#pragma once
2+
3+
// clang-format off
4+
#include "types.h"
5+
#include "maps.h"
6+
7+
#include "vmlinux.h"
8+
9+
#include <bpf/bpf_helpers.h>
10+
#include <bpf/bpf_core_read.h>
11+
// clang-format on
12+
13+
#define PATH_MAX_MASK (PATH_MAX - 1)
14+
#define path_len_clamp(len) ((len) & PATH_MAX_MASK)
15+
16+
__always_inline static char* path_safe_access(char* p, unsigned int offset) {
17+
return &p[path_len_clamp(offset)];
18+
}
19+
20+
__always_inline static void path_write_char(char* p, unsigned int offset, char c) {
21+
*path_safe_access(p, offset) = c;
22+
}
23+
24+
__always_inline static struct bound_path_t* path_read(struct path* path) {
25+
struct bound_path_t* bound_path = get_bound_path();
26+
27+
bound_path->len = bpf_d_path(path, bound_path->path, PATH_MAX);
28+
if (bound_path->len <= 0) {
29+
return NULL;
30+
}
31+
32+
// Ensure length is within PATH_MAX for the verifier
33+
bound_path->len = path_len_clamp(bound_path->len);
34+
35+
return bound_path;
36+
}
37+
38+
enum path_append_status_t {
39+
PATH_APPEND_SUCCESS = 0,
40+
PATH_APPEND_INVALID_LENGTH,
41+
PATH_APPEND_READ_ERROR,
42+
};
43+
44+
__always_inline static enum path_append_status_t path_append_dentry(struct bound_path_t* path, struct dentry* dentry) {
45+
struct qstr d_name;
46+
BPF_CORE_READ_INTO(&d_name, dentry, d_name);
47+
int len = d_name.len;
48+
if (len + path->len > PATH_MAX) {
49+
path->len += len;
50+
return PATH_APPEND_INVALID_LENGTH;
51+
}
52+
53+
char* path_offset = path_safe_access(path->path, path->len);
54+
if (bpf_probe_read_kernel(path_offset, path_len_clamp(len), d_name.name)) {
55+
return PATH_APPEND_READ_ERROR;
56+
}
57+
58+
path->len += len;
59+
path_write_char(path->path, path->len, '\0');
60+
61+
return 0;
62+
}

fact-ebpf/src/bpf/events.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#include <bpf/bpf_helpers.h>
2+
3+
#include "maps.h"
4+
#include "process.h"
5+
#include "types.h"
6+
#include "vmlinux.h"
7+
8+
__always_inline static void submit_event(struct metrics_by_hook_t* m, file_activity_type_t event_type, const char filename[PATH_MAX], struct dentry* dentry) {
9+
struct event_t* event = bpf_ringbuf_reserve(&rb, sizeof(struct event_t), 0);
10+
if (event == NULL) {
11+
m->ringbuffer_full++;
12+
return;
13+
}
14+
15+
event->type = event_type;
16+
event->timestamp = bpf_ktime_get_boot_ns();
17+
bpf_probe_read_str(event->filename, PATH_MAX, filename);
18+
19+
struct helper_t* helper = get_helper();
20+
const char* p = get_host_path(helper->buf, dentry);
21+
if (p != NULL) {
22+
bpf_probe_read_str(event->host_file, PATH_MAX, p);
23+
}
24+
25+
int64_t err = process_fill(&event->process);
26+
if (err) {
27+
bpf_printk("Failed to fill process information: %d", err);
28+
goto error;
29+
}
30+
31+
m->added++;
32+
bpf_ringbuf_submit(event, 0);
33+
return;
34+
35+
error:
36+
m->error++;
37+
bpf_ringbuf_discard(event, 0);
38+
}

fact-ebpf/src/bpf/file.h

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// clang-format off
44
#include "vmlinux.h"
55

6+
#include "bound_path.h"
67
#include "builtins.h"
78
#include "types.h"
89
#include "maps.h"
@@ -11,13 +12,6 @@
1112
#include <bpf/bpf_core_read.h>
1213
// clang-format on
1314

14-
#define PATH_MAX_MASK (PATH_MAX - 1)
15-
#define path_len_clamp(len) ((len) & PATH_MAX_MASK)
16-
17-
__always_inline static char* path_safe_access(char* p, unsigned int offset) {
18-
return &p[path_len_clamp(offset)];
19-
}
20-
2115
/**
2216
* Reimplementation of the kernel d_path function.
2317
*
@@ -129,11 +123,22 @@ __always_inline static char* get_host_path(char buf[PATH_MAX * 2], struct dentry
129123
return &buf[offset];
130124
}
131125

132-
__always_inline static bool is_monitored(struct path_cfg_helper_t* path) {
126+
__always_inline static bool is_monitored(struct bound_path_t* path) {
133127
if (!filter_by_prefix) {
134128
// no path configured, allow all
135129
return true;
136130
}
137131

138-
return bpf_map_lookup_elem(&path_prefix, path) != NULL;
132+
// Backup bytes length and restore it before exiting
133+
unsigned int len = path->len;
134+
135+
if (path->len > LPM_SIZE_MAX) {
136+
path->len = LPM_SIZE_MAX;
137+
}
138+
// for LPM maps, the length is the total number of bits
139+
path->len = path->len * 8;
140+
141+
bool res = bpf_map_lookup_elem(&path_prefix, path) != NULL;
142+
path->len = len;
143+
return res;
139144
}

fact-ebpf/src/bpf/main.c

Lines changed: 25 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#include "types.h"
44
#include "process.h"
55
#include "maps.h"
6+
#include "events.h"
7+
#include "bound_path.h"
68

79
#include "vmlinux.h"
810

@@ -19,13 +21,7 @@ char _license[] SEC("license") = "Dual MIT/GPL";
1921

2022
SEC("lsm/file_open")
2123
int BPF_PROG(trace_file_open, struct file* file) {
22-
uint32_t key = 0;
23-
struct event_t* event = NULL;
24-
struct metrics_t* m = bpf_map_lookup_elem(&metrics, &key);
25-
if (m == NULL) {
26-
bpf_printk("Failed to get metrics entry, this should not happen");
27-
return 0;
28-
}
24+
struct metrics_t* m = get_metrics();
2925

3026
m->file_open.total++;
3127

@@ -38,67 +34,20 @@ int BPF_PROG(trace_file_open, struct file* file) {
3834
goto ignored;
3935
}
4036

41-
struct path_cfg_helper_t* prefix_helper = bpf_map_lookup_elem(&path_prefix_helper, &key);
42-
if (prefix_helper == NULL) {
43-
bpf_printk("Failed to get prefix helper");
44-
goto error;
45-
}
46-
47-
long len = bpf_d_path(&file->f_path, prefix_helper->path, PATH_MAX);
48-
if (len <= 0) {
37+
struct bound_path_t* path = path_read(&file->f_path);
38+
if (path == NULL) {
4939
bpf_printk("Failed to read path");
50-
goto error;
51-
}
52-
53-
if (len > LPM_SIZE_MAX) {
54-
len = LPM_SIZE_MAX;
55-
}
56-
// for LPM maps, the length is the total number of bits
57-
prefix_helper->bit_len = len * 8;
58-
59-
if (!is_monitored(prefix_helper)) {
60-
goto ignored;
61-
}
62-
63-
event = bpf_ringbuf_reserve(&rb, sizeof(struct event_t), 0);
64-
if (event == NULL) {
65-
m->file_open.ringbuffer_full++;
66-
bpf_printk("Failed to get event entry");
40+
m->file_open.error++;
6741
return 0;
6842
}
6943

70-
event->type = event_type;
71-
event->timestamp = bpf_ktime_get_boot_ns();
72-
bpf_probe_read_str(event->filename, PATH_MAX, prefix_helper->path);
73-
74-
int64_t err = process_fill(&event->process);
75-
if (err) {
76-
bpf_printk("Failed to fill process information: %d", err);
77-
goto error;
78-
}
79-
80-
struct helper_t* helper = bpf_map_lookup_elem(&helper_map, &key);
81-
if (helper == NULL) {
82-
bpf_printk("Failed to get helper entry");
83-
return 0;
44+
if (!is_monitored(path)) {
45+
goto ignored;
8446
}
8547

8648
struct dentry* d = BPF_CORE_READ(file, f_path.dentry);
87-
const char* p = get_host_path(helper->buf, d);
88-
if (p != NULL) {
89-
bpf_probe_read_str(event->host_file, PATH_MAX, p);
90-
}
91-
92-
m->file_open.added++;
93-
bpf_ringbuf_submit(event, 0);
94-
95-
return 0;
49+
submit_event(&m->file_open, event_type, path->path, d);
9650

97-
error:
98-
m->file_open.error++;
99-
if (event != NULL) {
100-
bpf_ringbuf_discard(event, 0);
101-
}
10251
return 0;
10352

10453
ignored:
@@ -108,89 +57,37 @@ int BPF_PROG(trace_file_open, struct file* file) {
10857

10958
SEC("lsm/path_unlink")
11059
int BPF_PROG(trace_path_unlink, struct path* dir, struct dentry* dentry) {
111-
uint32_t key = 0;
112-
struct event_t* event = NULL;
113-
struct metrics_t* m = bpf_map_lookup_elem(&metrics, &key);
114-
if (m == NULL) {
115-
bpf_printk("Failed to get metrics entry, this should not happen");
116-
return 0;
117-
}
60+
struct metrics_t* m = get_metrics();
11861

11962
m->path_unlink.total++;
12063

121-
struct path_cfg_helper_t* prefix_helper = bpf_map_lookup_elem(&path_prefix_helper, &key);
122-
if (prefix_helper == NULL) {
123-
bpf_printk("Failed to get prefix helper");
124-
goto error;
125-
}
126-
127-
long path_len = bpf_d_path(dir, prefix_helper->path, PATH_MAX);
128-
if (path_len <= 0) {
64+
struct bound_path_t* path = path_read(dir);
65+
if (path == NULL) {
12966
bpf_printk("Failed to read path");
13067
goto error;
13168
}
132-
*path_safe_access(prefix_helper->path, path_len - 1) = '/';
133-
134-
struct qstr d_name;
135-
BPF_CORE_READ_INTO(&d_name, dentry, d_name);
136-
int len = d_name.len;
137-
if (len + path_len > PATH_MAX) {
138-
bpf_printk("Invalid path length: %u", len + path_len);
139-
goto error;
140-
}
141-
142-
char* path_offset = path_safe_access(prefix_helper->path, path_len);
143-
if (bpf_probe_read_kernel(path_offset, path_len_clamp(len), d_name.name)) {
144-
bpf_printk("Failed to read final path component");
145-
goto error;
146-
}
147-
*path_safe_access(prefix_helper->path, path_len + len) = '\0';
148-
149-
len += path_len;
150-
if (len > LPM_SIZE_MAX) {
151-
len = LPM_SIZE_MAX;
152-
}
153-
// for LPM maps, the length is the total number of bits
154-
prefix_helper->bit_len = len * 8;
69+
path_write_char(path->path, path->len - 1, '/');
15570

156-
if (!is_monitored(prefix_helper)) {
157-
goto ignored;
71+
switch (path_append_dentry(path, dentry)) {
72+
case PATH_APPEND_SUCCESS:
73+
break;
74+
case PATH_APPEND_INVALID_LENGTH:
75+
bpf_printk("Invalid path length: %u", path->len);
76+
goto error;
77+
case PATH_APPEND_READ_ERROR:
78+
bpf_printk("Failed to read final path component");
79+
goto error;
15880
}
15981

160-
event = bpf_ringbuf_reserve(&rb, sizeof(struct event_t), 0);
161-
if (event == NULL) {
162-
m->path_unlink.ringbuffer_full++;
163-
bpf_printk("Failed to get event entry");
82+
if (!is_monitored(path)) {
83+
m->path_unlink.ignored++;
16484
return 0;
16585
}
16686

167-
bpf_probe_read_str(event->filename, PATH_MAX, prefix_helper->path);
168-
event->type = FILE_ACTIVITY_UNLINK;
169-
event->timestamp = bpf_ktime_get_boot_ns();
170-
int64_t err = process_fill(&event->process);
171-
if (err) {
172-
bpf_printk("Failed to fill process information: %d", err);
173-
goto error;
174-
}
175-
176-
const char* p = get_host_path(prefix_helper->path, dentry);
177-
if (p != NULL) {
178-
bpf_probe_read_str(event->host_file, PATH_MAX, p);
179-
}
180-
181-
m->path_unlink.added++;
182-
bpf_ringbuf_submit(event, 0);
183-
87+
submit_event(&m->path_unlink, FILE_ACTIVITY_UNLINK, path->path, dentry);
18488
return 0;
18589

18690
error:
18791
m->path_unlink.error++;
188-
if (event != NULL) {
189-
bpf_ringbuf_discard(event, 0);
190-
}
191-
return 0;
192-
193-
ignored:
194-
m->path_unlink.ignored++;
19592
return 0;
19693
}

0 commit comments

Comments
 (0)