Browse Source

Merge pull request #582 from marysaka/feature/no-kern-read-sanitizer

aya: Add sanitize code for kernels without bpf_probe_read_kernel
Alessandro Decina 1 year ago
parent
commit
b5c2928
3 changed files with 114 additions and 33 deletions
  1. 58 0
      aya-obj/src/obj.rs
  2. 25 33
      aya/src/bpf.rs
  3. 31 0
      aya/src/sys/bpf.rs

+ 58 - 0
aya-obj/src/obj.rs

@@ -15,6 +15,8 @@ use object::{
 };
 
 use crate::{
+    btf::BtfFeatures,
+    generated::{BPF_CALL, BPF_JMP, BPF_K},
     maps::{BtfMap, LegacyMap, Map, MINIMUM_MAP_SIZE},
     relocation::*,
     thiserror::{self, Error},
@@ -33,6 +35,16 @@ use crate::btf::{Array, DataSecEntry, FuncSecInfo, LineSecInfo};
 
 const KERNEL_VERSION_ANY: u32 = 0xFFFF_FFFE;
 
+/// Features implements BPF and BTF feature detection
+#[derive(Default, Debug)]
+#[allow(missing_docs)]
+pub struct Features {
+    pub bpf_name: bool,
+    pub bpf_probe_read_kernel: bool,
+    pub bpf_perf_link: bool,
+    pub btf: Option<BtfFeatures>,
+}
+
 /// The loaded object file representation
 #[derive(Clone)]
 pub struct Object {
@@ -878,6 +890,52 @@ impl Object {
 
         Ok(())
     }
+
+    /// Sanitize BPF programs.
+    pub fn sanitize_programs(&mut self, features: &Features) {
+        for program in self.programs.values_mut() {
+            program.sanitize(features);
+        }
+    }
+}
+
+fn insn_is_helper_call(ins: &bpf_insn) -> bool {
+    let klass = (ins.code & 0x07) as u32;
+    let op = (ins.code & 0xF0) as u32;
+    let src = (ins.code & 0x08) as u32;
+
+    klass == BPF_JMP && op == BPF_CALL && src == BPF_K && ins.src_reg() == 0 && ins.dst_reg() == 0
+}
+
+const BPF_FUNC_PROBE_READ: i32 = 4;
+const BPF_FUNC_PROBE_READ_STR: i32 = 45;
+const BPF_FUNC_PROBE_READ_USER: i32 = 112;
+const BPF_FUNC_PROBE_READ_KERNEL: i32 = 113;
+const BPF_FUNC_PROBE_READ_USER_STR: i32 = 114;
+const BPF_FUNC_PROBE_READ_KERNEL_STR: i32 = 115;
+
+impl Program {
+    fn sanitize(&mut self, features: &Features) {
+        for inst in &mut self.function.instructions {
+            if !insn_is_helper_call(inst) {
+                continue;
+            }
+
+            match inst.imm {
+                BPF_FUNC_PROBE_READ_USER | BPF_FUNC_PROBE_READ_KERNEL
+                    if !features.bpf_probe_read_kernel =>
+                {
+                    inst.imm = BPF_FUNC_PROBE_READ;
+                }
+                BPF_FUNC_PROBE_READ_USER_STR | BPF_FUNC_PROBE_READ_KERNEL_STR
+                    if !features.bpf_probe_read_kernel =>
+                {
+                    inst.imm = BPF_FUNC_PROBE_READ_STR;
+                }
+                _ => {}
+            }
+        }
+    }
 }
 
 // Parses multiple map definition contained in a single `maps` section (which is

+ 25 - 33
aya/src/bpf.rs

@@ -11,7 +11,7 @@ use aya_obj::{
     btf::{BtfFeatures, BtfRelocationError},
     generated::BPF_F_XDP_HAS_FRAGS,
     relocation::BpfRelocationError,
-    BpfSectionKind,
+    BpfSectionKind, Features,
 };
 use log::debug;
 use thiserror::Error;
@@ -36,7 +36,7 @@ use crate::{
         bpf_load_btf, bpf_map_freeze, bpf_map_update_elem_ptr, is_btf_datasec_supported,
         is_btf_decl_tag_supported, is_btf_float_supported, is_btf_func_global_supported,
         is_btf_func_supported, is_btf_supported, is_btf_type_tag_supported, is_perf_link_supported,
-        is_prog_name_supported, retry_with_verifier_logs,
+        is_probe_read_kernel_supported, is_prog_name_supported, retry_with_verifier_logs,
     },
     util::{bytes_of, bytes_of_slice, possible_cpus, VerifierLog, POSSIBLE_CPUS},
 };
