Skip to content

Commit 98fc082

Browse files
committed
fix(fact-ebpf): improve compatibility with arm64 kernels
1 parent cef027c commit 98fc082

File tree

8 files changed

+148
-46
lines changed

8 files changed

+148
-46
lines changed

fact-ebpf/build.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@ use std::{
66
};
77

88
fn compile_bpf(out_dir: &Path) -> anyhow::Result<()> {
9-
let target_arch = format!("-D__TARGET_ARCH_{}", env::var("CARGO_CFG_TARGET_ARCH")?);
9+
let target_arch = match env::var("CARGO_CFG_TARGET_ARCH")?.as_str() {
10+
"x86_64" => "x86",
11+
"aarch64" => "arm64",
12+
arch => unimplemented!("Unsupported arch {arch}"),
13+
};
14+
let target_arch = format!("-D__TARGET_ARCH_{target_arch}");
1015
let base_args = [
1116
"-target",
1217
"bpf",

fact-ebpf/src/bpf/bound_path.h

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ __always_inline static void path_write_char(char* p, unsigned int offset, char c
2222
*path_safe_access(p, offset) = c;
2323
}
2424

25-
__always_inline static struct bound_path_t* _path_read(struct path* path, bool use_bpf_d_path) {
25+
__always_inline static struct bound_path_t* path_read(struct path* path, bool use_bpf_d_path) {
2626
struct bound_path_t* bound_path = get_bound_path();
2727
if (bound_path == NULL) {
2828
return NULL;
@@ -39,14 +39,6 @@ __always_inline static struct bound_path_t* _path_read(struct path* path, bool u
3939
return bound_path;
4040
}
4141

42-
__always_inline static struct bound_path_t* path_read(struct path* path) {
43-
return _path_read(path, true);
44-
}
45-
46-
__always_inline static struct bound_path_t* path_read_alternate(struct path* path) {
47-
return _path_read(path, false);
48-
}
49-
5042
enum path_append_status_t {
5143
PATH_APPEND_SUCCESS = 0,
5244
PATH_APPEND_INVALID_LENGTH,

fact-ebpf/src/bpf/checks.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,14 @@
77
#include <bpf/bpf_tracing.h>
88
// clang-format on
99

10+
SEC("lsm/file_open")
11+
int BPF_PROG(check_lsm_support, struct file* file) {
12+
return 0;
13+
}
14+
1015
SEC("lsm/path_unlink")
1116
int BPF_PROG(check_path_unlink_supports_bpf_d_path, struct path* dir, struct dentry* dentry) {
12-
struct bound_path_t* p = path_read(dir);
17+
struct bound_path_t* p = path_read(dir, true);
1318
bpf_printk("dir: %s", p->path);
1419
return 0;
1520
}

fact-ebpf/src/bpf/main.c

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ char _license[] SEC("license") = "Dual MIT/GPL";
1919
#define FMODE_PWRITE ((fmode_t)(1 << 4))
2020
#define FMODE_CREATED ((fmode_t)(1 << 20))
2121

22-
SEC("lsm/file_open")
23-
int BPF_PROG(trace_file_open, struct file* file) {
22+
__always_inline static int do_file_open(struct file* file, bool use_bpf_d_path) {
2423
struct metrics_t* m = get_metrics();
2524
if (m == NULL) {
2625
return 0;
@@ -37,7 +36,7 @@ int BPF_PROG(trace_file_open, struct file* file) {
3736
goto ignored;
3837
}
3938

40-
struct bound_path_t* path = path_read(&file->f_path);
39+
struct bound_path_t* path = path_read(&file->f_path, use_bpf_d_path);
4140
if (path == NULL) {
4241
bpf_printk("Failed to read path");
4342
m->file_open.error++;
@@ -58,21 +57,28 @@ int BPF_PROG(trace_file_open, struct file* file) {
5857
return 0;
5958
}
6059

61-
SEC("lsm/path_unlink")
62-
int BPF_PROG(trace_path_unlink, struct path* dir, struct dentry* dentry) {
60+
SEC("kprobe/security_file_open")
61+
int BPF_KPROBE(kprobe_file_open, struct file* file) {
62+
struct file f;
63+
BPF_CORE_READ_INTO(&f.f_mode, file, f_mode);
64+
BPF_CORE_READ_INTO(&f.f_path, file, f_path);
65+
return do_file_open(&f, false);
66+
}
67+
68+
SEC("lsm/file_open")
69+
int BPF_PROG(trace_file_open, struct file* file) {
70+
return do_file_open(file, true);
71+
}
72+
73+
__always_inline int do_path_unlink(struct path* dir, struct dentry* dentry, bool use_bpf_d_path) {
6374
struct metrics_t* m = get_metrics();
6475
if (m == NULL) {
6576
return 0;
6677
}
6778

6879
m->path_unlink.total++;
6980

70-
struct bound_path_t* path = NULL;
71-
if (path_unlink_supports_bpf_d_path) {
72-
path = path_read(dir);
73-
} else {
74-
path = path_read_alternate(dir);
75-
}
81+
struct bound_path_t* path = path_read(dir, use_bpf_d_path);
7682

7783
if (path == NULL) {
7884
bpf_printk("Failed to read path");
@@ -103,3 +109,16 @@ int BPF_PROG(trace_path_unlink, struct path* dir, struct dentry* dentry) {
103109
m->path_unlink.error++;
104110
return 0;
105111
}
112+
113+
SEC("kprobe/security_path_unlink")
114+
int BPF_KPROBE(kprobe_path_unlink, struct path* dir, struct dentry* dentry) {
115+
struct path p;
116+
p.mnt = BPF_CORE_READ(dir, mnt);
117+
p.dentry = BPF_CORE_READ(dir, dentry);
118+
return do_path_unlink(&p, dentry, false);
119+
}
120+
121+
SEC("lsm/path_unlink")
122+
int BPF_PROG(trace_path_unlink, struct path* dir, struct dentry* dentry) {
123+
return do_path_unlink(dir, dentry, path_unlink_supports_bpf_d_path);
124+
}

fact-ebpf/src/bpf/vmlinux.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#pragma once
22

3-
#if defined(__TARGET_ARCH_x86_64)
3+
#if defined(__TARGET_ARCH_x86)
44
# include "vmlinux/x86_64.h"
5-
#elif defined(__TARGET_ARCH_aarch64)
5+
#elif defined(__TARGET_ARCH_arm64)
66
# include "vmlinux/aarch64.h"
77
#else
88
# error "Unknown target"

fact/src/bpf/checks.rs

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,66 @@
1-
use anyhow::Context;
2-
use aya::{programs::Lsm, Btf};
1+
use anyhow::{Context, Ok};
2+
use aya::{programs::Lsm, Btf, Ebpf};
33
use log::debug;
44

5+
#[derive(Default)]
56
pub(super) struct Checks {
7+
pub(super) lsm_support: bool,
68
pub(super) path_unlink_supports_bpf_d_path: bool,
79
}
810

911
impl Checks {
1012
pub(super) fn new(btf: &Btf) -> anyhow::Result<Self> {
11-
let mut obj = aya::EbpfLoader::new()
13+
let checks = ChecksBuilder::new(btf)?
14+
.check_lsm_support()
15+
.check_path_unlink_supports_bpf_d_path()
16+
.build();
17+
18+
Ok(checks)
19+
}
20+
}
21+
22+
struct ChecksBuilder<'a> {
23+
obj: Ebpf,
24+
btf: &'a Btf,
25+
checks: Checks,
26+
}
27+
28+
impl<'a> ChecksBuilder<'a> {
29+
fn new(btf: &'a Btf) -> anyhow::Result<Self> {
30+
let obj = aya::EbpfLoader::new()
1231
.load(fact_ebpf::CHECKS_OBJ)
1332
.context("Failed to load checks.o")?;
1433

15-
let prog = obj
34+
let checks = Checks::default();
35+
36+
Ok(ChecksBuilder { obj, btf, checks })
37+
}
38+
39+
fn build(self) -> Checks {
40+
self.checks
41+
}
42+
43+
fn check_lsm_support(mut self) -> Self {
44+
let prog = self
45+
.obj
46+
.program_mut("check_lsm_support")
47+
.expect("Failed to find 'check_lsm_support' program");
48+
let prog: &mut Lsm = prog.try_into().expect("'check_lsm_support' is not Lsm");
49+
50+
self.checks.lsm_support = prog.load("file_open", self.btf).is_ok() && prog.attach().is_ok();
51+
self
52+
}
53+
54+
fn check_path_unlink_supports_bpf_d_path(mut self) -> Self {
55+
let prog = self
56+
.obj
1657
.program_mut("check_path_unlink_supports_bpf_d_path")
17-
.context("Failed to find 'check_path_unlink_supports_bpf_d_path' program")?;
18-
let prog: &mut Lsm = prog.try_into()?;
19-
let path_unlink_supports_bpf_d_path = prog.load("path_unlink", btf).is_ok();
58+
.expect("Failed to find 'check_path_unlink_supports_bpf_d_path' program");
59+
let prog: &mut Lsm = prog
60+
.try_into()
61+
.expect("'check_path_unlink_supports_bpf_d_path' is not Lsm");
62+
let path_unlink_supports_bpf_d_path = prog.load("path_unlink", self.btf).is_ok();
2063
debug!("path_unlink_supports_bpf_d_path: {path_unlink_supports_bpf_d_path}");
21-
22-
Ok(Checks {
23-
path_unlink_supports_bpf_d_path,
24-
})
64+
self
2565
}
2666
}

fact/src/bpf/mod.rs

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::{io, path::PathBuf, sync::Arc};
33
use anyhow::{bail, Context};
44
use aya::{
55
maps::{Array, LpmTrie, MapData, PerCpuArray, RingBuf},
6-
programs::Lsm,
6+
programs::Program,
77
Btf, Ebpf,
88
};
99
use checks::Checks;
@@ -30,6 +30,8 @@ pub struct Bpf {
3030

3131
paths: Vec<path_prefix_t>,
3232
paths_config: watch::Receiver<Vec<PathBuf>>,
33+
34+
use_lsm_hook: bool,
3335
}
3436

3537
impl Bpf {
@@ -61,6 +63,7 @@ impl Bpf {
6163
tx,
6264
paths,
6365
paths_config,
66+
use_lsm_hook: checks.lsm_support,
6467
};
6568

6669
bpf.load_paths()?;
@@ -138,24 +141,59 @@ impl Bpf {
138141
Ok(())
139142
}
140143

141-
fn load_lsm_prog(&mut self, name: &str, hook: &str, btf: &Btf) -> anyhow::Result<()> {
144+
fn load_prog(&mut self, name: &str, btf: &Btf) -> anyhow::Result<()> {
142145
let Some(prog) = self.obj.program_mut(name) else {
143146
bail!("{name} program not found");
144147
};
145-
let prog: &mut Lsm = prog.try_into()?;
146-
prog.load(hook, btf)?;
148+
149+
match prog {
150+
Program::Lsm(lsm) => match name {
151+
"trace_file_open" => lsm.load("file_open", btf)?,
152+
"trace_path_unlink" => lsm.load("path_unlink", btf)?,
153+
name => bail!("Unexpected LSM hook '{name}'"),
154+
},
155+
Program::KProbe(kprobe) => kprobe.load()?,
156+
prog => bail!("Unexpected program {prog:?}"),
157+
}
158+
147159
Ok(())
148160
}
149161

150162
fn load_progs(&mut self, btf: &Btf) -> anyhow::Result<()> {
151-
self.load_lsm_prog("trace_file_open", "file_open", btf)?;
152-
self.load_lsm_prog("trace_path_unlink", "path_unlink", btf)
163+
if self.use_lsm_hook {
164+
debug!("Loading LSM hooks");
165+
self.load_prog("trace_file_open", btf)?;
166+
self.load_prog("trace_path_unlink", btf)?;
167+
} else {
168+
debug!("Loading KProbes");
169+
self.load_prog("kprobe_file_open", btf)?;
170+
self.load_prog("kprobe_path_unlink", btf)?;
171+
}
172+
Ok(())
153173
}
154174

155175
fn attach_progs(&mut self) -> anyhow::Result<()> {
156-
for (_, prog) in self.obj.programs_mut() {
157-
let prog: &mut Lsm = prog.try_into()?;
158-
prog.attach()?;
176+
for (name, prog) in self.obj.programs_mut() {
177+
match prog {
178+
Program::Lsm(prog) => {
179+
if self.use_lsm_hook {
180+
debug!("Attaching '{name}'");
181+
prog.attach()?;
182+
}
183+
}
184+
Program::KProbe(prog) => {
185+
if !self.use_lsm_hook {
186+
debug!("Attaching '{name}'");
187+
let fn_name = match name {
188+
"kprobe_file_open" => "security_file_open",
189+
"kprobe_path_unlink" => "security_path_unlink",
190+
name => unimplemented!("Invalid '{name}' kprobe"),
191+
};
192+
prog.attach(fn_name, 0)?;
193+
}
194+
}
195+
ty => unimplemented!("Unsupported type {ty:?}"),
196+
}
159197
}
160198
Ok(())
161199
}

fact/src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,11 @@ pub async fn run(config: FactConfig) -> anyhow::Result<()> {
9898
_ = sigterm.recv() => break,
9999
_ = sighup.recv() => config_trigger.notify_one(),
100100
res = bpf_handle.borrow_mut() => {
101-
if let Err(e) = res {
102-
warn!("BPF worker errored out: {e}");
101+
match res {
102+
Ok(res) => if let Err(e) = res {
103+
warn!("BPF worker errored out: {e:?}");
104+
}
105+
Err(e) => warn!("BPF task errored out: {e:?}"),
103106
}
104107
break;
105108
}

0 commit comments

Comments
 (0)