diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index f7089596..bd70f219 100644 --- a/aya/src/bpf.rs +++ b/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, + error: Box, }, #[error("map error: {0}")] diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs index e4a63990..b78eebfb 100644 --- a/aya/src/programs/mod.rs +++ b/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 { - self.fd.ok_or(ProgramError::NotLoaded { - program: self.name.clone(), - }) + self.fd.ok_or(ProgramError::NotLoaded) } pub fn link(&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(), }); diff --git a/aya/src/programs/probe.rs b/aya/src/programs/probe.rs index 25bb9f29..5fbcd03b 100644 --- a/aya/src/programs/probe.rs +++ b/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::load("/etc/ld.so.cache"); + static ref LD_SO_CACHE: Result> = + 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, + }, + + #[error("could not resolve uprobe target `{path}`")] + InvalidTarget { path: PathBuf }, + + #[error("error resolving symbol")] + SymbolError { + symbol: String, + #[source] + error: Box, + }, + + #[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 { 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 { .ok_or_else(|| ResolveSymbolError::Unknown(symbol.to_string())) } -fn read_sys_fs_perf_type(pmu: &str) -> Result { +fn read_sys_fs_perf_type(pmu: &str) -> Result { 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::() - .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 { +fn read_sys_fs_perf_ret_probe(pmu: &str) -> Result { 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::().map_err(|e| ProgramError::Other { - message: format!("error parsing {}: {}", file, e), - }) + + config + .parse::() + .map_err(|e| (file, io::Error::new(io::ErrorKind::Other, e))) } diff --git a/aya/src/programs/socket_filter.rs b/aya/src/programs/socket_filter.rs index c9e1e151..a3786f09 100644 --- a/aya/src/programs/socket_filter.rs +++ b/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 { diff --git a/aya/src/programs/trace_point.rs b/aya/src/programs/trace_point.rs index 0f1d972e..be8d7d37 100644 --- a/aya/src/programs/trace_point.rs +++ b/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 { +fn read_sys_fs_trace_point_id(category: &str, name: &str) -> Result { 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::().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::() + .map_err(|error| TracePointError::FileError { + filename: file.clone(), + io_error: io::Error::new(io::ErrorKind::Other, error), + })?; Ok(id) } diff --git a/aya/src/programs/xdp.rs b/aya/src/programs/xdp.rs index a125adeb..97bfc7eb 100644 --- a/aya/src/programs/xdp.rs +++ b/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,