@@ -66,39 +66,30 @@ unsafe impl<T: Pod, const N: usize> Pod for [T; N] {}
 pub use aya_obj::maps::{bpf_map_def, PinningType};
 
 lazy_static! {
-    pub(crate) static ref FEATURES: Features = Features::new();
+    pub(crate) static ref FEATURES: Features = detect_features();
 }
 
-// Features implements BPF and BTF feature detection
-#[derive(Default, Debug)]
-pub(crate) struct Features {
-    pub bpf_name: bool,
-    pub bpf_perf_link: bool,
-    pub btf: Option<BtfFeatures>,
-}
-
-impl Features {
-    fn new() -> Self {
-        let btf = if is_btf_supported() {
-            Some(BtfFeatures {
-                btf_func: is_btf_func_supported(),
-                btf_func_global: is_btf_func_global_supported(),
-                btf_datasec: is_btf_datasec_supported(),
-                btf_float: is_btf_float_supported(),
-                btf_decl_tag: is_btf_decl_tag_supported(),
-                btf_type_tag: is_btf_type_tag_supported(),
-            })
-        } else {
-            None
-        };
-        let f = Features {
-            bpf_name: is_prog_name_supported(),
-            bpf_perf_link: is_perf_link_supported(),
-            btf,
-        };
-        debug!("BPF Feature Detection: {:#?}", f);
-        f
-    }
+fn detect_features() -> Features {
+    let btf = if is_btf_supported() {
+        Some(BtfFeatures {
+            btf_func: is_btf_func_supported(),
+            btf_func_global: is_btf_func_global_supported(),
+            btf_datasec: is_btf_datasec_supported(),
+            btf_float: is_btf_float_supported(),
+            btf_decl_tag: is_btf_decl_tag_supported(),
+            btf_type_tag: is_btf_type_tag_supported(),
+        })
+    } else {
+        None
+    };
+    let f = Features {
+        bpf_name: is_prog_name_supported(),
+        bpf_probe_read_kernel: is_probe_read_kernel_supported(),
+        bpf_perf_link: is_perf_link_supported(),
+        btf,
+    };
+    debug!("BPF Feature Detection: {:#?}", f);
+    f
 }
 
 /// Builder style API for advanced loading of eBPF programs.
@@ -443,6 +434,7 @@ impl<'a> BpfLoader<'a> {
             &text_sections,
         )?;
         obj.relocate_calls(&text_sections)?;
+        obj.sanitize_programs(&FEATURES);
 
         let programs = obj
             .programs

+ 31 - 0
aya/src/sys/bpf.rs

@@ -599,6 +599,37 @@ pub(crate) fn is_prog_name_supported() -> bool {
     }
 }
 
+pub(crate) fn is_probe_read_kernel_supported() -> bool {
+    let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
+    let u = unsafe { &mut attr.__bindgen_anon_3 };
+
+    let prog: &[u8] = &[
+        0xbf, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // r1 = r10
+        0x07, 0x01, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, // r1 -= 8
+        0xb7, 0x02, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, // r2 = 8
+        0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // r3 = 0
+        0x85, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, // call 113
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit
+    ];
+
+    let gpl = b"GPL\0";
+    u.license = gpl.as_ptr() as u64;
+
+    let insns = copy_instructions(prog).unwrap();
+    u.insn_cnt = insns.len() as u32;
+    u.insns = insns.as_ptr() as u64;
+    u.prog_type = bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT as u32;
+
+    match sys_bpf(bpf_cmd::BPF_PROG_LOAD, &attr) {
+        Ok(v) => {
+            let fd = v as RawFd;
+            unsafe { close(fd) };
+            true
+        }
+        Err(_) => false,
+    }
+}
+
 pub(crate) fn is_perf_link_supported() -> bool {
     let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
     let u = unsafe { &mut attr.__bindgen_anon_3 };