From 5d65c9d6eef7f514e53157576dcac6610544b5a6 Mon Sep 17 00:00:00 2001 From: Wouter Dullaert Date: Fri, 6 Sep 2024 17:42:05 +0200 Subject: [PATCH] feat: Add an option to register allowed ranges of memory Certain kernel helpers return pointers to kernel managed memory, which the ebpf program is allowed to load and read. In order to implement stubs for these using rbpf, we need a way to mark these ranges of memory as safe for the check_mem function. The API specifically deals with adresses, because helpers need to be function types and not closures. This means the pointers to objects returned from them need to be static, and dealing with references to static objects gets clunky. So I have chosen to push the obtaining of the addresses into calling code. Signed-off-by: Wouter Dullaert --- Cargo.toml | 3 + README.md | 10 ++ examples/allowed-memory.o | Bin 0 -> 46624 bytes examples/allowed_memory.rs | 54 +++++++++++ examples/ebpf-allowed-memory.rs | 40 ++++++++ src/interpreter.rs | 23 +++-- src/lib.rs | 158 +++++++++++++++++++++++++++++++- 7 files changed, 280 insertions(+), 8 deletions(-) create mode 100644 examples/allowed-memory.o create mode 100644 examples/allowed_memory.rs create mode 100644 examples/ebpf-allowed-memory.rs diff --git a/Cargo.toml b/Cargo.toml index f37652338..da1986d7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,3 +78,6 @@ name = "to_json" [[example]] name = "rbpf_plugin" + +[[example]] +name = "allowed_memory" diff --git a/README.md b/README.md index f15d9500e..337411be2 100644 --- a/README.md +++ b/README.md @@ -209,6 +209,16 @@ registers in a hashmap, so the key can be any `u32` value you want. It may be useful for programs that should be compatible with the Linux kernel and therefore must use specific helper numbers. +```rust,ignore +pub fn register_allowed_memory(&mut self,, addr: &[u64]) -> () +``` + +This function adds a list of memory addresses that the ebpf program is allowed +to load and store. Multiple calls to this function will append the addresses to +an internal HashSet. At the moment rbpf only validates memory accesses when +using the interpreter. This function is useful when using kernel helpers which +return pointers to objects stored in ebpf maps. + ```rust,ignore // for struct EbpfVmMbuff pub fn execute_program(&self, diff --git a/examples/allowed-memory.o b/examples/allowed-memory.o new file mode 100644 index 0000000000000000000000000000000000000000..9b396d6cc59be0b188ea3c08300256064c66af35 GIT binary patch literal 46624 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 u64 { + let key: Key = unsafe { *(key_addr as *const Key) }; + if key.protocol == 1 { + return addr_of!(MAP_VALUE) as u64; + } + 0 +} + +fn main() { + let file = elf::File::open_path("examples/allowed-memory.o").unwrap(); + let func = file.get_section("classifier").unwrap(); + + let mut vm = rbpf::EbpfVmNoData::new(Some(&func.data)).unwrap(); + vm.register_helper(BPF_MAP_LOOKUP_ELEM_IDX, bpf_lookup_elem) + .unwrap(); + + let start = addr_of!(MAP_VALUE) as u64; + let addrs = Vec::from_iter(start..start + size_of::() as u64); + vm.register_allowed_memory(&addrs); + + let res = vm.execute_program().unwrap(); + assert_eq!(res, 1); +} diff --git a/examples/ebpf-allowed-memory.rs b/examples/ebpf-allowed-memory.rs new file mode 100644 index 000000000..88ff3cbf7 --- /dev/null +++ b/examples/ebpf-allowed-memory.rs @@ -0,0 +1,40 @@ +#![no_std] +#![no_main] + +use aya_ebpf::{ + bindings::{BPF_F_NO_PREALLOC, TC_ACT_PIPE}, + macros::{classifier, map}, + maps::HashMap, + programs::TcContext, +}; + +#[no_mangle] +#[link_section = "license"] +pub static LICENSE: [u8; 13] = *b"Dual MIT/GPL\0"; + +#[map] +static RULES: HashMap = HashMap::::with_max_entries(1, BPF_F_NO_PREALLOC); + +#[repr(C, packed)] +pub struct Key { + pub protocol: u8, +} + +#[repr(C, packed)] +pub struct Value { + pub result: i32, +} + +#[classifier] +pub fn ingress_tc(_ctx: TcContext) -> i32 { + let key = Key { protocol: 1 }; + if let Some(action) = unsafe { RULES.get(&key) } { + return action.result; + } + return TC_ACT_PIPE; +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + unsafe { core::hint::unreachable_unchecked() } +} diff --git a/src/interpreter.rs b/src/interpreter.rs index 33a406226..1e83ee204 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -9,16 +9,19 @@ use ebpf; use crate::lib::*; fn check_mem(addr: u64, len: usize, access_type: &str, insn_ptr: usize, - mbuff: &[u8], mem: &[u8], stack: &[u8]) -> Result<(), Error> { + mbuff: &[u8], mem: &[u8], stack: &[u8], allowed_memory: &HashSet) -> Result<(), Error> { if let Some(addr_end) = addr.checked_add(len as u64) { if mbuff.as_ptr() as u64 <= addr && addr_end <= mbuff.as_ptr() as u64 + mbuff.len() as u64 { - return Ok(()) + return Ok(()); } if mem.as_ptr() as u64 <= addr && addr_end <= mem.as_ptr() as u64 + mem.len() as u64 { - return Ok(()) + return Ok(()); } if stack.as_ptr() as u64 <= addr && addr_end <= stack.as_ptr() as u64 + stack.len() as u64 { - return Ok(()) + return Ok(()); + } + if allowed_memory.contains(&addr) { + return Ok(()); } } @@ -33,7 +36,13 @@ fn check_mem(addr: u64, len: usize, access_type: &str, insn_ptr: usize, #[allow(unknown_lints)] #[allow(cyclomatic_complexity)] -pub fn execute_program(prog_: Option<&[u8]>, mem: &[u8], mbuff: &[u8], helpers: &HashMap) -> Result { +pub fn execute_program( + prog_: Option<&[u8]>, + mem: &[u8], + mbuff: &[u8], + helpers: &HashMap, + allowed: &HashSet, +) -> Result { const U32MAX: u64 = u32::MAX as u64; const SHIFT_MASK_64: u64 = 0x3f; @@ -56,10 +65,10 @@ pub fn execute_program(prog_: Option<&[u8]>, mem: &[u8], mbuff: &[u8], helpers: } let check_mem_load = | addr: u64, len: usize, insn_ptr: usize | { - check_mem(addr, len, "load", insn_ptr, mbuff, mem, &stack) + check_mem(addr, len, "load", insn_ptr, mbuff, mem, &stack, allowed) }; let check_mem_store = | addr: u64, len: usize, insn_ptr: usize | { - check_mem(addr, len, "store", insn_ptr, mbuff, mem, &stack) + check_mem(addr, len, "store", insn_ptr, mbuff, mem, &stack, allowed) }; // Loop on instructions diff --git a/src/lib.rs b/src/lib.rs index f348fef25..78d5c88d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -182,6 +182,7 @@ pub struct EbpfVmMbuff<'a> { #[cfg(feature = "cranelift")] cranelift_prog: Option, helpers: HashMap, + allowed_memory: HashSet, } impl<'a> EbpfVmMbuff<'a> { @@ -213,6 +214,7 @@ impl<'a> EbpfVmMbuff<'a> { #[cfg(feature = "cranelift")] cranelift_prog: None, helpers: HashMap::new(), + allowed_memory: HashSet::new(), }) } @@ -320,6 +322,46 @@ impl<'a> EbpfVmMbuff<'a> { Ok(()) } + /// Register a set of addresses that the ebpf program is allowed to load and store. + /// + /// When using certain helpers, typically map lookups, the linux kernel will return pointers + /// to structs that the ebpf program needs to interact with. By default rbpf only allows the + /// program to interact with its stack, the memory buffer and the program itself, making it + /// impossible to supply functional implementations of these helpers. + /// This option allows you to pass in a list of addresses that rbpf will allow the program + /// to load and store to. Given rust's memory model you will always know these addresses up + /// front when implementing the helpers. + /// + /// Each invocation of this method will append to the set of allowed addresses. + /// + /// # Examples + /// + /// ``` + /// use std::iter::FromIterator; + /// use std::ptr::addr_of; + /// + /// struct MapValue { + /// data: u8 + /// } + /// static VALUE: MapValue = MapValue { data: 1 }; + /// + /// let prog = &[ + /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 + /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit + /// ]; + /// + /// // Instantiate a VM. + /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap(); + /// let start = addr_of!(VALUE) as u64; + /// let addrs = Vec::from_iter(start..start+size_of::() as u64); + /// vm.register_allowed_memory(&addrs); + /// ``` + pub fn register_allowed_memory(&mut self, addrs: &[u64]) -> () { + for i in addrs { + self.allowed_memory.insert(*i); + } + } + /// Execute the program loaded, with the given packet data and metadata buffer. /// /// If the program is made to be compatible with Linux kernel, it is expected to load the @@ -357,7 +399,7 @@ impl<'a> EbpfVmMbuff<'a> { /// assert_eq!(res, 0x2211); /// ``` pub fn execute_program(&self, mem: &[u8], mbuff: &[u8]) -> Result { - interpreter::execute_program(self.prog, mem, mbuff, &self.helpers) + interpreter::execute_program(self.prog, mem, mbuff, &self.helpers, &self.allowed_memory) } /// JIT-compile the loaded program. No argument required for this. @@ -826,6 +868,44 @@ impl<'a> EbpfVmFixedMbuff<'a> { self.parent.register_helper(key, function) } + /// Register an object that the ebpf program is allowed to load and store. + /// + /// When using certain helpers, typically map lookups, the linux kernel will return pointers + /// to structs that the ebpf program needs to interact with. By default rbpf only allows the + /// program to interact with its stack, the memory buffer and the program itself, making it + /// impossible to supply functional implementations of these helpers. + /// This option allows you to pass in a list of addresses that rbpf will allow the program + /// to load and store to. Given rust's memory model you will always know these addresses up + /// front when implementing the helpers. + /// + /// Each invocation of this method will append to the set of allowed addresses. + /// + /// # Examples + /// + /// ``` + /// use std::iter::FromIterator; + /// use std::ptr::addr_of; + /// + /// struct MapValue { + /// data: u8 + /// } + /// static VALUE: MapValue = MapValue { data: 1 }; + /// + /// let prog = &[ + /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 + /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit + /// ]; + /// + /// // Instantiate a VM. + /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); + /// let start = addr_of!(VALUE) as u64; + /// let addrs = Vec::from_iter(start..start+size_of::() as u64); + /// vm.register_allowed_memory(&addrs); + /// ``` + pub fn register_allowed_memory(&mut self, allowed: &[u64]) -> () { + self.parent.register_allowed_memory(allowed) + } + /// Execute the program loaded, with the given packet data. /// /// If the program is made to be compatible with Linux kernel, it is expected to load the @@ -1260,6 +1340,44 @@ impl<'a> EbpfVmRaw<'a> { self.parent.register_helper(key, function) } + /// Register an object that the ebpf program is allowed to load and store. + /// + /// When using certain helpers, typically map lookups, the linux kernel will return pointers + /// to structs that the ebpf program needs to interact with. By default rbpf only allows the + /// program to interact with its stack, the memory buffer and the program itself, making it + /// impossible to supply functional implementations of these helpers. + /// This option allows you to pass in a list of addresses that rbpf will allow the program + /// to load and store to. Given rust's memory model you will always know these addresses up + /// front when implementing the helpers. + /// + /// Each invocation of this method will append to the set of allowed addresses. + /// + /// # Examples + /// + /// ``` + /// use std::iter::FromIterator; + /// use std::ptr::addr_of; + /// + /// struct MapValue { + /// data: u8 + /// } + /// static VALUE: MapValue = MapValue { data: 1 }; + /// + /// let prog = &[ + /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 + /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit + /// ]; + /// + /// // Instantiate a VM. + /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); + /// let start = addr_of!(VALUE) as u64; + /// let addrs = Vec::from_iter(start..start+size_of::() as u64); + /// vm.register_allowed_memory(&addrs); + /// ``` + pub fn register_allowed_memory(&mut self, allowed: &[u64]) -> () { + self.parent.register_allowed_memory(allowed) + } + /// Execute the program loaded, with the given packet data. /// /// # Examples @@ -1602,6 +1720,44 @@ impl<'a> EbpfVmNoData<'a> { self.parent.register_helper(key, function) } + /// Register an object that the ebpf program is allowed to load and store. + /// + /// When using certain helpers, typically map lookups, the linux kernel will return pointers + /// to structs that the ebpf program needs to interact with. By default rbpf only allows the + /// program to interact with its stack, the memory buffer and the program itself, making it + /// impossible to supply functional implementations of these helpers. + /// This option allows you to pass in a list of addresses that rbpf will allow the program + /// to load and store to. Given rust's memory model you will always know these addresses up + /// front when implementing the helpers. + /// + /// Each invocation of this method will append to the set of allowed addresses. + /// + /// # Examples + /// + /// ``` + /// use std::iter::FromIterator; + /// use std::ptr::addr_of; + /// + /// struct MapValue { + /// data: u8 + /// } + /// static VALUE: MapValue = MapValue { data: 1 }; + /// + /// let prog = &[ + /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 + /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit + /// ]; + /// + /// // Instantiate a VM. + /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); + /// let start = addr_of!(VALUE) as u64; + /// let addrs = Vec::from_iter(start..start+size_of::() as u64); + /// vm.register_allowed_memory(&addrs); + /// ``` + pub fn register_allowed_memory(&mut self, allowed: &[u64]) -> () { + self.parent.register_allowed_memory(allowed) + } + /// JIT-compile the loaded program. No argument required for this. /// /// If using helper functions, be sure to register them into the VM before calling this