Rework the error hierarchy

Add BpfError::IO and BpfError::BtfError and remove the nested IO and
BtfError variants inside ParseError and RelocationError
pull/1/head
Alessandro Decina 4 years ago
parent 96db24e285
commit 37c3a198c4

@ -2,13 +2,15 @@ use std::{
cell::{Ref, RefCell, RefMut}, cell::{Ref, RefCell, RefMut},
collections::HashMap, collections::HashMap,
convert::TryFrom, convert::TryFrom,
io,
}; };
use thiserror::Error; use thiserror::Error;
use crate::{ use crate::{
maps::{Map, MapError}, maps::{Map, MapError},
obj::{BtfRelocationError, Object, ParseError, RelocationError}, obj::btf::RelocationError as BtfRelocationError,
obj::{btf::BtfError, Object, ParseError, RelocationError},
programs::{KProbe, Program, ProgramData, ProgramError, SocketFilter, TracePoint, UProbe, Xdp}, programs::{KProbe, Program, ProgramData, ProgramError, SocketFilter, TracePoint, UProbe, Xdp},
syscalls::bpf_map_update_elem_ptr, syscalls::bpf_map_update_elem_ptr,
}; };
@ -141,17 +143,24 @@ impl Bpf {
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum BpfError { pub enum BpfError {
#[error("IO error: {0}")]
IO(#[from] io::Error),
#[error("error parsing BPF object: {0}")] #[error("error parsing BPF object: {0}")]
ParseError(#[from] ParseError), ParseError(#[from] ParseError),
#[error("BTF error: {0}")]
BtfError(#[from] BtfError),
#[error("error relocating BPF object: {0}")] #[error("error relocating BPF object: {0}")]
RelocationError(#[from] RelocationError), RelocationError(#[from] RelocationError),
#[error("BTF error: {error}")]
BtfRelocationError { #[error(transparent)]
#[from] BtfRelocationError(#[from] BtfRelocationError),
error: BtfRelocationError,
},
#[error("map error: {0}")] #[error("map error: {0}")]
MapError(#[from] MapError), MapError(#[from] MapError),
#[error("program error: {0}")] #[error("program error: {0}")]
ProgramError(#[from] ProgramError), ProgramError(#[from] ProgramError),
} }

@ -261,17 +261,11 @@ pub enum PerfMapError {
#[error("invalid cpu {cpu_id}")] #[error("invalid cpu {cpu_id}")]
InvalidCpu { cpu_id: u32 }, InvalidCpu { cpu_id: u32 },
#[error("map error: {map_error}")] #[error("map error: {0}")]
MapError { MapError(#[from] MapError),
#[from]
map_error: MapError,
},
#[error("perf buffer error: {buf_error}")] #[error("perf buffer error: {0}")]
PerfBufferError { PerfBufferError(#[from] PerfBufferError),
#[from]
buf_error: PerfBufferError,
},
#[error("bpf_map_update_elem failed: {io_error}")] #[error("bpf_map_update_elem failed: {io_error}")]
UpdateElementFailed { UpdateElementFailed {

@ -19,26 +19,18 @@ use crate::{
}, },
Btf, BtfError, Object, Program, Btf, BtfError, Object, Program,
}, },
BpfError,
}; };
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum RelocationError { pub enum RelocationError {
#[error("{error}")] #[error(transparent)]
BtfError { IOError(#[from] io::Error),
#[from]
error: BtfError,
},
#[error("{error}")]
IOError {
#[from]
error: io::Error,
},
#[error("section `{name}` not found")] #[error("section `{name}` not found")]
SectionNotFound { name: String }, SectionNotFound { name: String },
#[error("invalid BTF relocation access string {access_str}")] #[error("invalid relocation access string {access_str}")]
InvalidAccessString { access_str: String }, InvalidAccessString { access_str: String },
#[error("invalid instruction index #{index} referenced by relocation #{relocation_number} in section `{section_name}`")] #[error("invalid instruction index #{index} referenced by relocation #{relocation_number} in section `{section_name}`")]
@ -161,7 +153,7 @@ impl Relocation {
} }
impl Object { impl Object {
pub fn relocate_btf(&mut self) -> Result<(), RelocationError> { pub fn relocate_btf(&mut self) -> Result<(), BpfError> {
let (local_btf, btf_ext) = match (&self.btf, &self.btf_ext) { let (local_btf, btf_ext) = match (&self.btf, &self.btf_ext) {
(Some(btf), Some(btf_ext)) => (btf, btf_ext), (Some(btf), Some(btf_ext)) => (btf, btf_ext),
_ => return Ok(()), _ => return Ok(()),
@ -203,7 +195,7 @@ impl Object {
num_instructions: instructions.len(), num_instructions: instructions.len(),
section_name: section_name.to_string(), section_name: section_name.to_string(),
relocation_number: rel.number, relocation_number: rel.number,
}); })?;
} }
let local_ty = local_btf.type_by_id(rel.type_id)?; let local_ty = local_btf.type_by_id(rel.type_id)?;
@ -262,7 +254,7 @@ impl Object {
return Err(RelocationError::ConflictingCandidates { return Err(RelocationError::ConflictingCandidates {
type_name: local_name.to_string(), type_name: local_name.to_string(),
candidates: conflicts, candidates: conflicts,
}); })?;
} }
target_comp_rel target_comp_rel
} else { } else {
@ -314,7 +306,7 @@ fn find_candidates<'target>(
fn match_candidate<'target>( fn match_candidate<'target>(
local_spec: &AccessSpec, local_spec: &AccessSpec,
candidate: &'target Candidate, candidate: &'target Candidate,
) -> Result<Option<AccessSpec<'target>>, RelocationError> { ) -> Result<Option<AccessSpec<'target>>, BpfError> {
let mut target_spec = AccessSpec { let mut target_spec = AccessSpec {
btf: candidate.btf, btf: candidate.btf,
root_type_id: candidate.type_id, root_type_id: candidate.type_id,
@ -416,7 +408,7 @@ fn match_candidate<'target>(
if target_spec.parts.len() == MAX_SPEC_LEN { if target_spec.parts.len() == MAX_SPEC_LEN {
return Err(RelocationError::MaximumNestingLevelReached { return Err(RelocationError::MaximumNestingLevelReached {
type_name: Some(candidate.name.clone()), type_name: Some(candidate.name.clone()),
}); })?;
} }
target_spec.parts.push(accessor.index); target_spec.parts.push(accessor.index);
@ -442,7 +434,7 @@ fn match_member<'local, 'target>(
target_btf: &'target Btf, target_btf: &'target Btf,
target_id: u32, target_id: u32,
target_spec: &mut AccessSpec<'target>, target_spec: &mut AccessSpec<'target>,
) -> Result<Option<u32>, RelocationError> { ) -> Result<Option<u32>, BpfError> {
let local_ty = local_btf.type_by_id(local_accessor.type_id)?; let local_ty = local_btf.type_by_id(local_accessor.type_id)?;
let local_member = match local_ty { let local_member = match local_ty {
BtfType::Struct(_, members) | BtfType::Union(_, members) => { BtfType::Struct(_, members) | BtfType::Union(_, members) => {
@ -467,7 +459,7 @@ fn match_member<'local, 'target>(
let root_ty = target_spec.btf.type_by_id(target_spec.root_type_id)?; let root_ty = target_spec.btf.type_by_id(target_spec.root_type_id)?;
return Err(RelocationError::MaximumNestingLevelReached { return Err(RelocationError::MaximumNestingLevelReached {
type_name: target_spec.btf.err_type_name(root_ty), type_name: target_spec.btf.err_type_name(root_ty),
}); })?;
} }
let bit_offset = member_bit_offset(target_ty.info().unwrap(), target_member); let bit_offset = member_bit_offset(target_ty.info().unwrap(), target_member);
@ -527,7 +519,7 @@ impl<'a> AccessSpec<'a> {
root_type_id: u32, root_type_id: u32,
spec: &str, spec: &str,
relocation: Relocation, relocation: Relocation,
) -> Result<AccessSpec<'a>, RelocationError> { ) -> Result<AccessSpec<'a>, BpfError> {
let parts = spec let parts = spec
.split(":") .split(":")
.map(|s| s.parse::<usize>()) .map(|s| s.parse::<usize>())
@ -547,7 +539,7 @@ impl<'a> AccessSpec<'a> {
if parts != [0] { if parts != [0] {
return Err(RelocationError::InvalidAccessString { return Err(RelocationError::InvalidAccessString {
access_str: spec.to_string(), access_str: spec.to_string(),
}); })?;
} }
AccessSpec { AccessSpec {
btf, btf,
@ -563,7 +555,7 @@ impl<'a> AccessSpec<'a> {
if parts.len() != 1 { if parts.len() != 1 {
return Err(RelocationError::InvalidAccessString { return Err(RelocationError::InvalidAccessString {
access_str: spec.to_string(), access_str: spec.to_string(),
}); })?;
} }
let index = parts[0]; let index = parts[0];
if index >= members.len() { if index >= members.len() {
@ -573,7 +565,7 @@ impl<'a> AccessSpec<'a> {
index: index, index: index,
max_index: members.len(), max_index: members.len(),
error: "tried to access nonexistant enum variant".to_string(), error: "tried to access nonexistant enum variant".to_string(),
}); })?;
} }
let accessors = vec![Accessor { let accessors = vec![Accessor {
type_id, type_id,
@ -596,7 +588,7 @@ impl<'a> AccessSpec<'a> {
relocation_kind: format!("{:?}", relocation.kind), relocation_kind: format!("{:?}", relocation.kind),
type_kind: format!("{:?}", ty.kind()?.unwrap()), type_kind: format!("{:?}", ty.kind()?.unwrap()),
error: "enum relocation on non-enum type".to_string(), error: "enum relocation on non-enum type".to_string(),
}) })?
} }
}, },
@ -626,7 +618,7 @@ impl<'a> AccessSpec<'a> {
index: index, index: index,
max_index: members.len(), max_index: members.len(),
error: "out of bounds struct or union access".to_string(), error: "out of bounds struct or union access".to_string(),
}); })?;
} }
let member = members[index]; let member = members[index];
@ -662,7 +654,7 @@ impl<'a> AccessSpec<'a> {
index, index,
max_index: array.nelems as usize, max_index: array.nelems as usize,
error: "array index out of bounds".to_string(), error: "array index out of bounds".to_string(),
}); })?;
} }
accessors.push(Accessor { accessors.push(Accessor {
type_id, type_id,
@ -679,7 +671,7 @@ impl<'a> AccessSpec<'a> {
type_kind: format!("{:?}", ty.kind()), type_kind: format!("{:?}", ty.kind()),
error: "field relocation on a type that doesn't have fields" error: "field relocation on a type that doesn't have fields"
.to_string(), .to_string(),
}); })?;
} }
}; };
} }
@ -732,7 +724,7 @@ impl ComputedRelocation {
rel: &Relocation, rel: &Relocation,
local_spec: &AccessSpec, local_spec: &AccessSpec,
target_spec: Option<&AccessSpec>, target_spec: Option<&AccessSpec>,
) -> Result<ComputedRelocation, RelocationError> { ) -> Result<ComputedRelocation, BpfError> {
use RelocationKind::*; use RelocationKind::*;
let ret = match rel.kind { let ret = match rel.kind {
FieldByteOffset | FieldByteSize | FieldExists | FieldSigned | FieldLShift64 FieldByteOffset | FieldByteSize | FieldExists | FieldSigned | FieldLShift64
@ -760,7 +752,7 @@ impl ComputedRelocation {
section_name: &str, section_name: &str,
local_btf: &Btf, local_btf: &Btf,
target_btf: &Btf, target_btf: &Btf,
) -> Result<(), RelocationError> { ) -> Result<(), BpfError> {
let instructions = &mut program.instructions; let instructions = &mut program.instructions;
let num_instructions = instructions.len(); let num_instructions = instructions.len();
let ins_index = rel.ins_offset as usize / std::mem::size_of::<bpf_insn>(); let ins_index = rel.ins_offset as usize / std::mem::size_of::<bpf_insn>();
@ -786,7 +778,7 @@ impl ComputedRelocation {
relocation_number: rel.number, relocation_number: rel.number,
index: ins_index, index: ins_index,
error: format!("invalid src_reg={:x} expected {:x}", src_reg, BPF_K), error: format!("invalid src_reg={:x} expected {:x}", src_reg, BPF_K),
}); })?;
} }
ins.imm = target_value as i32; ins.imm = target_value as i32;
@ -797,7 +789,7 @@ impl ComputedRelocation {
relocation_number: rel.number, relocation_number: rel.number,
index: ins_index, index: ins_index,
error: format!("value `{}` overflows 16 bits offset field", target_value), error: format!("value `{}` overflows 16 bits offset field", target_value),
}); })?;
} }
ins.off = target_value as i16; ins.off = target_value as i16;
@ -822,7 +814,7 @@ impl ComputedRelocation {
err_type_name(&target_btf.err_type_name(target_ty)), err_type_name(&target_btf.err_type_name(target_ty)),
self.target.size, self.target.size,
), ),
}) })?
} }
} }
@ -836,7 +828,7 @@ impl ComputedRelocation {
relocation_number: rel.number, relocation_number: rel.number,
index: ins_index, index: ins_index,
error: format!("invalid target size {}", size), error: format!("invalid target size {}", size),
}) })?
} }
} as u8; } as u8;
ins.code = ins.code & 0xE0 | size | ins.code & 0x07; ins.code = ins.code & 0xE0 | size | ins.code & 0x07;
@ -860,7 +852,7 @@ impl ComputedRelocation {
relocation_number: rel.number, relocation_number: rel.number,
index: ins_index, index: ins_index,
error: format!("invalid instruction class {:x}", class), error: format!("invalid instruction class {:x}", class),
}) })?
} }
}; };
@ -870,7 +862,7 @@ impl ComputedRelocation {
fn compute_enum_relocation( fn compute_enum_relocation(
rel: &Relocation, rel: &Relocation,
spec: Option<&AccessSpec>, spec: Option<&AccessSpec>,
) -> Result<ComputedRelocationValue, RelocationError> { ) -> Result<ComputedRelocationValue, BpfError> {
use RelocationKind::*; use RelocationKind::*;
let value = match rel.kind { let value = match rel.kind {
EnumVariantExists => spec.is_some() as u32, EnumVariantExists => spec.is_some() as u32,
@ -896,7 +888,7 @@ impl ComputedRelocation {
fn compute_field_relocation( fn compute_field_relocation(
rel: &Relocation, rel: &Relocation,
spec: Option<&AccessSpec>, spec: Option<&AccessSpec>,
) -> Result<ComputedRelocationValue, RelocationError> { ) -> Result<ComputedRelocationValue, BpfError> {
use RelocationKind::*; use RelocationKind::*;
if let FieldExists = rel.kind { if let FieldExists = rel.kind {
@ -931,7 +923,7 @@ impl ComputedRelocation {
relocation_kind: format!("{:?}", rel_kind), relocation_kind: format!("{:?}", rel_kind),
type_kind: format!("{:?}", ty.kind()), type_kind: format!("{:?}", ty.kind()),
error: "invalid relocation kind for array type".to_string(), error: "invalid relocation kind for array type".to_string(),
}); })?;
} }
}; };
} }
@ -947,7 +939,7 @@ impl ComputedRelocation {
relocation_kind: format!("{:?}", rel.kind), relocation_kind: format!("{:?}", rel.kind),
type_kind: format!("{:?}", ty.kind()), type_kind: format!("{:?}", ty.kind()),
error: "field relocation on a type that doesn't have fields".to_string(), error: "field relocation on a type that doesn't have fields".to_string(),
}); })?;
} }
}; };
@ -1021,7 +1013,7 @@ impl ComputedRelocation {
rel: &Relocation, rel: &Relocation,
local_spec: &AccessSpec, local_spec: &AccessSpec,
target_spec: Option<&AccessSpec>, target_spec: Option<&AccessSpec>,
) -> Result<ComputedRelocationValue, RelocationError> { ) -> Result<ComputedRelocationValue, BpfError> {
use RelocationKind::*; use RelocationKind::*;
let value = match rel.kind { let value = match rel.kind {
TypeIdLocal => local_spec.root_type_id, TypeIdLocal => local_spec.root_type_id,

@ -1,4 +1,4 @@
mod btf; pub(crate) mod btf;
mod relocation; mod relocation;
use object::{ use object::{
@ -14,13 +14,13 @@ use std::{
}; };
use thiserror::Error; use thiserror::Error;
pub use btf::RelocationError as BtfRelocationError;
pub use relocation::*; pub use relocation::*;
use crate::{ use crate::{
bpf_map_def, bpf_map_def,
generated::{bpf_insn, bpf_map_type::BPF_MAP_TYPE_ARRAY}, generated::{bpf_insn, bpf_map_type::BPF_MAP_TYPE_ARRAY},
obj::btf::{Btf, BtfError, BtfExt}, obj::btf::{Btf, BtfError, BtfExt},
BpfError,
}; };
const KERNEL_VERSION_ANY: u32 = 0xFFFF_FFFE; const KERNEL_VERSION_ANY: u32 = 0xFFFF_FFFE;
@ -85,8 +85,8 @@ impl FromStr for ProgramKind {
} }
impl Object { impl Object {
pub(crate) fn parse(data: &[u8]) -> Result<Object, ParseError> { pub(crate) fn parse(data: &[u8]) -> Result<Object, BpfError> {
let obj = object::read::File::parse(data).map_err(|source| ParseError::Error { source })?; let obj = object::read::File::parse(data).map_err(|e| ParseError::ElfError(e))?;
let endianness = obj.endianness(); let endianness = obj.endianness();
let section = obj let section = obj
@ -138,9 +138,7 @@ impl Object {
fn parse_program(&self, section: &Section, ty: &str) -> Result<Program, ParseError> { fn parse_program(&self, section: &Section, ty: &str) -> Result<Program, ParseError> {
let num_instructions = section.data.len() / mem::size_of::<bpf_insn>(); let num_instructions = section.data.len() / mem::size_of::<bpf_insn>();
if section.data.len() % mem::size_of::<bpf_insn>() > 0 { if section.data.len() % mem::size_of::<bpf_insn>() > 0 {
return Err(ParseError::InvalidProgramCode { return Err(ParseError::InvalidProgramCode);
name: section.name.to_owned(),
});
} }
let instructions = (0..num_instructions) let instructions = (0..num_instructions)
.map(|i| unsafe { .map(|i| unsafe {
@ -171,7 +169,7 @@ impl Object {
Ok(()) Ok(())
} }
fn parse_section(&mut self, section: Section) -> Result<(), ParseError> { fn parse_section(&mut self, section: Section) -> Result<(), BpfError> {
let parts = section.name.split("/").collect::<Vec<_>>(); let parts = section.name.split("/").collect::<Vec<_>>();
match parts.as_slice() { match parts.as_slice() {
@ -209,13 +207,7 @@ impl Object {
#[derive(Debug, Clone, Error)] #[derive(Debug, Clone, Error)]
pub enum ParseError { pub enum ParseError {
#[error("error parsing ELF data")] #[error("error parsing ELF data")]
Error { ElfError(#[from] object::read::Error),
#[source]
source: object::read::Error,
},
#[error("Error parsing BTF: {0}")]
BTF(#[from] BtfError),
#[error("no license specified")] #[error("no license specified")]
MissingLicense, MissingLicense,
@ -245,8 +237,8 @@ pub enum ParseError {
#[error("invalid program kind `{kind}`")] #[error("invalid program kind `{kind}`")]
InvalidProgramKind { kind: String }, InvalidProgramKind { kind: String },
#[error("error parsing program `{name}`")] #[error("invalid program code")]
InvalidProgramCode { name: String }, InvalidProgramCode,
#[error("error parsing map `{name}`")] #[error("error parsing map `{name}`")]
InvalidMapDefinition { name: String }, InvalidMapDefinition { name: String },
@ -410,7 +402,7 @@ mod tests {
fn test_parse_generic_error() { fn test_parse_generic_error() {
assert!(matches!( assert!(matches!(
Object::parse(&b"foo"[..]), Object::parse(&b"foo"[..]),
Err(ParseError::Error { .. }) Err(BpfError::ParseError(ParseError::ElfError(_)))
)) ))
} }
@ -570,14 +562,8 @@ mod tests {
let obj = fake_obj(); let obj = fake_obj();
assert_matches!( assert_matches!(
obj.parse_program( obj.parse_program(&fake_section("kprobe/foo", &42u32.to_ne_bytes(),), "kprobe"),
&fake_section( Err(ParseError::InvalidProgramCode)
"kprobe/foo",
&42u32.to_ne_bytes(),
),
"kprobe"
),
Err(ParseError::InvalidProgramCode { name }) if name == "kprobe/foo"
); );
} }

@ -34,12 +34,6 @@ pub enum RelocationError {
num_instructions: usize, num_instructions: usize,
relocation_number: usize, relocation_number: usize,
}, },
#[error("IO error: {io_error}")]
IO {
#[from]
io_error: io::Error,
},
} }
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]

Loading…
Cancel
Save