diff --git a/ebpf/aya-ebpf/src/args.rs b/ebpf/aya-ebpf/src/args.rs index 7cee97a8..9a061318 100644 --- a/ebpf/aya-ebpf/src/args.rs +++ b/ebpf/aya-ebpf/src/args.rs @@ -33,6 +33,14 @@ unsafe impl FromBtfArgument for *const T { } } +unsafe impl FromBtfArgument for *mut T { + unsafe fn from_argument(ctx: *const c_void, n: usize) -> *mut T { + // BTF arguments are exposed as an array of `usize` where `usize` can + // either be treated as a pointer or a primitive type + *(ctx as *const usize).add(n) as _ + } +} + /// Helper macro to implement [`FromBtfArgument`] for a primitive type. macro_rules! unsafe_impl_from_btf_argument { ($type:ident) => { diff --git a/ebpf/aya-ebpf/src/maps/inode_storage.rs b/ebpf/aya-ebpf/src/maps/inode_storage.rs index c04e7d0d..5d275b05 100644 --- a/ebpf/aya-ebpf/src/maps/inode_storage.rs +++ b/ebpf/aya-ebpf/src/maps/inode_storage.rs @@ -143,7 +143,7 @@ const fn build_def(ty: u32, flags: u32, pin: PinningType) -> bpf_map_def { type_: ty, key_size: mem::size_of::() as u32, value_size: mem::size_of::() as u32, - max_entries: 0, + max_entries: 1, map_flags: flags, id: 0, pinning: pin as u32, diff --git a/ebpf/aya-ebpf/src/programs/lsm.rs b/ebpf/aya-ebpf/src/programs/lsm.rs index 0ad29590..58799676 100644 --- a/ebpf/aya-ebpf/src/programs/lsm.rs +++ b/ebpf/aya-ebpf/src/programs/lsm.rs @@ -51,7 +51,7 @@ impl LsmContext { /// /// [1]: https://elixir.bootlin.com/linux/latest/source/include/linux/lsm_hook_defs.h pub unsafe fn arg(&self, n: usize) -> T { - T::from_argument(self.ctx as *const _, n) + T::from_argument(self.ctx, n) } } diff --git a/test/integration-ebpf/src/inode_storage.rs b/test/integration-ebpf/src/inode_storage.rs index bf793740..493260fd 100644 --- a/test/integration-ebpf/src/inode_storage.rs +++ b/test/integration-ebpf/src/inode_storage.rs @@ -1,10 +1,46 @@ +//! The purpose of this test is to identify if we can prevent a tmpfile from being linked to by +//! storing information in inode storage. + #![no_std] #![no_main] -use aya_ebpf::{macros::map, maps::inode_storage::InodeStorage}; +use aya_ebpf::{ + bindings::BPF_F_NO_PREALLOC, + cty::c_void, + macros::{lsm, map}, + maps::inode_storage::InodeStorage, + programs::LsmContext, +}; +use aya_log_ebpf::warn; #[map] -static INODE_STORE: InodeStorage = InodeStorage::new(0); +static TMP_INODE_STORE: InodeStorage = InodeStorage::new(BPF_F_NO_PREALLOC); + +#[lsm(hook = "inode_post_create_tmpfile")] +pub fn inode_post_create_tmpfile(ctx: LsmContext) -> i32 { + unsafe { try_inode_post_create_tmpfile(ctx) }.unwrap_or_else(|ret| ret) +} + +unsafe fn try_inode_post_create_tmpfile(ctx: LsmContext) -> Result { + let tmpfile: *mut c_void = ctx.arg(1); + if TMP_INODE_STORE.get_or_insert_ptr(tmpfile, &0).is_none() { + warn!(&ctx, "Couldn't add information that we deleted a tmp node!"); + } + Ok(0) +} + +#[lsm(hook = "inode_link")] +pub fn inode_link(ctx: LsmContext) -> i32 { + unsafe { try_inode_link(ctx) }.unwrap_or_else(|ret| ret) +} + +unsafe fn try_inode_link(ctx: LsmContext) -> Result { + let maybe_tmpfile: *mut c_void = ctx.arg(0); + if TMP_INODE_STORE.get(maybe_tmpfile).is_some() { + return Err(130); + } + Ok(0) +} #[cfg(not(test))] #[panic_handler] diff --git a/test/integration-test/src/lib.rs b/test/integration-test/src/lib.rs index d4708033..d2ae2e70 100644 --- a/test/integration-test/src/lib.rs +++ b/test/integration-test/src/lib.rs @@ -12,6 +12,8 @@ pub const TEXT_64_64_RELOC: &[u8] = 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 INODE_STORAGE_TEST: &[u8] = + include_bytes_aligned!(concat!(env!("OUT_DIR"), "/inode_storage")); 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 TEST: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/test")); diff --git a/test/integration-test/src/tests.rs b/test/integration-test/src/tests.rs index f37d54bb..fdfe5c70 100644 --- a/test/integration-test/src/tests.rs +++ b/test/integration-test/src/tests.rs @@ -1,6 +1,7 @@ mod bpf_probe_read; mod btf_relocations; mod elf; +mod inode_storage; mod load; mod log; mod rbpf; diff --git a/test/integration-test/src/tests/inode_storage.rs b/test/integration-test/src/tests/inode_storage.rs new file mode 100644 index 00000000..2defe0b6 --- /dev/null +++ b/test/integration-test/src/tests/inode_storage.rs @@ -0,0 +1,49 @@ +use std::{ + error::Error, + fs::OpenOptions, + os::{fd::IntoRawFd, unix::fs::OpenOptionsExt}, +}; + +use aya::{programs::Lsm, Ebpf}; +use aya_obj::btf::Btf; +use libc::{linkat, AT_EMPTY_PATH, AT_FDCWD, O_TMPFILE}; + +use crate::INODE_STORAGE_TEST; + +#[test] +fn no_link_to_tmp() -> Result<(), Box> { + let mut bpf = Ebpf::load(INODE_STORAGE_TEST)?; + let btf = Btf::from_sys_fs()?; + + let rename: &mut Lsm = bpf + .program_mut("inode_post_create_tmpfile") + .unwrap() + .try_into()?; + rename.load("inode_post_create_tmpfile", &btf)?; + rename.attach()?; + + let link: &mut Lsm = bpf.program_mut("inode_link").unwrap().try_into()?; + link.load("inode_link", &btf)?; + link.attach()?; + + // create a temporary file + let tmpfile = OpenOptions::new() + .custom_flags(O_TMPFILE) + .create_new(true) + .open("/tmp/")?; + + let fd = tmpfile.into_raw_fd(); + let res = unsafe { + linkat( + fd, + c"".as_ptr(), + AT_FDCWD, + c"/tmp/blah".as_ptr(), + AT_EMPTY_PATH, + ) + }; + + assert_eq!(130, res); + + Ok(()) +}