diff --git a/bpf/aya-bpf/src/helpers.rs b/bpf/aya-bpf/src/helpers.rs index 169c83ac2..f955c5214 100644 --- a/bpf/aya-bpf/src/helpers.rs +++ b/bpf/aya-bpf/src/helpers.rs @@ -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)] @@ -836,3 +840,32 @@ pub unsafe fn bpf_printk_impl( _ => 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) +} diff --git a/test/integration-ebpf/Cargo.toml b/test/integration-ebpf/Cargo.toml index d471acf31..80a1942ec 100644 --- a/test/integration-ebpf/Cargo.toml +++ b/test/integration-ebpf/Cargo.toml @@ -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" diff --git a/test/integration-ebpf/src/strncmp.rs b/test/integration-ebpf/src/strncmp.rs new file mode 100644 index 000000000..524fdddbc --- /dev/null +++ b/test/integration-ebpf/src/strncmp.rs @@ -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 = 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 {} +} diff --git a/test/integration-test/src/lib.rs b/test/integration-test/src/lib.rs index d47080336..06a6e29d8 100644 --- a/test/integration-test/src/lib.rs +++ b/test/integration-test/src/lib.rs @@ -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; diff --git a/test/integration-test/src/tests.rs b/test/integration-test/src/tests.rs index f37d54bbe..2b5c2ebb0 100644 --- a/test/integration-test/src/tests.rs +++ b/test/integration-test/src/tests.rs @@ -7,4 +7,5 @@ mod rbpf; mod relocations; mod ring_buf; mod smoke; +mod strncmp; mod xdp; diff --git a/test/integration-test/src/tests/strncmp.rs b/test/integration-test/src/tests/strncmp.rs new file mode 100644 index 000000000..19b3438fb --- /dev/null +++ b/test/integration-test/src/tests/strncmp.rs @@ -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); +}