Browse Source

Merge pull request #284 from vadorovsky/str

bpf(helpers): Add *_str_bytes helpers
Alessandro Decina 2 years ago
parent
commit
6f301ac9ca
1 changed files with 220 additions and 0 deletions
  1. 220 0
      bpf/aya-bpf/src/helpers.rs

+ 220 - 0
bpf/aya-bpf/src/helpers.rs

@@ -259,6 +259,9 @@ pub unsafe fn bpf_probe_read_kernel_buf(src: *const u8, dst: &mut [u8]) -> Resul
 /// # Errors
 ///
 /// On failure, this function returns Err(-1).
+#[deprecated(
+    note = "Use `bpf_probe_read_user_str_bytes` or `bpf_probe_read_kernel_str_bytes` instead"
+)]
 #[inline]
 pub unsafe fn bpf_probe_read_str(src: *const u8, dest: &mut [u8]) -> Result<usize, c_long> {
     let len = gen::bpf_probe_read_str(
@@ -302,6 +305,7 @@ pub unsafe fn bpf_probe_read_str(src: *const u8, dest: &mut [u8]) -> Result<usiz
 /// # Errors
 ///
 /// On failure, this function returns Err(-1).
+#[deprecated(note = "Use `bpf_probe_read_user_str_bytes` instead")]
 #[inline]
 pub unsafe fn bpf_probe_read_user_str(src: *const u8, dest: &mut [u8]) -> Result<usize, c_long> {
     let len = gen::bpf_probe_read_user_str(
@@ -322,6 +326,113 @@ pub unsafe fn bpf_probe_read_user_str(src: *const u8, dest: &mut [u8]) -> Result
     Ok(len as usize)
 }
 
+/// Returns a byte slice read from _user space_ address `src`.
+///
+/// Reads at most `dest.len()` bytes from the `src` address, truncating if the
+/// length of the source string is larger than `dest`. On success, the
+/// destination buffer is always null terminated, and the returned slice
+/// includes the bytes up to and not including NULL.
+///
+/// # Examples
+///
+/// With an array allocated on the stack (not recommended for bigger strings,
+/// eBPF stack limit is 512 bytes):
+///
+/// ```no_run
+/// # #![allow(dead_code)]
+/// # use aya_bpf::{cty::c_long, helpers::bpf_probe_read_user_str_bytes};
+/// # fn try_test() -> Result<(), c_long> {
+/// # let user_ptr: *const u8 = 0 as _;
+/// let mut buf = [0u8; 16];
+/// let my_str_bytes = unsafe { bpf_probe_read_user_str_bytes(user_ptr, &mut buf)? };
+///
+/// // Do something with my_str_bytes
+/// # Ok::<(), c_long>(())
+/// # }
+/// ```
+///
+/// With a `PerCpuArray` (with size defined by us):
+///
+/// ```no_run
+/// # use aya_bpf::{cty::c_long, helpers::bpf_probe_read_user_str_bytes};
+/// use aya_bpf::{macros::map, maps::PerCpuArray};
+///
+/// #[repr(C)]
+/// pub struct Buf {
+///     pub buf: [u8; 4096],
+/// }
+///
+/// #[map]
+/// pub static mut BUF: PerCpuArray<Buf> = PerCpuArray::with_max_entries(1, 0);
+///
+/// # fn try_test() -> Result<(), c_long> {
+/// # let user_ptr: *const u8 = 0 as _;
+/// let buf = unsafe {
+///     let ptr = BUF.get_ptr_mut(0).ok_or(0)?;
+///     &mut *ptr
+/// };
+/// let my_str_bytes = unsafe { bpf_probe_read_user_str_bytes(user_ptr, &mut buf.buf)? };
+///
+/// // Do something with my_str_bytes
+/// # Ok::<(), c_long>(())
+/// # }
+/// ```
+///
+/// You can also convert the resulted bytes slice into `&str` using
+/// [core::str::from_utf8_unchecked]:
+///
+/// ```no_run
+/// # #![allow(dead_code)]
+/// # use aya_bpf::{cty::c_long, helpers::bpf_probe_read_user_str_bytes};
+/// # use aya_bpf::{macros::map, maps::PerCpuArray};
+/// # #[repr(C)]
+/// # pub struct Buf {
+/// #     pub buf: [u8; 4096],
+/// # }
+/// # #[map]
+/// # pub static mut BUF: PerCpuArray<Buf> = PerCpuArray::with_max_entries(1, 0);
+/// # fn try_test() -> Result<(), c_long> {
+/// # let user_ptr: *const u8 = 0 as _;
+/// # let buf = unsafe {
+/// #     let ptr = BUF.get_ptr_mut(0).ok_or(0)?;
+/// #     &mut *ptr
+/// # };
+/// let my_str = unsafe {
+///     core::str::from_utf8_unchecked(bpf_probe_read_user_str_bytes(user_ptr, &mut buf.buf)?)
+/// };
+///
+/// // Do something with my_str
+/// # Ok::<(), c_long>(())
+/// # }
+/// ```
+///
+/// # Errors
+///
+/// On failure, this function returns Err(-1).
+#[inline]
+pub unsafe fn bpf_probe_read_user_str_bytes(
+    src: *const u8,
+    dest: &mut [u8],
+) -> Result<&[u8], c_long> {
+    let len = gen::bpf_probe_read_user_str(
+        dest.as_mut_ptr() as *mut c_void,
+        dest.len() as u32,
+        src as *const c_void,
+    );
+    if len < 0 {
+        return Err(-1);
+    }
+
+    let len = len as usize;
+    if len >= dest.len() {
+        // this can never happen, it's needed to tell the verifier that len is
+        // bounded
+        return Err(-1);
+    }
+
+    Ok(&dest[..len])
+}
+
 /// Read a null-terminated string from _kernel space_ stored at `src` into `dest`.
 ///
 /// In case the length of `dest` is smaller then the length of `src`, the read bytes will
@@ -345,6 +456,7 @@ pub unsafe fn bpf_probe_read_user_str(src: *const u8, dest: &mut [u8]) -> Result
 /// # Errors
 ///
 /// On failure, this function returns Err(-1).
+#[deprecated(note = "Use bpf_probe_read_kernel_str_bytes instead")]
 #[inline]
 pub unsafe fn bpf_probe_read_kernel_str(src: *const u8, dest: &mut [u8]) -> Result<usize, c_long> {
     let len = gen::bpf_probe_read_kernel_str(
@@ -365,6 +477,114 @@ pub unsafe fn bpf_probe_read_kernel_str(src: *const u8, dest: &mut [u8]) -> Resu
     Ok(len as usize)
 }
 
+/// Returns a byte slice read from _kernel space_ address `src`.
+///
+/// Reads at most `dest.len()` bytes from the `src` address, truncating if the
+/// length of the source string is larger than `dest`. On success, the
+/// destination buffer is always null terminated, and the returned slice
+/// includes the bytes up to and not including NULL.
+///
+/// # Examples
+///
+/// With an array allocated on the stack (not recommended for bigger strings,
+/// eBPF stack limit is 512 bytes):
+///
+/// ```no_run
+/// # #![allow(dead_code)]
+/// # use aya_bpf::{cty::c_long, helpers::bpf_probe_read_kernel_str_bytes};
+/// # fn try_test() -> Result<(), c_long> {
+/// # let kernel_ptr: *const u8 = 0 as _;
+/// let mut buf = [0u8; 16];
+/// let my_str_bytes = unsafe { bpf_probe_read_kernel_str_bytes(kernel_ptr, &mut buf)? };
+///
+/// // Do something with my_str_bytes
+/// # Ok::<(), c_long>(())
+/// # }
+/// ```
+///
+/// With a `PerCpuArray` (with size defined by us):
+///
+/// ```no_run
+/// # #![allow(dead_code)]
+/// # use aya_bpf::{cty::c_long, helpers::bpf_probe_read_kernel_str_bytes};
+/// use aya_bpf::{macros::map, maps::PerCpuArray};
+///
+/// #[repr(C)]
+/// pub struct Buf {
+///     pub buf: [u8; 4096],
+/// }
+///
+/// #[map]
+/// pub static mut BUF: PerCpuArray<Buf> = PerCpuArray::with_max_entries(1, 0);
+///
+/// # fn try_test() -> Result<(), c_long> {
+/// # let kernel_ptr: *const u8 = 0 as _;
+/// let buf = unsafe {
+///     let ptr = BUF.get_ptr_mut(0).ok_or(0)?;
+///     &mut *ptr
+/// };
+/// let my_str_bytes = unsafe { bpf_probe_read_kernel_str_bytes(kernel_ptr, &mut buf.buf)? };
+///
+/// // Do something with my_str_bytes
+/// # Ok::<(), c_long>(())
+/// # }
+/// ```
+///
+/// You can also convert the resulted bytes slice into `&str` using
+/// [core::str::from_utf8_unchecked]:
+///
+/// ```no_run
+/// # #![allow(dead_code)]
+/// # use aya_bpf::{cty::c_long, helpers::bpf_probe_read_kernel_str_bytes};
+/// # use aya_bpf::{macros::map, maps::PerCpuArray};
+/// # #[repr(C)]
+/// # pub struct Buf {
+/// #     pub buf: [u8; 4096],
+/// # }
+/// # #[map]
+/// # pub static mut BUF: PerCpuArray<Buf> = PerCpuArray::with_max_entries(1, 0);
+/// # fn try_test() -> Result<(), c_long> {
+/// # let kernel_ptr: *const u8 = 0 as _;
+/// # let buf = unsafe {
+/// #     let ptr = BUF.get_ptr_mut(0).ok_or(0)?;
+/// #     &mut *ptr
+/// # };
+/// let my_str = unsafe {
+///     core::str::from_utf8_unchecked(bpf_probe_read_kernel_str_bytes(kernel_ptr, &mut buf.buf)?)
+/// };
+///
+/// // Do something with my_str
+/// # Ok::<(), c_long>(())
+/// # }
+/// ```
+///
+/// # Errors
+///
+/// On failure, this function returns Err(-1).
+#[inline]
+pub unsafe fn bpf_probe_read_kernel_str_bytes(
+    src: *const u8,
+    dest: &mut [u8],
+) -> Result<&[u8], c_long> {
+    let len = gen::bpf_probe_read_kernel_str(
+        dest.as_mut_ptr() as *mut c_void,
+        dest.len() as u32,
+        src as *const c_void,
+    );
+    if len < 0 {
+        return Err(-1);
+    }
+
+    let len = len as usize;
+    if len >= dest.len() {
+        // this can never happen, it's needed to tell the verifier that len is
+        // bounded
+        return Err(-1);
+    }
+
+    Ok(&dest[..len])
+}
+
 /// Write bytes to the _user space_ pointer `src` and store them as a `T`.
 ///
 /// # Examples