From 27d69c35f030dda687664af8dc2d2fd52d238c6b Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Mon, 3 Mar 2025 16:13:11 -0500 Subject: [PATCH] aya: remove panics on indeterminate kernel version Cache the current kernel version in a thread-local while I'm here. Closes https://github.com/aya-rs/aya/issues/1024. Closes https://github.com/aya-rs/aya/pull/1042. --- aya/src/maps/mod.rs | 8 ++----- aya/src/programs/cgroup_device.rs | 2 +- aya/src/programs/cgroup_skb.rs | 2 +- aya/src/programs/cgroup_sock.rs | 2 +- aya/src/programs/cgroup_sock_addr.rs | 2 +- aya/src/programs/cgroup_sockopt.rs | 2 +- aya/src/programs/cgroup_sysctl.rs | 2 +- aya/src/programs/mod.rs | 7 +++++-- aya/src/programs/probe.rs | 2 +- aya/src/programs/sock_ops.rs | 2 +- aya/src/programs/tc.rs | 4 +--- aya/src/programs/xdp.rs | 4 ++-- aya/src/sys/bpf.rs | 3 +-- aya/src/util.rs | 31 +++++++++++++++++++++++++++- 14 files changed, 49 insertions(+), 24 deletions(-) diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index e551bc3d..5e8283e0 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -579,12 +579,8 @@ impl MapData { } }; - #[cfg(not(test))] - let kernel_version = KernelVersion::current().unwrap(); - #[cfg(test)] - let kernel_version = KernelVersion::new(0xff, 0xff, 0xff); - let fd = bpf_create_map(&c_name, &obj, btf_fd, kernel_version).map_err(|io_error| { - if kernel_version < KernelVersion::new(5, 11, 0) { + let fd = bpf_create_map(&c_name, &obj, btf_fd).map_err(|io_error| { + if !KernelVersion::at_least(5, 11, 0) { maybe_warn_rlimit(); } diff --git a/aya/src/programs/cgroup_device.rs b/aya/src/programs/cgroup_device.rs index 7d65e2f4..33421552 100644 --- a/aya/src/programs/cgroup_device.rs +++ b/aya/src/programs/cgroup_device.rs @@ -73,7 +73,7 @@ impl CgroupDevice { let prog_fd = prog_fd.as_fd(); let cgroup_fd = cgroup.as_fd(); - if KernelVersion::current().unwrap() >= KernelVersion::new(5, 7, 0) { + if KernelVersion::at_least(5, 7, 0) { let link_fd = bpf_link_create( prog_fd, LinkTarget::Fd(cgroup_fd), diff --git a/aya/src/programs/cgroup_skb.rs b/aya/src/programs/cgroup_skb.rs index b40c459b..d216eb66 100644 --- a/aya/src/programs/cgroup_skb.rs +++ b/aya/src/programs/cgroup_skb.rs @@ -99,7 +99,7 @@ impl CgroupSkb { CgroupSkbAttachType::Ingress => BPF_CGROUP_INET_INGRESS, CgroupSkbAttachType::Egress => BPF_CGROUP_INET_EGRESS, }; - if KernelVersion::current().unwrap() >= KernelVersion::new(5, 7, 0) { + if KernelVersion::at_least(5, 7, 0) { let link_fd = bpf_link_create( prog_fd, LinkTarget::Fd(cgroup_fd), diff --git a/aya/src/programs/cgroup_sock.rs b/aya/src/programs/cgroup_sock.rs index caf1f067..ea9f8112 100644 --- a/aya/src/programs/cgroup_sock.rs +++ b/aya/src/programs/cgroup_sock.rs @@ -76,7 +76,7 @@ impl CgroupSock { let prog_fd = prog_fd.as_fd(); let cgroup_fd = cgroup.as_fd(); let attach_type = self.data.expected_attach_type.unwrap(); - if KernelVersion::current().unwrap() >= KernelVersion::new(5, 7, 0) { + if KernelVersion::at_least(5, 7, 0) { let link_fd = bpf_link_create( prog_fd, LinkTarget::Fd(cgroup_fd), diff --git a/aya/src/programs/cgroup_sock_addr.rs b/aya/src/programs/cgroup_sock_addr.rs index 98abc554..22e29dba 100644 --- a/aya/src/programs/cgroup_sock_addr.rs +++ b/aya/src/programs/cgroup_sock_addr.rs @@ -77,7 +77,7 @@ impl CgroupSockAddr { let prog_fd = prog_fd.as_fd(); let cgroup_fd = cgroup.as_fd(); let attach_type = self.data.expected_attach_type.unwrap(); - if KernelVersion::current().unwrap() >= KernelVersion::new(5, 7, 0) { + if KernelVersion::at_least(5, 7, 0) { let link_fd = bpf_link_create( prog_fd, LinkTarget::Fd(cgroup_fd), diff --git a/aya/src/programs/cgroup_sockopt.rs b/aya/src/programs/cgroup_sockopt.rs index bfd4d857..ed2d6542 100644 --- a/aya/src/programs/cgroup_sockopt.rs +++ b/aya/src/programs/cgroup_sockopt.rs @@ -74,7 +74,7 @@ impl CgroupSockopt { let prog_fd = prog_fd.as_fd(); let cgroup_fd = cgroup.as_fd(); let attach_type = self.data.expected_attach_type.unwrap(); - if KernelVersion::current().unwrap() >= KernelVersion::new(5, 7, 0) { + if KernelVersion::at_least(5, 7, 0) { let link_fd = bpf_link_create( prog_fd, LinkTarget::Fd(cgroup_fd), diff --git a/aya/src/programs/cgroup_sysctl.rs b/aya/src/programs/cgroup_sysctl.rs index 96e57dc9..299e662e 100644 --- a/aya/src/programs/cgroup_sysctl.rs +++ b/aya/src/programs/cgroup_sysctl.rs @@ -72,7 +72,7 @@ impl CgroupSysctl { let prog_fd = prog_fd.as_fd(); let cgroup_fd = cgroup.as_fd(); - if KernelVersion::current().unwrap() >= KernelVersion::new(5, 7, 0) { + if KernelVersion::at_least(5, 7, 0) { let link_fd = bpf_link_create( prog_fd, LinkTarget::Fd(cgroup_fd), diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs index 89ce8d26..d83e882b 100644 --- a/aya/src/programs/mod.rs +++ b/aya/src/programs/mod.rs @@ -659,8 +659,11 @@ fn load_program( }, ) = obj; - let target_kernel_version = - kernel_version.unwrap_or_else(|| KernelVersion::current().unwrap().code()); + let target_kernel_version = kernel_version.unwrap_or_else(|| { + KernelVersion::current() + .map(KernelVersion::code) + .unwrap_or(0) + }); let prog_name = if let Some(name) = name.as_deref() { let prog_name = CString::new(name).map_err(|err @ std::ffi::NulError { .. }| { diff --git a/aya/src/programs/probe.rs b/aya/src/programs/probe.rs index d197667d..43db919c 100644 --- a/aya/src/programs/probe.rs +++ b/aya/src/programs/probe.rs @@ -118,7 +118,7 @@ pub(crate) fn attach>( // Use debugfs to create probe let prog_fd = program_data.fd()?; let prog_fd = prog_fd.as_fd(); - let link = if KernelVersion::current().unwrap() < KernelVersion::new(4, 17, 0) { + let link = if !KernelVersion::at_least(4, 17, 0) { if cookie.is_some() { return Err(ProgramError::AttachCookieNotSupported); } diff --git a/aya/src/programs/sock_ops.rs b/aya/src/programs/sock_ops.rs index 90d2e2fe..0dab18f8 100644 --- a/aya/src/programs/sock_ops.rs +++ b/aya/src/programs/sock_ops.rs @@ -71,7 +71,7 @@ impl SockOps { let prog_fd = prog_fd.as_fd(); let cgroup_fd = cgroup.as_fd(); let attach_type = BPF_CGROUP_SOCK_OPS; - if KernelVersion::current().unwrap() >= KernelVersion::new(5, 7, 0) { + if KernelVersion::at_least(5, 7, 0) { let link_fd = bpf_link_create( prog_fd, LinkTarget::Fd(cgroup_fd), diff --git a/aya/src/programs/tc.rs b/aya/src/programs/tc.rs index 98be9af4..a61d79c3 100644 --- a/aya/src/programs/tc.rs +++ b/aya/src/programs/tc.rs @@ -181,9 +181,7 @@ impl SchedClassifier { interface: &str, attach_type: TcAttachType, ) -> Result { - if !matches!(attach_type, TcAttachType::Custom(_)) - && KernelVersion::current().unwrap() >= KernelVersion::new(6, 6, 0) - { + if !matches!(attach_type, TcAttachType::Custom(_)) && KernelVersion::at_least(6, 6, 0) { self.attach_with_options( interface, attach_type, diff --git a/aya/src/programs/xdp.rs b/aya/src/programs/xdp.rs index ead9f7ec..a6a1eb7c 100644 --- a/aya/src/programs/xdp.rs +++ b/aya/src/programs/xdp.rs @@ -133,7 +133,7 @@ impl Xdp { let prog_fd = self.fd()?; let prog_fd = prog_fd.as_fd(); - if KernelVersion::current().unwrap() >= KernelVersion::new(5, 9, 0) { + if KernelVersion::at_least(5, 9, 0) { // Unwrap safety: the function starts with `self.fd()?` that will succeed if and only // if the program has been loaded, i.e. there is an fd. We get one by: // - Using `Xdp::from_pin` that sets `expected_attach_type` @@ -255,7 +255,7 @@ impl Link for NlLink { } fn detach(self) -> Result<(), ProgramError> { - let flags = if KernelVersion::current().unwrap() >= KernelVersion::new(5, 7, 0) { + let flags = if KernelVersion::at_least(5, 7, 0) { self.flags.bits() | XDP_FLAGS_REPLACE } else { self.flags.bits() diff --git a/aya/src/sys/bpf.rs b/aya/src/sys/bpf.rs index fb938286..078e2443 100644 --- a/aya/src/sys/bpf.rs +++ b/aya/src/sys/bpf.rs @@ -46,7 +46,6 @@ pub(crate) fn bpf_create_map( name: &CStr, def: &aya_obj::Map, btf_fd: Option>, - kernel_version: KernelVersion, ) -> io::Result { let mut attr = unsafe { mem::zeroed::() }; @@ -93,7 +92,7 @@ pub(crate) fn bpf_create_map( // https://github.com/torvalds/linux/commit/ad5b177bd73f5107d97c36f56395c4281fb6f089 // The map name was added as a parameter in kernel 4.15+ so we skip adding it on // older kernels for compatibility - if kernel_version >= KernelVersion::new(4, 15, 0) { + if KernelVersion::at_least(4, 15, 0) { // u.map_name is 16 bytes max and must be NULL terminated let name_bytes = name.to_bytes_with_nul(); let len = cmp::min(name_bytes.len(), u.map_name.len()); diff --git a/aya/src/util.rs b/aya/src/util.rs index 5c3b3967..ba9af55a 100644 --- a/aya/src/util.rs +++ b/aya/src/util.rs @@ -14,6 +14,7 @@ use std::{ use aya_obj::generated::{TC_H_MAJ_MASK, TC_H_MIN_MASK}; use libc::{if_nametoindex, sysconf, uname, utsname, _SC_PAGESIZE}; +use log::warn; use crate::Pod; @@ -48,7 +49,35 @@ impl KernelVersion { /// Returns the kernel version of the currently running kernel. pub fn current() -> Result { - Self::get_kernel_version() + thread_local! { + // TODO(https://github.com/rust-lang/rust/issues/109737): Use + // `std::cell::OnceCell` when `get_or_try_init` is stabilized. + static CACHE: once_cell::unsync::OnceCell = const { once_cell::unsync::OnceCell::new() }; + } + CACHE.with(|cell| { + // TODO(https://github.com/rust-lang/rust/issues/109737): Replace `once_cell` with + // `std::cell::OnceCell`. + cell.get_or_try_init(|| { + // error: unsupported operation: `open` not available when isolation is enabled + if cfg!(miri) { + Ok(Self::new(0xff, 0xff, 0xff)) + } else { + Self::get_kernel_version() + } + }) + .copied() + }) + } + + /// Returns true iff the current kernel version is greater than or equal to the given version. + pub(crate) fn at_least(major: u8, minor: u8, patch: u16) -> bool { + match Self::current() { + Ok(current) => current >= Self::new(major, minor, patch), + Err(error) => { + warn!("failed to get current kernel version: {error}"); + false + } + } } /// The equivalent of LINUX_VERSION_CODE.