@@ -11,6 +11,8 @@ pub struct KernelCapability {
1111 pub bpf_lsm_configured : bool ,
1212 pub bpf_lsm_active : bool ,
1313 pub bpf_kprobe_override_configured : bool ,
14+ pub bpf_kprobe_override_available : bool ,
15+ pub bpf_kprobe_override_symbols : Vec < String > ,
1416 pub seccomp_filter_configured : bool ,
1517 pub btf_vmlinux_available : bool ,
1618}
@@ -29,14 +31,18 @@ impl KernelCapability {
2931 let lsm_text = fs:: read_to_string ( sys_root. join ( "kernel/security/lsm" ) ) . unwrap_or_default ( ) ;
3032 let config_text = read_kernel_config_from_roots ( proc_root, boot_root) . unwrap_or_default ( ) ;
3133 let bpf_lsm_active = lsm_has_bpf ( & lsm_text) ;
34+ let bpf_kprobe_override_symbols = read_kprobe_override_symbols ( sys_root) ;
35+ let bpf_kprobe_override_configured =
36+ config_enabled ( & config_text, "CONFIG_BPF_KPROBE_OVERRIDE" )
37+ || !bpf_kprobe_override_symbols. is_empty ( ) ;
3238
3339 Self {
3440 bpf_lsm_configured : config_enabled ( & config_text, "CONFIG_BPF_LSM" ) || bpf_lsm_active,
3541 bpf_lsm_active,
36- bpf_kprobe_override_configured : config_enabled (
37- & config_text ,
38- "CONFIG_BPF_KPROBE_OVERRIDE" ,
39- ) ,
42+ bpf_kprobe_override_configured,
43+ bpf_kprobe_override_available : bpf_kprobe_override_configured
44+ && !bpf_kprobe_override_symbols . is_empty ( ) ,
45+ bpf_kprobe_override_symbols ,
4046 seccomp_filter_configured : config_enabled ( & config_text, "CONFIG_SECCOMP_FILTER" ) ,
4147 btf_vmlinux_available : sys_root. join ( "kernel/btf/vmlinux" ) . exists ( ) ,
4248 }
@@ -45,6 +51,14 @@ impl KernelCapability {
4551 pub fn supports_exec_lsm_enforcement ( & self ) -> bool {
4652 self . bpf_lsm_configured && self . bpf_lsm_active
4753 }
54+
55+ pub fn supports_kprobe_override_symbol ( & self , symbol : & str ) -> bool {
56+ self . bpf_kprobe_override_available
57+ && self
58+ . bpf_kprobe_override_symbols
59+ . iter ( )
60+ . any ( |allowed| allowed == symbol)
61+ }
4862}
4963
5064fn path_from_env ( name : & str , default : & str ) -> PathBuf {
@@ -97,6 +111,37 @@ fn read_proc_kernel_config(proc_root: &Path) -> Option<String> {
97111 decode_gzip ( & compressed) . ok ( )
98112}
99113
114+ fn read_kprobe_override_symbols ( sys_root : & Path ) -> Vec < String > {
115+ const REL_PATHS : [ & str ; 2 ] = [
116+ "kernel/debug/error_injection/list" ,
117+ "kernel/debug/fail_function/injectable" ,
118+ ] ;
119+
120+ let mut symbols = Vec :: new ( ) ;
121+ for rel_path in REL_PATHS {
122+ let Ok ( text) = fs:: read_to_string ( sys_root. join ( rel_path) ) else {
123+ continue ;
124+ } ;
125+ symbols. extend ( parse_error_injection_symbols ( & text) ) ;
126+ }
127+ symbols. sort ( ) ;
128+ symbols. dedup ( ) ;
129+ symbols
130+ }
131+
132+ fn parse_error_injection_symbols ( text : & str ) -> Vec < String > {
133+ text. lines ( )
134+ . filter_map ( |line| {
135+ let token = line. split_whitespace ( ) . next ( ) . unwrap_or_default ( ) . trim ( ) ;
136+ if token. is_empty ( ) || token. starts_with ( '#' ) {
137+ None
138+ } else {
139+ Some ( token. to_string ( ) )
140+ }
141+ } )
142+ . collect ( )
143+ }
144+
100145fn decode_gzip ( bytes : & [ u8 ] ) -> Result < String , std:: io:: Error > {
101146 let mut decoder = GzDecoder :: new ( Cursor :: new ( bytes) ) ;
102147 let mut output = String :: new ( ) ;
@@ -123,6 +168,14 @@ mod tests {
123168 ) ) ;
124169 }
125170
171+ #[ test]
172+ fn parse_error_injection_list_takes_first_column ( ) {
173+ assert_eq ! (
174+ parse_error_injection_symbols( "__x64_sys_reboot\t EI_ETYPE_ERRNO\n # ignored\n " ) ,
175+ vec![ "__x64_sys_reboot" . to_string( ) ]
176+ ) ;
177+ }
178+
126179 #[ test]
127180 fn support_exec_lsm_requires_config_and_active_lsm ( ) {
128181 assert ! ( KernelCapability {
@@ -166,6 +219,31 @@ mod tests {
166219 let _ = fs:: remove_dir_all ( root) ;
167220 }
168221
222+ #[ test]
223+ fn detect_from_roots_uses_error_injection_allowlist_for_kprobe_override ( ) {
224+ let root = make_temp_root ( "kprobe-override-allowlist" ) ;
225+ let proc_root = root. join ( "host-proc" ) ;
226+ let sys_root = root. join ( "host-sys" ) ;
227+ let boot_root = root. join ( "boot" ) ;
228+ fs:: create_dir_all ( proc_root. join ( "sys/kernel" ) ) . unwrap ( ) ;
229+ fs:: create_dir_all ( sys_root. join ( "kernel/debug/error_injection" ) ) . unwrap ( ) ;
230+ fs:: write ( proc_root. join ( "sys/kernel/osrelease" ) , "4.18.0-test\n " ) . unwrap ( ) ;
231+ fs:: write (
232+ sys_root. join ( "kernel/debug/error_injection/list" ) ,
233+ "__x64_sys_reboot\n __x64_sys_init_module\n " ,
234+ )
235+ . unwrap ( ) ;
236+
237+ let capability = KernelCapability :: detect_from_roots ( & proc_root, & sys_root, & boot_root) ;
238+
239+ assert ! ( capability. bpf_kprobe_override_configured) ;
240+ assert ! ( capability. bpf_kprobe_override_available) ;
241+ assert ! ( capability. supports_kprobe_override_symbol( "__x64_sys_reboot" ) ) ;
242+ assert ! ( !capability. supports_kprobe_override_symbol( "__x64_sys_mount" ) ) ;
243+
244+ let _ = fs:: remove_dir_all ( root) ;
245+ }
246+
169247 fn make_temp_root ( name : & str ) -> std:: path:: PathBuf {
170248 let root = std:: env:: temp_dir ( ) . join ( format ! (
171249 "deepflow-kernel-capability-{name}-{}" ,
0 commit comments