Skip to content

Commit 299b3cb

Browse files
committed
bpf: Handle raw tracepoint arguments
Provide an `arg()` method in `RawTracepointArgs` wrapper of `bpf_raw_tracepoint_args` and also in `RawTracepointContext`, so it's directly available in raw tracepoint programs. The methods and traits implemented here are unsafe. There is no way to reliably check the number of available arguments, so requesting a non-existing one leads to undefined behavior.
1 parent c130500 commit 299b3cb

File tree

13 files changed

+175
-5
lines changed

13 files changed

+175
-5
lines changed

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ members = [
77
"aya-obj",
88
"aya-tool",
99
"init",
10+
"test/integration-common",
1011
"test/integration-test",
1112
"xtask",
1213

@@ -68,6 +69,7 @@ diff = { version = "0.1.13", default-features = false }
6869
env_logger = { version = "0.10", default-features = false }
6970
hashbrown = { version = "0.14", default-features = false }
7071
indoc = { version = "2.0", default-features = false }
72+
integration-common = { path = "test/integration-common", default-features = false }
7173
integration-ebpf = { path = "test/integration-ebpf", default-features = false }
7274
lazy_static = { version = "1", default-features = false }
7375
libc = { version = "0.2.105", default-features = false }

bpf/aya-bpf/src/args.rs

+69
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use aya_bpf_bindings::bindings::bpf_raw_tracepoint_args;
2+
13
use crate::{cty::c_void, helpers::bpf_probe_read};
24

35
// aarch64 uses user_pt_regs instead of pt_regs
@@ -326,3 +328,70 @@ impl_from_pt_regs!(i32);
326328
impl_from_pt_regs!(i64);
327329
impl_from_pt_regs!(usize);
328330
impl_from_pt_regs!(isize);
331+
332+
/// A Rust wrapper on `bpf_raw_tracepoint_args`.
333+
pub struct RawTracepointArgs {
334+
args: *mut bpf_raw_tracepoint_args,
335+
}
336+
337+
impl RawTracepointArgs {
338+
pub fn new(args: *mut bpf_raw_tracepoint_args) -> Self {
339+
RawTracepointArgs { args }
340+
}
341+
342+
/// Returns the n-th argument of the raw tracepoint.
343+
///
344+
/// ## Safety
345+
///
346+
/// This method is unsafe because it performs raw pointer conversion and makes assumptions
347+
/// about the structure of the `bpf_raw_tracepoint_args` type. The tracepoint arguments are
348+
/// represented as an array of `__u64` values (i.e., `__u64 args[0]`), and this method
349+
/// provides a way to access these arguments conveniently in Rust using `as_slice`.
350+
///
351+
/// However, the method does not check the total number of available arguments for a given
352+
/// tracepoint and assumes that the slice has at least `n` elements, leading to undefined
353+
/// behavior if this condition is not met.
354+
pub unsafe fn arg<T: FromRawTracepointArgs>(&self, n: usize) -> *const T {
355+
&T::from_argument(&*self.args, n)
356+
}
357+
}
358+
359+
pub unsafe trait FromRawTracepointArgs: Sized {
360+
unsafe fn from_argument(ctx: &bpf_raw_tracepoint_args, n: usize) -> Self;
361+
}
362+
363+
unsafe impl<T> FromRawTracepointArgs for *const T {
364+
unsafe fn from_argument(ctx: &bpf_raw_tracepoint_args, n: usize) -> *const T {
365+
// Raw tracepoint arguments are exposed as `__u64 args[0]`.
366+
// https://elixir.bootlin.com/linux/v6.5.5/source/include/uapi/linux/bpf.h#L6829
367+
//
368+
// The most convenient way of accessing such type in Rust is to use
369+
// `as_slice`.
370+
//
371+
// We don't know how many arguments are there for the given tracepoint,
372+
// so we just assume that the slice has at least n elements. The whole
373+
// assumntion and implementation is unsafe.
374+
ctx.args.as_slice(n + 1)[n] as *const _
375+
}
376+
}
377+
378+
macro_rules! unsafe_impl_from_raw_tracepoint_args {
379+
($type:ident) => {
380+
unsafe impl FromRawTracepointArgs for $type {
381+
unsafe fn from_argument(ctx: &bpf_raw_tracepoint_args, n: usize) -> Self {
382+
ctx.args.as_slice(n + 1)[n] as _
383+
}
384+
}
385+
};
386+
}
387+
388+
unsafe_impl_from_raw_tracepoint_args!(u8);
389+
unsafe_impl_from_raw_tracepoint_args!(u16);
390+
unsafe_impl_from_raw_tracepoint_args!(u32);
391+
unsafe_impl_from_raw_tracepoint_args!(u64);
392+
unsafe_impl_from_raw_tracepoint_args!(i8);
393+
unsafe_impl_from_raw_tracepoint_args!(i16);
394+
unsafe_impl_from_raw_tracepoint_args!(i32);
395+
unsafe_impl_from_raw_tracepoint_args!(i64);
396+
unsafe_impl_from_raw_tracepoint_args!(usize);
397+
unsafe_impl_from_raw_tracepoint_args!(isize);

bpf/aya-bpf/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
pub use aya_bpf_bindings::bindings;
1818

1919
mod args;
20-
pub use args::PtRegs;
20+
pub use args::{PtRegs, RawTracepointArgs};
2121
pub mod helpers;
2222
pub mod maps;
2323
pub mod programs;
+10-4
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
11
use core::ffi::c_void;
22

3-
use crate::BpfContext;
3+
use crate::{args::FromRawTracepointArgs, bindings::bpf_raw_tracepoint_args, BpfContext};
44

55
pub struct RawTracePointContext {
6-
ctx: *mut c_void,
6+
ctx: *mut bpf_raw_tracepoint_args,
77
}
88

99
impl RawTracePointContext {
1010
pub fn new(ctx: *mut c_void) -> RawTracePointContext {
11-
RawTracePointContext { ctx }
11+
RawTracePointContext {
12+
ctx: ctx as *mut bpf_raw_tracepoint_args,
13+
}
14+
}
15+
16+
pub unsafe fn arg<T: FromRawTracepointArgs>(&self, n: usize) -> T {
17+
T::from_argument(&*self.ctx, n)
1218
}
1319
}
1420

1521
impl BpfContext for RawTracePointContext {
1622
fn as_ptr(&self) -> *mut c_void {
17-
self.ctx
23+
self.ctx as *mut c_void
1824
}
1925
}

test/integration-common/Cargo.toml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "integration-common"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[features]
7+
default = []
8+
user = ["aya"]
9+
10+
[dependencies]
11+
aya = { workspace = true, optional = true }

test/integration-common/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#![no_std]
2+
3+
pub mod raw_tracepoint;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#[repr(C)]
2+
#[derive(Clone, Copy)]
3+
pub struct SysEnterEvent {
4+
pub common_type: u16,
5+
pub common_flags: u8,
6+
_padding: u8,
7+
}
8+
9+
impl SysEnterEvent {
10+
pub fn new(common_type: u16, common_flags: u8) -> Self {
11+
Self {
12+
common_type,
13+
common_flags,
14+
_padding: 0,
15+
}
16+
}
17+
}
18+
19+
#[cfg(feature = "user")]
20+
unsafe impl aya::Pod for SysEnterEvent {}

test/integration-ebpf/Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ publish = false
77
[dependencies]
88
aya-bpf = { path = "../../bpf/aya-bpf" }
99
aya-log-ebpf = { path = "../../bpf/aya-log-ebpf" }
10+
integration-common = { workspace = true }
1011

1112
[build-dependencies]
1213
which = { workspace = true }
@@ -43,3 +44,7 @@ path = "src/bpf_probe_read.rs"
4344
[[bin]]
4445
name = "two_progs"
4546
path = "src/two_progs.rs"
47+
48+
[[bin]]
49+
name = "raw_tracepoint"
50+
path = "src/raw_tracepoint.rs"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#![no_std]
2+
#![no_main]
3+
4+
use aya_bpf::{
5+
macros::{map, raw_tracepoint},
6+
maps::Array,
7+
programs::RawTracePointContext,
8+
};
9+
10+
use integration_common::raw_tracepoint::SysEnterEvent;
11+
12+
#[map]
13+
static RESULT: Array<SysEnterEvent> = Array::with_max_entries(1, 0);
14+
15+
#[raw_tracepoint(tracepoint = "sys_enter")]
16+
pub fn sys_enter(ctx: RawTracePointContext) -> i32 {
17+
let common_type: u16 = unsafe { ctx.arg(0) };
18+
let common_flags: u8 = unsafe { ctx.arg(1) };
19+
20+
if let Some(ptr) = RESULT.get_ptr_mut(0) {
21+
unsafe {
22+
(*ptr).common_type = common_type;
23+
(*ptr).common_flags = common_flags;
24+
}
25+
}
26+
27+
0
28+
}
29+
30+
#[cfg(not(test))]
31+
#[panic_handler]
32+
fn panic(_info: &core::panic::PanicInfo) -> ! {
33+
loop {}
34+
}

test/integration-test/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ assert_matches = { workspace = true }
1010
aya = { workspace = true }
1111
aya-log = { workspace = true }
1212
aya-obj = { workspace = true }
13+
integration-common = { workspace = true, features = ["user"] }
1314
libc = { workspace = true }
1415
log = { workspace = true }
1516
netns-rs = { workspace = true }

test/integration-test/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ pub const RELOCATIONS: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "
1919
pub const TWO_PROGS: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/two_progs"));
2020
pub const BPF_PROBE_READ: &[u8] =
2121
include_bytes_aligned!(concat!(env!("OUT_DIR"), "/bpf_probe_read"));
22+
pub const RAW_TRACEPOINT: &[u8] =
23+
include_bytes_aligned!(concat!(env!("OUT_DIR"), "/raw_tracepoint"));
2224

2325
#[cfg(test)]
2426
mod tests;

test/integration-test/src/tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ mod btf_relocations;
33
mod elf;
44
mod load;
55
mod log;
6+
mod raw_tracepoint;
67
mod rbpf;
78
mod relocations;
89
mod smoke;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use aya::{maps::Array, programs::RawTracePoint, Bpf};
2+
use integration_common::raw_tracepoint::SysEnterEvent;
3+
4+
#[test]
5+
fn raw_tracepoint() {
6+
let mut bpf = Bpf::load(crate::RAW_TRACEPOINT).unwrap();
7+
let prog: &mut RawTracePoint = bpf.program_mut("sys_enter").unwrap().try_into().unwrap();
8+
prog.load().unwrap();
9+
prog.attach("sys_enter").unwrap();
10+
11+
let map: Array<_, SysEnterEvent> = Array::try_from(bpf.map_mut("RESULT").unwrap()).unwrap();
12+
let result = map.get(&0, 0).unwrap();
13+
14+
assert_ne!(result.common_type, 0);
15+
assert_ne!(result.common_flags, 0);
16+
}

0 commit comments

Comments
 (0)