Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpf: Add bpf_strncmp helper #871

Merged
merged 2 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion ebpf/aya-ebpf/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@
//! helpers, but 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_ebpf_bindings::helpers as gen;
#[doc(hidden)]
Expand Down Expand Up @@ -838,3 +842,23 @@ 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_ebpf::helpers::bpf_strncmp;
/// # let data = b"something";
/// assert_ne!(bpf_strncmp(data, c"foo"), core::cmp::Ordering::Equal);
/// ```
#[inline]
pub fn bpf_strncmp<const N: usize>(s1: &[u8; N], s2: &CStr) -> Ordering {
// NB: s1 does not need to be null-terminated.
//
// See https://github.com/torvalds/linux/blob/adc218676/include/uapi/linux/bpf.h#L5391-L5393.
//
// NB: s1's size must be known at compile time to appease the verifier. This is also the typical
// usage of strncmp in C programs.
unsafe { gen::bpf_strncmp(s1.as_ptr() as *const _, N as u32, s2.as_ptr() as *const _) }.cmp(&0)
}
44 changes: 24 additions & 20 deletions test/integration-ebpf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ network-types = "0.0.7"
which = { workspace = true }
xtask = { path = "../../xtask" }

[[bin]]
name = "bpf_probe_read"
path = "src/bpf_probe_read.rs"

[[bin]]
name = "log"
path = "src/log.rs"
Expand All @@ -25,6 +29,10 @@ path = "src/log.rs"
name = "map_test"
path = "src/map_test.rs"

[[bin]]
name = "memmove_test"
path = "src/memmove_test.rs"

[[bin]]
name = "name_test"
path = "src/name_test.rs"
Expand All @@ -34,41 +42,37 @@ name = "pass"
path = "src/pass.rs"

[[bin]]
name = "test"
path = "src/test.rs"

[[bin]]
name = "tcx"
path = "src/tcx.rs"
name = "redirect"
path = "src/redirect.rs"

[[bin]]
name = "relocations"
path = "src/relocations.rs"

[[bin]]
name = "bpf_probe_read"
path = "src/bpf_probe_read.rs"
name = "ring_buf"
path = "src/ring_buf.rs"

[[bin]]
name = "two_progs"
path = "src/two_progs.rs"
name = "simple_prog"
path = "src/simple_prog.rs"

[[bin]]
name = "redirect"
path = "src/redirect.rs"
name = "strncmp"
path = "src/strncmp.rs"

[[bin]]
name = "xdp_sec"
path = "src/xdp_sec.rs"
name = "tcx"
path = "src/tcx.rs"

[[bin]]
name = "ring_buf"
path = "src/ring_buf.rs"
name = "test"
path = "src/test.rs"

[[bin]]
name = "memmove_test"
path = "src/memmove_test.rs"
name = "two_progs"
path = "src/two_progs.rs"

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

use core::cmp::Ordering;

use aya_ebpf::{
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 s1: *const u8 = ctx.arg(0).ok_or(-1)?;
let mut b1 = [0u8; 3];
let _: &[u8] = unsafe { bpf_probe_read_user_str_bytes(s1, &mut b1) }?;

let ptr = RESULT.get_ptr_mut(0).ok_or(-1)?;
let dst = unsafe { ptr.as_mut() };
let TestResult(dst_res) = dst.ok_or(-1)?;
*dst_res = bpf_strncmp(&b1, c"ff");

Ok(())
}

#[cfg(not(test))]
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}
15 changes: 8 additions & 7 deletions test/integration-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,22 @@ pub const TEXT_64_64_RELOC: &[u8] =
pub const VARIABLES_RELOC: &[u8] =
include_bytes_aligned!(concat!(env!("OUT_DIR"), "/variables_reloc.bpf.o"));

pub const BPF_PROBE_READ: &[u8] =
include_bytes_aligned!(concat!(env!("OUT_DIR"), "/bpf_probe_read"));
pub const LOG: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/log"));
pub const MAP_TEST: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/map_test"));
pub const MEMMOVE_TEST: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/memmove_test"));
pub const NAME_TEST: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/name_test"));
pub const PASS: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/pass"));
pub const REDIRECT: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/redirect"));
pub const RELOCATIONS: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/relocations"));
pub const RING_BUF: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/ring_buf"));
pub const SIMPLE_PROG: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/simple_prog"));
pub const STRNCMP: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/strncmp"));
pub const TCX: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/tcx"));
pub const TEST: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/test"));
pub const RELOCATIONS: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/relocations"));
pub const TWO_PROGS: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/two_progs"));
pub const BPF_PROBE_READ: &[u8] =
include_bytes_aligned!(concat!(env!("OUT_DIR"), "/bpf_probe_read"));
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 MEMMOVE_TEST: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/memmove_test"));
pub const SIMPLE_PROG: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/simple_prog"));

#[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 @@ -8,5 +8,6 @@ mod rbpf;
mod relocations;
mod ring_buf;
mod smoke;
mod strncmp;
mod tcx;
mod xdp;
55 changes: 55 additions & 0 deletions test/integration-test/src/tests/strncmp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use std::{
cmp::Ordering,
ffi::{c_char, CStr},
};

use aya::{
maps::{Array, MapData},
programs::UProbe,
Ebpf,
};

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

unsafe impl aya::Pod for TestResult {}

#[test]
fn bpf_strncmp() {
let mut bpf = Ebpf::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();
}

let array = Array::<_, TestResult>::try_from(bpf.map("RESULT").unwrap()).unwrap();

assert_eq!(do_bpf_strncmp(&array, c"ff"), Ordering::Equal);

// This is truncated in BPF; the buffer size is 3 including the null terminator.
assert_eq!(do_bpf_strncmp(&array, c"fff"), Ordering::Equal);

assert_eq!(do_bpf_strncmp(&array, c"aa"), Ordering::Less);
assert_eq!(do_bpf_strncmp(&array, c"zz"), Ordering::Greater);
}

fn do_bpf_strncmp(array: &Array<&MapData, TestResult>, s1: &CStr) -> Ordering {
trigger_bpf_strncmp(s1.as_ptr());
let TestResult(ord) = array.get(&0, 0).unwrap();
ord
}

#[no_mangle]
#[inline(never)]
pub extern "C" fn trigger_bpf_strncmp(s1: *const c_char) {
core::hint::black_box(s1);
}
1 change: 1 addition & 0 deletions xtask/public-api/aya-ebpf.txt
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ pub unsafe fn aya_ebpf::helpers::bpf_probe_read_user_buf(src: *const u8, dst: &m
pub unsafe fn aya_ebpf::helpers::bpf_probe_read_user_str(src: *const u8, dest: &mut [u8]) -> core::result::Result<usize, aya_ebpf_cty::od::c_long>
pub unsafe fn aya_ebpf::helpers::bpf_probe_read_user_str_bytes(src: *const u8, dest: &mut [u8]) -> core::result::Result<&[u8], aya_ebpf_cty::od::c_long>
pub unsafe fn aya_ebpf::helpers::bpf_probe_write_user<T>(dst: *mut T, src: *const T) -> core::result::Result<(), aya_ebpf_cty::od::c_long>
pub fn aya_ebpf::helpers::bpf_strncmp<const N: usize>(s1: &[u8; N], s2: &core::ffi::c_str::CStr) -> core::cmp::Ordering
pub mod aya_ebpf::maps
pub mod aya_ebpf::maps::array
#[repr(transparent)] pub struct aya_ebpf::maps::array::Array<T>
Expand Down
Loading