test: add a perf_event breakpoint test

Test perf_event breakpoints by attaching a RW breakpoint to
modprobe_path and triggering a read from procfs, asserting that the tgid
of the program triggering the breakpoint matches the test program.
reviewable/pr1365/r2
Friday Ortiz 4 weeks ago committed by Friday Ortiz
parent f7a9d73d42
commit 8b58fc13fc
No known key found for this signature in database
GPG Key ID: 16CDAE2458091846

@ -103,3 +103,7 @@ path = "src/xdp_sec.rs"
[[bin]] [[bin]]
name = "uprobe_cookie" name = "uprobe_cookie"
path = "src/uprobe_cookie.rs" path = "src/uprobe_cookie.rs"
[[bin]]
name = "perf_event_bp"
path = "src/perf_event_bp.rs"

@ -0,0 +1,24 @@
#![no_std]
#![no_main]
#![expect(unused_crate_dependencies, reason = "used in other bins")]
use aya_ebpf::{
EbpfContext as _,
macros::{map, perf_event},
maps::HashMap,
programs::PerfEventContext,
};
#[cfg(not(test))]
extern crate ebpf_panic;
#[map]
static READERS: HashMap<u32, u64> = HashMap::with_max_entries(1, 0);
#[perf_event]
fn perf_event_bp(ctx: PerfEventContext) -> u32 {
let tgid = ctx.tgid();
let addr = unsafe { (*ctx.ctx).addr };
let _ = READERS.insert(tgid, addr, 0);
0
}

@ -46,6 +46,7 @@ bpf_file!(
MEMMOVE_TEST => "memmove_test", MEMMOVE_TEST => "memmove_test",
NAME_TEST => "name_test", NAME_TEST => "name_test",
PASS => "pass", PASS => "pass",
PERF_EVENT_BP => "perf_event_bp",
RAW_TRACEPOINT => "raw_tracepoint", RAW_TRACEPOINT => "raw_tracepoint",
REDIRECT => "redirect", REDIRECT => "redirect",
RELOCATIONS => "relocations", RELOCATIONS => "relocations",

@ -10,6 +10,7 @@ mod load;
mod log; mod log;
mod lsm; mod lsm;
mod map_pin; mod map_pin;
mod perf_event_bp;
mod raw_tracepoint; mod raw_tracepoint;
mod rbpf; mod rbpf;
mod relocations; mod relocations;

@ -0,0 +1,81 @@
use std::{
fs::{self, File},
io::{BufRead as _, BufReader},
};
use aya::{
Ebpf,
programs::{
PerfEventScope, PerfTypeId, SamplePolicy,
perf_event::{
PerfBreakpoint, PerfBreakpointSize::HwBreakpointLen1,
PerfBreakpointType::HwBreakpointRW,
},
},
util::online_cpus,
};
use log::info;
// Parse /proc/kallsyms and return the address for the given symbol name, if
// found.
fn find_kallsyms_symbol(sym: &str) -> Option<u64> {
let file = File::open("/proc/kallsyms").ok()?;
let reader = BufReader::new(file);
for line in reader.lines().map_while(Result::ok) {
// Format: "<addr> <type> <symbol> [<module>]"
let mut parts = line.split_whitespace();
let addr_str = parts.next()?;
let _type = parts.next()?;
let name = parts.next()?;
if name == sym
&& let Ok(addr) = u64::from_str_radix(addr_str, 16)
{
return Some(addr);
}
}
None
}
#[test_log::test]
fn perf_event_bp() {
let mut bpf = Ebpf::load(crate::PERF_EVENT_BP).unwrap();
let attach_addr = find_kallsyms_symbol("modprobe_path").unwrap();
let prog: &mut aya::programs::PerfEvent = bpf
.program_mut("perf_event_bp")
.unwrap()
.try_into()
.unwrap();
prog.load().unwrap();
// attach hardware breakpoint to modprobe_path global
for cpu in online_cpus().unwrap() {
info!("attaching to cpu {cpu}");
prog.attach(
PerfTypeId::Breakpoint,
0u64,
PerfEventScope::AllProcessesOneCpu { cpu },
SamplePolicy::Period(1),
true,
Some(PerfBreakpoint {
address: attach_addr,
length: HwBreakpointLen1,
type_: HwBreakpointRW,
}),
)
.unwrap();
}
// trigger hardware breakpoint by reading modprobe_path via procfs
let _ = fs::read_to_string("/proc/sys/kernel/modprobe");
// assert that the map contains an entry for this process
let map: aya::maps::HashMap<_, u32, u64> =
aya::maps::HashMap::try_from(bpf.map_mut("READERS").unwrap()).unwrap();
let tgid = std::process::id();
let read_addr = map.get(&tgid, 0).unwrap();
assert_eq!(read_addr, attach_addr);
}
Loading…
Cancel
Save