diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index bbe064b1..357232ac 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -695,23 +695,17 @@ impl<'a> EbpfLoader<'a> { .collect(); let maps = maps .drain() - .map(parse_map) + .map(|data| parse_map(data, *allow_unsupported_maps)) .collect::, EbpfError>>()?; - if !*allow_unsupported_maps { - maps.iter().try_for_each(|(_, x)| match x { - Map::Unsupported(map) => Err(EbpfError::MapError(MapError::Unsupported { - map_type: map.obj().map_type(), - })), - _ => Ok(()), - })?; - }; - Ok(Ebpf { maps, programs }) } } -fn parse_map(data: (String, MapData)) -> Result<(String, Map), EbpfError> { +fn parse_map( + data: (String, MapData), + allow_unsupported_maps: bool, +) -> Result<(String, Map), EbpfError> { let (name, map) = data; let map_type = bpf_map_type::try_from(map.obj().map_type()).map_err(MapError::from)?; let map = match map_type { @@ -735,9 +729,15 @@ fn parse_map(data: (String, MapData)) -> Result<(String, Map), EbpfError> { BPF_MAP_TYPE_DEVMAP => Map::DevMap(map), BPF_MAP_TYPE_DEVMAP_HASH => Map::DevMapHash(map), BPF_MAP_TYPE_XSKMAP => Map::XskMap(map), - m => { - warn!("The map {name} is of type {:#?} which is currently unsupported in Aya, use `allow_unsupported_maps()` to load it anyways", m); - Map::Unsupported(map) + m_type => { + if allow_unsupported_maps { + Map::Unsupported(map) + } else { + return Err(EbpfError::MapError(MapError::Unsupported { + name, + map_type: m_type, + })); + } } }; diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index bb641288..f5115522 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -195,10 +195,14 @@ pub enum MapError { ProgIdNotSupported, /// Unsupported Map type - #[error("Unsupported map type found {map_type}")] + #[error( + "type of {name} ({map_type:?}) is unsupported; see `EbpfLoader::allow_unsupported_maps`" + )] Unsupported { + /// Map name + name: String, /// The map type - map_type: u32, + map_type: bpf_map_type, }, } diff --git a/ebpf/aya-ebpf/src/helpers.rs b/ebpf/aya-ebpf/src/helpers.rs index 6720bc72..f96044f3 100644 --- a/ebpf/aya-ebpf/src/helpers.rs +++ b/ebpf/aya-ebpf/src/helpers.rs @@ -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)] @@ -838,3 +842,23 @@ 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_ebpf::helpers::bpf_strncmp; +/// # let data = b"something"; +/// assert_ne!(bpf_strncmp(data, c"foo"), core::cmp::Ordering::Equal); +/// ``` +#[inline] +pub fn bpf_strncmp(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) +} diff --git a/test/integration-ebpf/Cargo.toml b/test/integration-ebpf/Cargo.toml index 2649871b..6c550dae 100644 --- a/test/integration-ebpf/Cargo.toml +++ b/test/integration-ebpf/Cargo.toml @@ -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" @@ -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" @@ -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" diff --git a/test/integration-ebpf/src/strncmp.rs b/test/integration-ebpf/src/strncmp.rs new file mode 100644 index 00000000..975d9f0c --- /dev/null +++ b/test/integration-ebpf/src/strncmp.rs @@ -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 = 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 {} +} diff --git a/test/integration-test/src/lib.rs b/test/integration-test/src/lib.rs index ffed5dd4..9d72286a 100644 --- a/test/integration-test/src/lib.rs +++ b/test/integration-test/src/lib.rs @@ -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; diff --git a/test/integration-test/src/tests.rs b/test/integration-test/src/tests.rs index 61f490cf..4b5c8fcb 100644 --- a/test/integration-test/src/tests.rs +++ b/test/integration-test/src/tests.rs @@ -8,5 +8,6 @@ mod rbpf; mod relocations; mod ring_buf; mod smoke; +mod strncmp; mod tcx; 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 00000000..55254d9c --- /dev/null +++ b/test/integration-test/src/tests/strncmp.rs @@ -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); +} diff --git a/xtask/public-api/aya-ebpf.txt b/xtask/public-api/aya-ebpf.txt index 43b93bed..7188dcb0 100644 --- a/xtask/public-api/aya-ebpf.txt +++ b/xtask/public-api/aya-ebpf.txt @@ -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 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(dst: *mut T, src: *const T) -> core::result::Result<(), aya_ebpf_cty::od::c_long> +pub fn aya_ebpf::helpers::bpf_strncmp(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 diff --git a/xtask/public-api/aya.txt b/xtask/public-api/aya.txt index 51106ea3..9fdceb2d 100644 --- a/xtask/public-api/aya.txt +++ b/xtask/public-api/aya.txt @@ -1362,7 +1362,8 @@ pub aya::maps::MapError::ProgIdNotSupported pub aya::maps::MapError::ProgramNotLoaded pub aya::maps::MapError::SyscallError(aya::sys::SyscallError) pub aya::maps::MapError::Unsupported -pub aya::maps::MapError::Unsupported::map_type: u32 +pub aya::maps::MapError::Unsupported::map_type: aya_obj::generated::linux_bindings_x86_64::bpf_map_type +pub aya::maps::MapError::Unsupported::name: alloc::string::String impl core::convert::From for aya::EbpfError pub fn aya::EbpfError::from(source: aya::maps::MapError) -> Self impl core::convert::From for aya::maps::xdp::XdpMapError