Browse Source

programs: rework ProgramError a bit

Move type specific errors to XdpError SocketFilterError etc.

Annotate all source errors with #[source]
Alessandro Decina 4 năm trước cách đây
mục cha
commit
d326038

+ 1 - 1
aya/src/bpf.rs

@@ -164,7 +164,7 @@ pub enum BpfError {
     #[error("error relocating BPF program `{program_name}`: {error}")]
     RelocationError {
         program_name: String,
-        error: Box<dyn Error>,
+        error: Box<dyn Error + Send + Sync>,
     },
 
     #[error("map error: {0}")]

+ 40 - 49
aya/src/programs/mod.rs

@@ -5,9 +5,7 @@ mod trace_point;
 mod xdp;
 
 use libc::{close, ENOSPC};
-use std::{
-    cell::RefCell, cmp, convert::TryFrom, ffi::CStr, io, os::raw::c_uint, path::PathBuf, rc::Rc,
-};
+use std::{cell::RefCell, cmp, convert::TryFrom, ffi::CStr, io, os::raw::c_uint, rc::Rc};
 use thiserror::Error;
 
 use perf_attach::*;
@@ -19,65 +17,63 @@ pub use xdp::*;
 use crate::{obj, sys::bpf_load_program, RawFd};
 #[derive(Debug, Error)]
 pub enum ProgramError {
-    #[error("the program {program} is already loaded")]
-    AlreadyLoaded { program: String },
+    #[error("the program is already loaded")]
+    AlreadyLoaded,
 
-    #[error("the program {program} is not loaded")]
-    NotLoaded { program: String },
-
-    #[error("the BPF_PROG_LOAD syscall for `{program}` failed: {io_error}\nVerifier output:\n{verifier_log}")]
-    LoadError {
-        program: String,
-        io_error: io::Error,
-        verifier_log: String,
-    },
+    #[error("the program is not loaded")]
+    NotLoaded,
 
     #[error("the program was already detached")]
     AlreadyDetached,
 
-    #[error("the perf_event_open syscall failed: {io_error}")]
-    PerfEventOpenError { io_error: io::Error },
-
-    #[error("PERF_EVENT_IOC_SET_BPF/PERF_EVENT_IOC_ENABLE failed: {io_error}")]
-    PerfEventAttachError { io_error: io::Error },
+    #[error("the program is not attached")]
+    NotAttached,
 
-    #[error("the program {program} is not attached")]
-    NotAttached { program: String },
+    #[error("the BPF_PROG_LOAD syscall failed: {io_error}\nVerifier output:\n{verifier_log}")]
+    LoadError {
+        #[source]
+        io_error: io::Error,
+        verifier_log: String,
+    },
 
-    #[error("error attaching {program}: BPF_LINK_CREATE failed with {io_error}")]
-    BpfLinkCreateError {
-        program: String,
+    #[error("the perf_event_open syscall failed")]
+    PerfEventOpenError {
         #[source]
         io_error: io::Error,
     },
 
-    #[error("error attaching XDP program using netlink: {io_error}")]
-    NetlinkXdpError {
-        program: String,
+    #[error("PERF_EVENT_IOC_SET_BPF/PERF_EVENT_IOC_ENABLE failed")]
+    PerfEventAttachError {
         #[source]
         io_error: io::Error,
     },
 
-    #[error("unkown network interface {name}")]
-    UnkownInterface { name: String },
+    #[error("unknown network interface {name}")]
+    UnknownInterface { name: String },
 
-    #[error("error reading ld.so.cache file")]
-    InvalidLdSoCache { error_kind: io::ErrorKind },
+    #[error("BPF_LINK_CREATE failed")]
+    BpfLinkCreateError {
+        #[source]
+        io_error: io::Error,
+    },
 
-    #[error("could not resolve uprobe target {path}")]
-    InvalidUprobeTarget { path: PathBuf },
+    #[error("unexpected program type")]
+    UnexpectedProgramType,
 
-    #[error("error resolving symbol: {error}")]
-    UprobeSymbolError { symbol: String, error: String },
+    #[error(transparent)]
+    KProbeError(#[from] KProbeError),
 
-    #[error("setsockopt SO_ATTACH_BPF failed: {io_error}")]
-    SocketFilterError { io_error: io::Error },
+    #[error(transparent)]
+    UProbeError(#[from] UProbeError),
 
-    #[error("unexpected program type")]
-    UnexpectedProgramType,
+    #[error(transparent)]
+    TracePointError(#[from] TracePointError),
 
-    #[error("{message}")]
-    Other { message: String },
+    #[error(transparent)]
+    SocketFilterError(#[from] SocketFilterError),
+
+    #[error(transparent)]
+    XdpError(#[from] XdpError),
 }
 
 pub trait ProgramFd {
@@ -140,9 +136,7 @@ pub(crate) struct ProgramData {
 
 impl ProgramData {
     fn fd_or_err(&self) -> Result<RawFd, ProgramError> {
-        self.fd.ok_or(ProgramError::NotLoaded {
-            program: self.name.clone(),
-        })
+        self.fd.ok_or(ProgramError::NotLoaded)
     }
 
     pub fn link<T: Link + 'static>(&mut self, link: T) -> LinkRef {
@@ -204,11 +198,9 @@ impl VerifierLog {
 }
 
 fn load_program(prog_type: c_uint, data: &mut ProgramData) -> Result<(), ProgramError> {
-    let ProgramData { obj, fd, name, .. } = data;
+    let ProgramData { obj, fd, .. } = data;
     if fd.is_some() {
-        return Err(ProgramError::AlreadyLoaded {
-            program: name.to_string(),
-        });
+        return Err(ProgramError::AlreadyLoaded);
     }
     let crate::obj::Program {
         instructions,
@@ -244,7 +236,6 @@ fn load_program(prog_type: c_uint, data: &mut ProgramData) -> Result<(), Program
     if let Err((_, io_error)) = ret {
         log_buf.truncate();
         return Err(ProgramError::LoadError {
-            program: name.clone(),
             io_error,
             verifier_log: log_buf.as_c_str().unwrap().to_string_lossy().to_string(),
         });

+ 82 - 33
aya/src/programs/probe.rs

@@ -1,12 +1,14 @@
 use libc::pid_t;
 use object::{Object, ObjectSymbol};
 use std::{
+    error::Error,
     ffi::CStr,
     fs,
     io::{self, BufRead, Cursor, Read},
     mem,
     os::raw::c_char,
     path::{Path, PathBuf},
+    sync::Arc,
 };
 use thiserror::Error;
 
@@ -16,11 +18,50 @@ use crate::{
     sys::perf_event_open_probe,
 };
 
+const LD_SO_CACHE_FILE: &str = "/etc/ld.so.cache";
+
 lazy_static! {
-    static ref LD_SO_CACHE: Result<LdSoCache, io::Error> = LdSoCache::load("/etc/ld.so.cache");
+    static ref LD_SO_CACHE: Result<LdSoCache, Arc<io::Error>> =
+        LdSoCache::load(LD_SO_CACHE_FILE).map_err(Arc::new);
 }
 const LD_SO_CACHE_HEADER: &str = "glibc-ld.so.cache1.1";
 
+#[derive(Debug, Error)]
+pub enum KProbeError {
+    #[error("`{filename}`")]
+    FileError {
+        filename: String,
+        #[source]
+        io_error: io::Error,
+    },
+}
+
+#[derive(Debug, Error)]
+pub enum UProbeError {
+    #[error("error reading `{}` file", LD_SO_CACHE_FILE)]
+    InvalidLdSoCache {
+        #[source]
+        io_error: Arc<io::Error>,
+    },
+
+    #[error("could not resolve uprobe target `{path}`")]
+    InvalidTarget { path: PathBuf },
+
+    #[error("error resolving symbol")]
+    SymbolError {
+        symbol: String,
+        #[source]
+        error: Box<dyn Error + Send + Sync>,
+    },
+
+    #[error("`{filename}`")]
+    FileError {
+        filename: String,
+        #[source]
+        io_error: io::Error,
+    },
+}
+
 #[derive(Debug)]
 pub struct KProbe {
     pub(crate) data: ProgramData,
@@ -70,8 +111,9 @@ impl UProbe {
         let target_str = &*target.as_os_str().to_string_lossy();
 
         let mut path = if let Some(pid) = pid {
-            find_lib_in_proc_maps(pid, &target_str).map_err(|io_error| ProgramError::Other {
-                message: format!("error parsing /proc/{}/maps: {}", pid, io_error),
+            find_lib_in_proc_maps(pid, &target_str).map_err(|io_error| UProbeError::FileError {
+                filename: format!("/proc/{}/maps", pid),
+                io_error,
             })?
         } else {
             None
@@ -84,22 +126,22 @@ impl UProbe {
                 let cache =
                     LD_SO_CACHE
                         .as_ref()
-                        .map_err(|io_error| ProgramError::InvalidLdSoCache {
-                            error_kind: io_error.kind(),
+                        .map_err(|error| UProbeError::InvalidLdSoCache {
+                            io_error: error.clone(),
                         })?;
                 cache.resolve(target_str)
             }
             .map(String::from)
         };
 
-        let path = path.ok_or(ProgramError::InvalidUprobeTarget {
+        let path = path.ok_or(UProbeError::InvalidTarget {
             path: target.to_owned(),
         })?;
 
         let sym_offset = if let Some(fn_name) = fn_name {
-            resolve_symbol(&path, fn_name).map_err(|error| ProgramError::UprobeSymbolError {
+            resolve_symbol(&path, fn_name).map_err(|error| UProbeError::SymbolError {
                 symbol: fn_name.to_string(),
-                error: error.to_string(),
+                error: Box::new(error),
             })?
         } else {
             0
@@ -131,13 +173,22 @@ fn attach(
 ) -> Result<LinkRef, ProgramError> {
     use ProbeKind::*;
 
-    let perf_ty = read_sys_fs_perf_type(match kind {
-        KProbe | KRetProbe => "kprobe",
-        UProbe | URetProbe => "uprobe",
-    })?;
+    let perf_ty = match kind {
+        KProbe | KRetProbe => read_sys_fs_perf_type("kprobe")
+            .map_err(|(filename, io_error)| KProbeError::FileError { filename, io_error })?,
+        UProbe | URetProbe => read_sys_fs_perf_type("uprobe")
+            .map_err(|(filename, io_error)| UProbeError::FileError { filename, io_error })?,
+    };
+
     let ret_bit = match kind {
-        KRetProbe => Some(read_sys_fs_perf_ret_probe("kprobe")?),
-        URetProbe => Some(read_sys_fs_perf_ret_probe("uprobe")?),
+        KRetProbe => Some(
+            read_sys_fs_perf_ret_probe("kprobe")
+                .map_err(|(filename, io_error)| KProbeError::FileError { filename, io_error })?,
+        ),
+        URetProbe => Some(
+            read_sys_fs_perf_ret_probe("uprobe")
+                .map_err(|(filename, io_error)| UProbeError::FileError { filename, io_error })?,
+        ),
         _ => None,
     };
 
@@ -270,13 +321,13 @@ impl LdSoCache {
 
 #[derive(Error, Debug)]
 enum ResolveSymbolError {
-    #[error("io error {0}")]
+    #[error(transparent)]
     Io(#[from] io::Error),
 
-    #[error("error parsing ELF {0}")]
+    #[error("error parsing ELF")]
     Object(#[from] object::Error),
 
-    #[error("unknown symbol {0}")]
+    #[error("unknown symbol `{0}`")]
     Unknown(String),
 }
 
@@ -291,34 +342,32 @@ fn resolve_symbol(path: &str, symbol: &str) -> Result<u64, ResolveSymbolError> {
         .ok_or_else(|| ResolveSymbolError::Unknown(symbol.to_string()))
 }
 
-fn read_sys_fs_perf_type(pmu: &str) -> Result<u32, ProgramError> {
+fn read_sys_fs_perf_type(pmu: &str) -> Result<u32, (String, io::Error)> {
     let file = format!("/sys/bus/event_source/devices/{}/type", pmu);
 
-    let perf_ty = fs::read_to_string(&file).map_err(|e| ProgramError::Other {
-        message: format!("error parsing {}: {}", file, e),
-    })?;
+    let perf_ty = fs::read_to_string(&file).map_err(|e| (file.clone(), e))?;
     let perf_ty = perf_ty
         .trim()
         .parse::<u32>()
-        .map_err(|e| ProgramError::Other {
-            message: format!("error parsing {}: {}", file, e),
-        })?;
+        .map_err(|e| (file, io::Error::new(io::ErrorKind::Other, e)))?;
 
     Ok(perf_ty)
 }
 
-fn read_sys_fs_perf_ret_probe(pmu: &str) -> Result<u32, ProgramError> {
+fn read_sys_fs_perf_ret_probe(pmu: &str) -> Result<u32, (String, io::Error)> {
     let file = format!("/sys/bus/event_source/devices/{}/format/retprobe", pmu);
 
-    let data = fs::read_to_string(&file).map_err(|e| ProgramError::Other {
-        message: format!("error parsing {}: {}", file, e),
-    })?;
+    let data = fs::read_to_string(&file).map_err(|e| (file.clone(), e))?;
 
     let mut parts = data.trim().splitn(2, ":").skip(1);
-    let config = parts.next().ok_or(ProgramError::Other {
-        message: format!("error parsing {}: `{}'", file, data),
+    let config = parts.next().ok_or_else(|| {
+        (
+            file.clone(),
+            io::Error::new(io::ErrorKind::Other, "invalid format"),
+        )
     })?;
-    config.parse::<u32>().map_err(|e| ProgramError::Other {
-        message: format!("error parsing {}: {}", file, e),
-    })
+
+    config
+        .parse::<u32>()
+        .map_err(|e| (file, io::Error::new(io::ErrorKind::Other, e)))
 }

+ 12 - 2
aya/src/programs/socket_filter.rs

@@ -1,11 +1,21 @@
 use libc::{setsockopt, SOL_SOCKET, SO_ATTACH_BPF, SO_DETACH_BPF};
 use std::{io, mem, os::unix::prelude::RawFd};
+use thiserror::Error;
 
 use crate::{
     generated::bpf_prog_type::BPF_PROG_TYPE_SOCKET_FILTER,
     programs::{load_program, Link, LinkRef, ProgramData, ProgramError},
 };
 
+#[derive(Debug, Error)]
+pub enum SocketFilterError {
+    #[error("setsockopt SO_ATTACH_BPF failed")]
+    SoAttachBpfError {
+        #[source]
+        io_error: io::Error,
+    },
+}
+
 #[derive(Debug)]
 pub struct SocketFilter {
     pub(crate) data: ProgramData,
@@ -29,9 +39,9 @@ impl SocketFilter {
             )
         };
         if ret < 0 {
-            return Err(ProgramError::SocketFilterError {
+            return Err(SocketFilterError::SoAttachBpfError {
                 io_error: io::Error::last_os_error(),
-            });
+            })?;
         }
 
         Ok(self.data.link(SocketFilterLink {

+ 23 - 7
aya/src/programs/trace_point.rs

@@ -1,9 +1,20 @@
-use std::fs;
+use std::{fs, io};
+use thiserror::Error;
 
 use crate::{generated::bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT, sys::perf_event_open_trace_point};
 
 use super::{load_program, perf_attach, LinkRef, ProgramData, ProgramError};
 
+#[derive(Debug, Error)]
+pub enum TracePointError {
+    #[error("`{filename}`")]
+    FileError {
+        filename: String,
+        #[source]
+        io_error: io::Error,
+    },
+}
+
 #[derive(Debug)]
 pub struct TracePoint {
     pub(crate) data: ProgramData,
@@ -24,15 +35,20 @@ impl TracePoint {
     }
 }
 
-fn read_sys_fs_trace_point_id(category: &str, name: &str) -> Result<u32, ProgramError> {
+fn read_sys_fs_trace_point_id(category: &str, name: &str) -> Result<u32, TracePointError> {
     let file = format!("/sys/kernel/debug/tracing/events/{}/{}/id", category, name);
 
-    let id = fs::read_to_string(&file).map_err(|e| ProgramError::Other {
-        message: format!("error parsing {}: {}", file, e),
-    })?;
-    let id = id.trim().parse::<u32>().map_err(|e| ProgramError::Other {
-        message: format!("error parsing {}: {}", file, e),
+    let id = fs::read_to_string(&file).map_err(|io_error| TracePointError::FileError {
+        filename: file.clone(),
+        io_error,
     })?;
+    let id = id
+        .trim()
+        .parse::<u32>()
+        .map_err(|error| TracePointError::FileError {
+            filename: file.clone(),
+            io_error: io::Error::new(io::ErrorKind::Other, error),
+        })?;
 
     Ok(id)
 }

+ 19 - 18
aya/src/programs/xdp.rs

@@ -1,16 +1,25 @@
-use std::ffi::CString;
-
 use libc::if_nametoindex;
+use std::{ffi::CString, io};
+use thiserror::Error;
 
-use crate::{generated::XDP_FLAGS_REPLACE, RawFd};
 use crate::{
-    generated::{bpf_attach_type::BPF_XDP, bpf_prog_type::BPF_PROG_TYPE_XDP},
+    generated::{bpf_attach_type::BPF_XDP, bpf_prog_type::BPF_PROG_TYPE_XDP, XDP_FLAGS_REPLACE},
     programs::{load_program, FdLink, Link, LinkRef, ProgramData, ProgramError},
     sys::bpf_link_create,
     sys::kernel_version,
     sys::netlink_set_xdp_fd,
+    RawFd,
 };
 
+#[derive(Debug, Error)]
+pub enum XdpError {
+    #[error("netlink error while attaching XDP program")]
+    NetlinkError {
+        #[source]
+        io_error: io::Error,
+    },
+}
+
 #[derive(Debug)]
 pub struct Xdp {
     pub(crate) data: ProgramData,
@@ -31,30 +40,22 @@ impl Xdp {
         let c_interface = CString::new(interface).unwrap();
         let if_index = unsafe { if_nametoindex(c_interface.as_ptr()) } as RawFd;
         if if_index == 0 {
-            return Err(ProgramError::UnkownInterface {
+            return Err(ProgramError::UnknownInterface {
                 name: interface.to_string(),
             })?;
         }
 
         let k_ver = kernel_version().unwrap();
         if k_ver >= (5, 7, 0) {
-            let link_fd =
-                bpf_link_create(prog_fd, if_index, BPF_XDP, 0).map_err(|(_, io_error)| {
-                    ProgramError::BpfLinkCreateError {
-                        program: self.name(),
-                        io_error,
-                    }
-                })? as RawFd;
+            let link_fd = bpf_link_create(prog_fd, if_index + 42, BPF_XDP, 0)
+                .map_err(|(_, io_error)| ProgramError::BpfLinkCreateError { io_error })?
+                as RawFd;
             Ok(self
                 .data
                 .link(XdpLink::FdLink(FdLink { fd: Some(link_fd) })))
         } else {
-            unsafe { netlink_set_xdp_fd(if_index, prog_fd, None, 0) }.map_err(|io_error| {
-                ProgramError::NetlinkXdpError {
-                    program: self.name(),
-                    io_error,
-                }
-            })?;
+            unsafe { netlink_set_xdp_fd(if_index, prog_fd, None, 0) }
+                .map_err(|io_error| XdpError::NetlinkError { io_error })?;
 
             Ok(self.data.link(XdpLink::NlLink(NlLink {
                 if_index,