Skip to content

Commit

Permalink
bpf: Add bpf_strncmp helper
Browse files Browse the repository at this point in the history
The `bpf_strncmp` helper allows for better string comparison in eBPF
programs.

Kernel patchset:

https://lore.kernel.org/bpf/20211210141652.877186-1-houtao1@huawei.com/
  • Loading branch information
vadorovsky committed Jan 22, 2024
1 parent fd2fb98 commit 5b86ae3
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 1 deletion.
35 changes: 34 additions & 1 deletion bpf/aya-bpf/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
//! also expose bindings to the underlying helpers as a fall-back in case of a missing
//! implementation.

use core::mem::{self, MaybeUninit};
use core::{
cmp::Ordering,
ffi::CStr,
mem::{self, MaybeUninit},
};

pub use aya_bpf_bindings::helpers as gen;
#[doc(hidden)]
Expand Down Expand Up @@ -836,3 +840,32 @@ pub unsafe fn bpf_printk_impl<const FMT_LEN: usize, const NUM_ARGS: usize>(
_ => gen::bpf_trace_vprintk(fmt_ptr, fmt_size, args.as_ptr() as _, (NUM_ARGS * 8) as _),
}
}

/// Compares the given byte `s1` with a [`&CStr`](core::ffi::CStr) `s2`.
///
/// # Examples
///
/// ```no_run
/// # use aya_bpf::{cty::c_long, helpers::bpf_strncmp};
/// # fn try_test() -> Result<(), c_long> {
/// # let data = b"something";
/// if bpf_strncmp(data, c"foo")? == core::cmp::Ordering::Equal {
/// // The comparison succeeded.
/// }
/// # Ok::<(), c_long>(())
/// # }
/// ```
#[inline]
pub fn bpf_strncmp(s1: &[u8], s2: &CStr) -> Ordering {
let s2 = s2.to_bytes_with_nul();

let res = unsafe {
gen::bpf_strncmp(
s1.as_ptr() as *const _,
// Don't include the NULL character in the comparison.
(s2.len() - 1) as u32,
s2.as_ptr() as *const _,
)
};
res.cmp(&0)
}
4 changes: 4 additions & 0 deletions test/integration-ebpf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,7 @@ path = "src/xdp_sec.rs"
[[bin]]
name = "ring_buf"
path = "src/ring_buf.rs"

[[bin]]
name = "strncmp"
path = "src/strncmp.rs"
40 changes: 40 additions & 0 deletions test/integration-ebpf/src/strncmp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#![no_std]
#![no_main]

use core::cmp::Ordering;

use aya_bpf::{
cty::c_long,
helpers::{bpf_probe_read_user_str_bytes, bpf_strncmp},
macros::{map, uprobe},
maps::Array,
programs::ProbeContext,
};

#[repr(C)]
struct TestResult(Ordering);

#[map]
static RESULT: Array<TestResult> = Array::with_max_entries(1, 0);

#[uprobe]
pub fn test_bpf_strncmp(ctx: ProbeContext) -> Result<(), c_long> {
let str_bytes: *const u8 = ctx.arg(0).ok_or(-1)?;
let mut buf = [0u8; 16];
let str_bytes = unsafe { bpf_probe_read_user_str_bytes(str_bytes, &mut buf)? };

let ptr = RESULT.get_ptr_mut(0).ok_or(-1)?;
let dst = unsafe { ptr.as_mut() };
let TestResult(dst_res) = dst.ok_or(-1)?;

let cmp_res = bpf_strncmp(str_bytes, c"fff");
*dst_res = cmp_res;

Ok(())
}

#[cfg(not(test))]
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}
1 change: 1 addition & 0 deletions test/integration-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub const BPF_PROBE_READ: &[u8] =
pub const REDIRECT: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/redirect"));
pub const XDP_SEC: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/xdp_sec"));
pub const RING_BUF: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/ring_buf"));
pub const STRNCMP: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/strncmp"));

#[cfg(test)]
mod tests;
Expand Down
1 change: 1 addition & 0 deletions test/integration-test/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ mod rbpf;
mod relocations;
mod ring_buf;
mod smoke;
mod strncmp;
mod xdp;
69 changes: 69 additions & 0 deletions test/integration-test/src/tests/strncmp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use std::cmp::Ordering;

use aya::{maps::Array, programs::UProbe, Bpf};

#[derive(Copy, Clone)]
#[repr(C)]
struct TestResult(Ordering);

unsafe impl aya::Pod for TestResult {}

#[test]
fn bpf_strncmp_equal() {
let bpf = load_and_attach_uprobe();
trigger_bpf_strncmp(b"fff".as_ptr());
let res = fetch_result(&bpf);
assert_eq!(res, Ordering::Equal);
}

#[test]
fn bpf_strncmp_equal_longer() {
let bpf = load_and_attach_uprobe();
trigger_bpf_strncmp(b"ffffff".as_ptr());
let res = fetch_result(&bpf);
assert_eq!(res, Ordering::Equal);
}

#[test]
fn bpf_strncmp_less() {
let bpf = load_and_attach_uprobe();
trigger_bpf_strncmp(b"aaa".as_ptr());
let res = fetch_result(&bpf);
assert_eq!(res, Ordering::Less);
}

#[test]
fn bpf_strncmp_greater() {
let bpf = load_and_attach_uprobe();
trigger_bpf_strncmp(b"zzz".as_ptr());
let res = fetch_result(&bpf);
assert_eq!(res, Ordering::Greater);
}

fn load_and_attach_uprobe() -> Bpf {
let mut bpf = Bpf::load(crate::STRNCMP).unwrap();

let prog: &mut UProbe = bpf
.program_mut("test_bpf_strncmp")
.unwrap()
.try_into()
.unwrap();
prog.load().unwrap();

prog.attach(Some("trigger_bpf_strncmp"), 0, "/proc/self/exe", None)
.unwrap();

bpf
}

fn fetch_result(bpf: &Bpf) -> Ordering {
let array = Array::<_, TestResult>::try_from(bpf.map("RESULT").unwrap()).unwrap();
let TestResult(res) = array.get(&0, 0).unwrap();
res
}

#[no_mangle]
#[inline(never)]
pub extern "C" fn trigger_bpf_strncmp(string: *const u8) {
core::hint::black_box(string);
}

0 comments on commit 5b86ae3

Please sign in to comment.