From b4f07ca1de247f438da548f6339fac666e6f98d7 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Tue, 7 Oct 2025 15:29:44 -0700 Subject: [PATCH] feature_probe: properly check for LSM support Turns out this is not supported in aarch64 until 6.4. --- aya/src/sys/feature_probe.rs | 63 ++++++++++++------- .../src/tests/feature_probe.rs | 56 ++++++++++------- 2 files changed, 73 insertions(+), 46 deletions(-) diff --git a/aya/src/sys/feature_probe.rs b/aya/src/sys/feature_probe.rs index 230976f0..e980b9e6 100644 --- a/aya/src/sys/feature_probe.rs +++ b/aya/src/sys/feature_probe.rs @@ -1,6 +1,10 @@ //! Probes and identifies available eBPF features supported by the host kernel. -use std::{mem, os::fd::AsRawFd as _, ptr}; +use std::{ + mem, + os::fd::{AsFd as _, AsRawFd as _}, + ptr, +}; use aya_obj::{ btf::{Btf, BtfKind}, @@ -10,7 +14,10 @@ use aya_obj::{ }; use libc::{E2BIG, EBADF, EINVAL}; -use super::{SyscallError, bpf_prog_load, fd_sys_bpf, unit_sys_bpf, with_trivial_prog}; +use super::{ + SyscallError, bpf_prog_load, bpf_raw_tracepoint_open, fd_sys_bpf, unit_sys_bpf, + with_trivial_prog, +}; use crate::{ MockableFd, maps::MapType, @@ -85,7 +92,7 @@ pub fn is_program_supported(program_type: ProgramType) -> Result Result err, - None => return Ok(true), - }; - - // Loading may fail for some types (namely tracing, extension, lsm, & struct_ops), so we - // perform additional examination on the OS error and/or verifier logs. - match &error { - ProgramError::SyscallError(err) => { - match err.io_error.raw_os_error() { + match bpf_prog_load(attr) { + Err(io_error) => match io_error.raw_os_error() { + // Loading may fail for some types (namely tracing, extension, lsm, & struct_ops), so we + // perform additional examination on the OS error and/or verifier logs. + // // For most types, `EINVAL` typically indicates it is not supported. // However, further examination is required for tracing, extension, and lsm. Some(EINVAL) => { @@ -152,11 +148,34 @@ pub fn is_program_supported(program_type: ProgramType) -> Result Ok(true), - _ => Err(error), + _ => Err(ProgramError::SyscallError(SyscallError { + call: "bpf_prog_load", + io_error, + })), + }, + Ok(prog_fd) => { + // Some arm64 kernels (notably < 6.4) can load LSM programs but cannot attach them: + // `bpf_raw_tracepoint_open` fails with `-ENOTSUPP`. Probe attach support + // explicitly. + // + // h/t to https://www.exein.io/blog/exploring-bpf-lsm-support-on-aarch64-with-ftrace. + if program_type != ProgramType::Lsm { + Ok(true) + } else { + match bpf_raw_tracepoint_open(None, prog_fd.as_fd()) { + Ok(_) => Ok(true), + Err(io_error) => match io_error.raw_os_error() { + Some(524) => Ok(false), + _ => Err(ProgramError::SyscallError(SyscallError { + call: "bpf_raw_tracepoint_open", + io_error, + })), + }, + } + } } } - _ => Err(error), - } + }) } /// Whether the host kernel supports the [`MapType`]. diff --git a/test/integration-test/src/tests/feature_probe.rs b/test/integration-test/src/tests/feature_probe.rs index dd76759f..5f148f88 100644 --- a/test/integration-test/src/tests/feature_probe.rs +++ b/test/integration-test/src/tests/feature_probe.rs @@ -107,31 +107,39 @@ fn probe_supported_programs() { kernel_assert!(is_supported!(ProgramType::StructOps), kern_version); kernel_assert!(is_supported!(ProgramType::Extension), kern_version); - let kern_version = KernelVersion::new(5, 7, 0); - // `lsm` requires `CONFIG_DEBUG_INFO_BTF=y` & `CONFIG_BPF_LSM=y` - // Ways to check if `CONFIG_BPF_LSM` is enabled: - // - kernel config has `CONFIG_BPF_LSM=y`, but config is not always exposed. - // - an LSM hook is present in BTF, e.g. `bpf_lsm_bpf`. hooks are found in `bpf_lsm.c` - if current >= kern_version { - let lsm_enabled = matches!( - kernel_config.get("CONFIG_BPF_LSM"), - Some(procfs::ConfigSetting::Yes) - ) || Btf::from_sys_fs() - .and_then(|btf| btf.id_by_type_name_kind("bpf_lsm_bpf", aya_obj::btf::BtfKind::Func)) - .is_ok(); - assert_eq!( - is_supported!(ProgramType::Lsm), - lsm_enabled, - "current={current}" - ); - if !lsm_enabled { - eprintln!("CONFIG_BPF_LSM required for lsm program type"); + { + let kern_version = if cfg!(target_arch = "aarch64") { + KernelVersion::new(6, 4, 0) + } else { + KernelVersion::new(5, 7, 0) + }; + // `lsm` requires `CONFIG_DEBUG_INFO_BTF=y` & `CONFIG_BPF_LSM=y` + // Ways to check if `CONFIG_BPF_LSM` is enabled: + // - kernel config has `CONFIG_BPF_LSM=y`, but config is not always exposed. + // - an LSM hook is present in BTF, e.g. `bpf_lsm_bpf`. hooks are found in `bpf_lsm.c` + if current >= kern_version { + let lsm_enabled = matches!( + kernel_config.get("CONFIG_BPF_LSM"), + Some(procfs::ConfigSetting::Yes) + ) || Btf::from_sys_fs() + .and_then(|btf| { + btf.id_by_type_name_kind("bpf_lsm_bpf", aya_obj::btf::BtfKind::Func) + }) + .is_ok(); + assert_eq!( + is_supported!(ProgramType::Lsm), + lsm_enabled, + "current={current}" + ); + if !lsm_enabled { + eprintln!("CONFIG_BPF_LSM required for lsm program type"); + } + } else { + assert!( + !is_supported!(ProgramType::Lsm), + "{current} < {kern_version}" + ); } - } else { - assert!( - !is_supported!(ProgramType::Lsm), - "{current} < {kern_version}" - ); } let kern_version = KernelVersion::new(5, 9, 0);