diff --git a/.vscode/settings.json b/.vscode/settings.json index b383a959..fe99dc28 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { "rust-analyzer.check.allTargets": true, "rust-analyzer.check.command": "clippy", + "rust-analyzer.cargo.target": "x86_64-unknown-linux-musl", "search.exclude": { "/xtask/public-api/*.txt": true }, diff --git a/aya-log-common/src/lib.rs b/aya-log-common/src/lib.rs index 37591f7e..449c891b 100644 --- a/aya-log-common/src/lib.rs +++ b/aya-log-common/src/lib.rs @@ -97,6 +97,10 @@ impl LowerMacFormatter for [u8; 6] {} pub trait UpperMacFormatter {} impl UpperMacFormatter for [u8; 6] {} +pub trait PointerFormatter {} +impl PointerFormatter for *const T {} +impl PointerFormatter for *mut T {} + #[repr(u8)] #[derive(Copy, Clone, Debug, IntoPrimitive)] pub enum RecordFieldKind { @@ -144,6 +148,8 @@ pub enum ArgumentKind { Bytes, Str, + + Pointer, } /// All display hints @@ -162,6 +168,8 @@ pub enum DisplayHint { LowerMac, /// `:MAC` UpperMac, + /// `:p` + Pointer, } mod sealed { @@ -282,6 +290,20 @@ impl Argument for DisplayHint { } } +impl sealed::Sealed for *const T {} +impl Argument for *const T { + fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) { + (ArgumentKind::Pointer, (*self as usize).to_ne_bytes()) + } +} + +impl sealed::Sealed for *mut T {} +impl Argument for *mut T { + fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) { + (ArgumentKind::Pointer, (*self as usize).to_ne_bytes()) + } +} + fn wire_len(value: &[u8]) -> Option<[u8; 2]> { match LogValueLength::try_from(value.len()) { Ok(wire_len) => Some(wire_len.to_ne_bytes()), diff --git a/aya-log-ebpf-macros/src/expand.rs b/aya-log-ebpf-macros/src/expand.rs index ab8dfc9e..24345092 100644 --- a/aya-log-ebpf-macros/src/expand.rs +++ b/aya-log-ebpf-macros/src/expand.rs @@ -127,6 +127,9 @@ pub(crate) fn log(args: LogArgs, level_expr: Option) -> Result { (quote!(DisplayHint::UpperMac), quote!(UpperMacFormatter)) } + DisplayHint::Pointer => { + (quote!(DisplayHint::Pointer), quote!(PointerFormatter)) + } }; let hint = quote!(::aya_log_ebpf::macro_support::#hint); let arg = quote!( diff --git a/aya-log-parser/src/lib.rs b/aya-log-parser/src/lib.rs index d01a17ed..8f3e9eb9 100644 --- a/aya-log-parser/src/lib.rs +++ b/aya-log-parser/src/lib.rs @@ -59,11 +59,12 @@ fn parse_param(input: &str) -> Result { let hint = match input.strip_prefix(":") { Some(input) => match input { "" => return Err("malformed format string (missing display hint after ':')".into()), - "p" | "x" => DisplayHint::LowerHex, + "x" => DisplayHint::LowerHex, "X" => DisplayHint::UpperHex, "i" => DisplayHint::Ip, "mac" => DisplayHint::LowerMac, "MAC" => DisplayHint::UpperMac, + "p" => DisplayHint::Pointer, input => return Err(format!("unknown display hint: {input:?}")), }, None => { @@ -155,7 +156,7 @@ mod test { }), Fragment::Literal(" lmao {} {something} ".into()), Fragment::Parameter(Parameter { - hint: DisplayHint::LowerHex + hint: DisplayHint::Pointer }), ]) ); diff --git a/aya-log/src/lib.rs b/aya-log/src/lib.rs index 04bb79b1..9c7c6492 100644 --- a/aya-log/src/lib.rs +++ b/aya-log/src/lib.rs @@ -284,6 +284,19 @@ impl Formatter<[u8; 6]> for UpperMacFormatter { } } +pub struct PointerFormatter; +impl Formatter<*const T> for PointerFormatter { + fn format(v: *const T) -> String { + format!("{v:p}") + } +} + +impl Formatter<*mut T> for PointerFormatter { + fn format(v: *mut T) -> String { + format!("{v:p}") + } +} + trait Format { fn format(&self, last_hint: Option) -> Result; } @@ -307,6 +320,7 @@ impl Format for u32 { Some(DisplayHint::Ip) => Ok(Ipv4Formatter::format(*self)), Some(DisplayHint::LowerMac) => Err(()), Some(DisplayHint::UpperMac) => Err(()), + Some(DisplayHint::Pointer) => Err(()), None => Ok(DefaultFormatter::format(self)), } } @@ -321,6 +335,7 @@ impl Format for Ipv4Addr { Some(DisplayHint::Ip) => Ok(Ipv4Formatter::format(*self)), Some(DisplayHint::LowerMac) => Err(()), Some(DisplayHint::UpperMac) => Err(()), + Some(DisplayHint::Pointer) => Err(()), None => Ok(Ipv4Formatter::format(*self)), } } @@ -335,6 +350,7 @@ impl Format for Ipv6Addr { Some(DisplayHint::Ip) => Ok(Ipv6Formatter::format(*self)), Some(DisplayHint::LowerMac) => Err(()), Some(DisplayHint::UpperMac) => Err(()), + Some(DisplayHint::Pointer) => Err(()), None => Ok(Ipv6Formatter::format(*self)), } } @@ -349,6 +365,7 @@ impl Format for [u8; 4] { Some(DisplayHint::Ip) => Ok(Ipv4Formatter::format(*self)), Some(DisplayHint::LowerMac) => Err(()), Some(DisplayHint::UpperMac) => Err(()), + Some(DisplayHint::Pointer) => Err(()), None => Ok(Ipv4Formatter::format(*self)), } } @@ -363,6 +380,7 @@ impl Format for [u8; 6] { Some(DisplayHint::Ip) => Err(()), Some(DisplayHint::LowerMac) => Ok(LowerMacFormatter::format(*self)), Some(DisplayHint::UpperMac) => Ok(UpperMacFormatter::format(*self)), + Some(DisplayHint::Pointer) => Err(()), None => Err(()), } } @@ -377,6 +395,7 @@ impl Format for [u8; 16] { Some(DisplayHint::Ip) => Ok(Ipv6Formatter::format(*self)), Some(DisplayHint::LowerMac) => Err(()), Some(DisplayHint::UpperMac) => Err(()), + Some(DisplayHint::Pointer) => Err(()), None => Err(()), } } @@ -391,6 +410,7 @@ impl Format for [u16; 8] { Some(DisplayHint::Ip) => Ok(Ipv6Formatter::format(*self)), Some(DisplayHint::LowerMac) => Err(()), Some(DisplayHint::UpperMac) => Err(()), + Some(DisplayHint::Pointer) => Err(()), None => Err(()), } } @@ -407,6 +427,7 @@ macro_rules! impl_format { Some(DisplayHint::Ip) => Err(()), Some(DisplayHint::LowerMac) => Err(()), Some(DisplayHint::UpperMac) => Err(()), + Some(DisplayHint::Pointer) => Err(()), None => Ok(DefaultFormatter::format(self)), } } @@ -436,6 +457,7 @@ macro_rules! impl_format_float { Some(DisplayHint::Ip) => Err(()), Some(DisplayHint::LowerMac) => Err(()), Some(DisplayHint::UpperMac) => Err(()), + Some(DisplayHint::Pointer) => Err(()), None => Ok(DefaultFormatter::format(self)), } } @@ -446,6 +468,24 @@ macro_rules! impl_format_float { impl_format_float!(f32); impl_format_float!(f64); +impl Format for *const T { + fn format(&self, last_hint: Option) -> Result { + match last_hint.map(|DisplayHintWrapper(dh)| dh) { + Some(DisplayHint::Pointer) => Ok(PointerFormatter::format(*self)), + _ => Err(()), + } + } +} + +impl Format for *mut T { + fn format(&self, last_hint: Option) -> Result { + match last_hint.map(|DisplayHintWrapper(dh)| dh) { + Some(DisplayHint::Pointer) => Ok(PointerFormatter::format(*self)), + _ => Err(()), + } + } +} + #[derive(Error, Debug)] pub enum Error { #[error("{} not found", MAP_NAME)] @@ -732,6 +772,13 @@ fn log_buf(mut buf: &[u8], logger: &T) -> Result<(), ()> { } Err(e) => error!("received invalid utf8 string: {e}"), }, + ArgumentKind::Pointer => { + let value = value + .try_into() + .map_err(|std::array::TryFromSliceError { .. }| ())?; + let ptr = usize::from_ne_bytes(value) as *const (); + full_log_msg.push_str(&ptr.format(last_hint.take())?); + } } buf = rest; diff --git a/aya-obj/src/btf/btf.rs b/aya-obj/src/btf/btf.rs index 4a580ffc..a94cac95 100644 --- a/aya-obj/src/btf/btf.rs +++ b/aya-obj/src/btf/btf.rs @@ -99,21 +99,21 @@ pub enum BtfError { }, /// unknown BTF type id - #[error("Unknown BTF type id `{type_id}`")] + #[error("unknown BTF type id `{type_id}`")] UnknownBtfType { /// type id type_id: u32, }, /// unexpected btf type id - #[error("Unexpected BTF type id `{type_id}`")] + #[error("unexpected BTF type id `{type_id}`")] UnexpectedBtfType { /// type id type_id: u32, }, /// unknown BTF type - #[error("Unknown BTF type `{type_name}`")] + #[error("unknown BTF type `{type_name}`")] UnknownBtfTypeName { /// type name type_name: String, @@ -128,7 +128,7 @@ pub enum BtfError { #[cfg(feature = "std")] /// Loading the btf failed - #[error("the BPF_BTF_LOAD syscall failed. Verifier output: {verifier_log}")] + #[error("the BPF_BTF_LOAD syscall returned {io_error}. Verifier output: {verifier_log}")] LoadError { /// The [`std::io::Error`] returned by the `BPF_BTF_LOAD` syscall. #[source] diff --git a/aya/src/programs/flow_dissector.rs b/aya/src/programs/flow_dissector.rs index e0b3685b..64d657bd 100644 --- a/aya/src/programs/flow_dissector.rs +++ b/aya/src/programs/flow_dissector.rs @@ -9,7 +9,7 @@ use aya_obj::generated::{ use crate::{ programs::{ CgroupAttachMode, FdLink, Link, ProgAttachLink, ProgramData, ProgramError, ProgramType, - define_link_wrapper, id_as_key, load_program, + define_link_wrapper, id_as_key, impl_try_into_fdlink, load_program, }, sys::{LinkTarget, SyscallError, bpf_link_create}, util::KernelVersion, @@ -155,3 +155,5 @@ define_link_wrapper!( FlowDissectorLinkIdInner, FlowDissector, ); + +impl_try_into_fdlink!(FlowDissectorLink, FlowDissectorLinkInner); diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs index 9b3f530f..ddcc0f25 100644 --- a/aya/src/programs/mod.rs +++ b/aya/src/programs/mod.rs @@ -158,7 +158,7 @@ pub enum ProgramError { NotAttached, /// Loading the program failed. - #[error("the BPF_PROG_LOAD syscall failed. Verifier output: {verifier_log}")] + #[error("the BPF_PROG_LOAD syscall returned {io_error}. Verifier output: {verifier_log}")] LoadError { /// The [`io::Error`] returned by the `BPF_PROG_LOAD` syscall. #[source] diff --git a/aya/src/sys/netlink.rs b/aya/src/sys/netlink.rs index cec74523..9788fe8b 100644 --- a/aya/src/sys/netlink.rs +++ b/aya/src/sys/netlink.rs @@ -40,8 +40,6 @@ pub(crate) enum NetlinkErrorInternal { #[error(transparent)] IoError(#[from] io::Error), #[error(transparent)] - NulError(#[from] std::ffi::NulError), - #[error(transparent)] NlAttrError(#[from] NlAttrError), } @@ -699,12 +697,6 @@ pub(crate) enum NlAttrError { InvalidHeaderLength(usize), } -impl From for io::Error { - fn from(err: NlAttrError) -> Self { - Self::other(err) - } -} - unsafe fn request_attributes(req: &mut T, msg_len: usize) -> &mut [u8] { let req: *mut _ = req; let req: *mut u8 = req.cast(); diff --git a/clippy.sh b/clippy.sh index a710c7c1..6d454a9e 100755 --- a/clippy.sh +++ b/clippy.sh @@ -2,15 +2,6 @@ set -eux -cargo +nightly hack clippy \ - --target bpfel-unknown-none -Zbuild-std=core \ - --package aya-ebpf-bindings \ - --package aya-ebpf \ - --package aya-log-ebpf \ - --package integration-ebpf \ - --feature-powerset \ - -- --deny warnings - # `-C panic=abort` because "unwinding panics are not supported without std"; integration-ebpf # contains `#[no_std]` binaries. # @@ -26,3 +17,12 @@ cargo +nightly hack clippy "$@" \ -- --deny warnings \ -C panic=abort \ -Zpanic_abort_tests + +cargo +nightly hack clippy \ + --target bpfel-unknown-none -Zbuild-std=core \ + --package aya-ebpf-bindings \ + --package aya-ebpf \ + --package aya-log-ebpf \ + --package integration-ebpf \ + --feature-powerset \ + -- --deny warnings diff --git a/ebpf/aya-log-ebpf/src/lib.rs b/ebpf/aya-log-ebpf/src/lib.rs index 7238c006..6be37975 100644 --- a/ebpf/aya-log-ebpf/src/lib.rs +++ b/ebpf/aya-log-ebpf/src/lib.rs @@ -14,7 +14,8 @@ pub mod macro_support { use aya_ebpf::maps::RingBuf; pub use aya_log_common::{ Argument, DefaultFormatter, DisplayHint, Field, Header, IpFormatter, Level, LogValueLength, - LowerHexFormatter, LowerMacFormatter, UpperHexFormatter, UpperMacFormatter, + LowerHexFormatter, LowerMacFormatter, PointerFormatter, UpperHexFormatter, + UpperMacFormatter, }; // This cfg_attr prevents compilation failures on macOS where the generated section name doesn't diff --git a/test/integration-ebpf/src/log.rs b/test/integration-ebpf/src/log.rs index 2abb5566..3e7bf5b8 100644 --- a/test/integration-ebpf/src/log.rs +++ b/test/integration-ebpf/src/log.rs @@ -89,6 +89,8 @@ fn test_log(ctx: ProbeContext) { &ctx, "{} {} {} {} {} {} {}", header, tmp, kind, value, size, op, buf ); + let ptr = 0xdeadbeef as *const u8; + debug!(&ctx, "ptr: {:p}", ptr); // Testing compilation only. if false { diff --git a/test/integration-test/src/tests/load.rs b/test/integration-test/src/tests/load.rs index 024b612d..ab784692 100644 --- a/test/integration-test/src/tests/load.rs +++ b/test/integration-test/src/tests/load.rs @@ -3,10 +3,16 @@ use std::{convert::TryInto as _, fs::remove_file, path::Path, thread, time::Dura use aya::{ Ebpf, maps::Array, + pin::PinError, programs::{ - FlowDissector, KProbe, TracePoint, UProbe, Xdp, XdpFlags, - links::{FdLink, PinnedLink}, + FlowDissector, KProbe, ProbeKind, Program, ProgramError, TracePoint, UProbe, Xdp, XdpFlags, + flow_dissector::{FlowDissectorLink, FlowDissectorLinkId}, + kprobe::{KProbeLink, KProbeLinkId}, + links::{FdLink, LinkError, PinnedLink}, loaded_links, loaded_programs, + trace_point::{TracePointLink, TracePointLinkId}, + uprobe::{UProbeLink, UProbeLinkId}, + xdp::{XdpLink, XdpLinkId}, }, util::KernelVersion, }; @@ -31,6 +37,13 @@ fn long_name() { // Therefore, as long as we were able to load the program, this is good enough. } +#[test_log::test] +fn memmove() { + let mut bpf = Ebpf::load(crate::MEMMOVE_TEST).unwrap(); + let prog: &mut Xdp = bpf.program_mut("do_dnat").unwrap().try_into().unwrap(); + prog.load().unwrap(); +} + #[test_log::test] fn multiple_btf_maps() { let mut bpf = Ebpf::load(crate::MULTIMAP_BTF).unwrap(); @@ -190,156 +203,128 @@ fn assert_unloaded(name: &str) { ) } -#[test_log::test] -fn unload_xdp() { - let mut bpf = Ebpf::load(crate::TEST).unwrap(); - let prog: &mut Xdp = bpf.program_mut("pass").unwrap().try_into().unwrap(); - prog.load().unwrap(); - assert_loaded("pass"); - let link = prog.attach("lo", XdpFlags::default()).unwrap(); - { - let _link_owned = prog.take_link(link).unwrap(); - prog.unload().unwrap(); - assert_loaded_and_linked("pass"); - }; +trait UnloadProgramOps { + type LinkId; + type OwnedLink; - assert_unloaded("pass"); - prog.load().unwrap(); + fn load(&mut self) -> Result<(), ProgramError>; + fn unload(&mut self) -> Result<(), ProgramError>; + fn take_link(&mut self, id: Self::LinkId) -> Result; +} - assert_loaded("pass"); - prog.attach("lo", XdpFlags::default()).unwrap(); +macro_rules! impl_unload_program_ops { + ($program:ty, $link_id:ty, $link:ty) => { + impl UnloadProgramOps for $program { + type LinkId = $link_id; + type OwnedLink = $link; - assert_loaded("pass"); - prog.unload().unwrap(); + fn load(&mut self) -> Result<(), ProgramError> { + <$program>::load(self) + } - assert_unloaded("pass"); + fn unload(&mut self) -> Result<(), ProgramError> { + <$program>::unload(self) + } + + fn take_link(&mut self, id: Self::LinkId) -> Result { + <$program>::take_link(self, id) + } + } + }; } +impl_unload_program_ops!(Xdp, XdpLinkId, XdpLink); +impl_unload_program_ops!(KProbe, KProbeLinkId, KProbeLink); +impl_unload_program_ops!(TracePoint, TracePointLinkId, TracePointLink); +impl_unload_program_ops!(UProbe, UProbeLinkId, UProbeLink); +impl_unload_program_ops!(FlowDissector, FlowDissectorLinkId, FlowDissectorLink); + #[test_log::test] -fn unload_kprobe() { - let mut bpf = Ebpf::load(crate::TEST).unwrap(); - let prog: &mut KProbe = bpf.program_mut("test_kprobe").unwrap().try_into().unwrap(); +fn unload_xdp() { + type P = Xdp; + + let program_name = "pass"; + let attach = |prog: &mut P| prog.attach("lo", XdpFlags::default()).unwrap(); + run_unload_program_test(crate::TEST, program_name, attach); +} + +fn run_unload_program_test

(bpf_image: &[u8], program_name: &str, attach: fn(&mut P) -> P::LinkId) +where + P: UnloadProgramOps, + for<'a> &'a mut Program: TryInto<&'a mut P, Error = ProgramError>, +{ + let mut bpf = Ebpf::load(bpf_image).unwrap(); + let prog: &mut P = bpf.program_mut(program_name).unwrap().try_into().unwrap(); prog.load().unwrap(); - assert_loaded("test_kprobe"); - let link = prog.attach("try_to_wake_up", 0).unwrap(); + assert_loaded(program_name); + let link = attach(prog); { - let _link_owned = prog.take_link(link).unwrap(); + let _link_owned: P::OwnedLink = prog.take_link(link).unwrap(); prog.unload().unwrap(); - assert_loaded_and_linked("test_kprobe"); - }; + assert_loaded_and_linked(program_name); + } - assert_unloaded("test_kprobe"); + assert_unloaded(program_name); prog.load().unwrap(); - assert_loaded("test_kprobe"); - prog.attach("try_to_wake_up", 0).unwrap(); + assert_loaded(program_name); + attach(prog); - assert_loaded("test_kprobe"); + assert_loaded(program_name); prog.unload().unwrap(); - assert_unloaded("test_kprobe"); + assert_unloaded(program_name); } #[test_log::test] -fn memmove() { - let mut bpf = Ebpf::load(crate::MEMMOVE_TEST).unwrap(); - let prog: &mut Xdp = bpf.program_mut("do_dnat").unwrap().try_into().unwrap(); +fn unload_kprobe() { + type P = KProbe; - prog.load().unwrap(); - assert_loaded("do_dnat"); + let program_name = "test_kprobe"; + let attach = |prog: &mut P| prog.attach("try_to_wake_up", 0).unwrap(); + run_unload_program_test(crate::TEST, program_name, attach); } #[test_log::test] fn basic_tracepoint() { - let mut bpf = Ebpf::load(crate::TEST).unwrap(); - let prog: &mut TracePoint = bpf - .program_mut("test_tracepoint") - .unwrap() - .try_into() - .unwrap(); - - prog.load().unwrap(); - assert_loaded("test_tracepoint"); - let link = prog.attach("syscalls", "sys_enter_kill").unwrap(); - - { - let _link_owned = prog.take_link(link).unwrap(); - prog.unload().unwrap(); - assert_loaded_and_linked("test_tracepoint"); - }; + type P = TracePoint; - assert_unloaded("test_tracepoint"); - prog.load().unwrap(); - - assert_loaded("test_tracepoint"); - prog.attach("syscalls", "sys_enter_kill").unwrap(); - - assert_loaded("test_tracepoint"); - prog.unload().unwrap(); - - assert_unloaded("test_tracepoint"); + let program_name = "test_tracepoint"; + let attach = |prog: &mut P| prog.attach("syscalls", "sys_enter_kill").unwrap(); + run_unload_program_test(crate::TEST, program_name, attach); } #[test_log::test] fn basic_uprobe() { - let mut bpf = Ebpf::load(crate::TEST).unwrap(); - let prog: &mut UProbe = bpf.program_mut("test_uprobe").unwrap().try_into().unwrap(); - - prog.load().unwrap(); - assert_loaded("test_uprobe"); - let link = prog - .attach("uprobe_function", "/proc/self/exe", None, None) - .unwrap(); + type P = UProbe; - { - let _link_owned = prog.take_link(link).unwrap(); - prog.unload().unwrap(); - assert_loaded_and_linked("test_uprobe"); + let program_name = "test_uprobe"; + let attach = |prog: &mut P| { + prog.attach("uprobe_function", "/proc/self/exe", None, None) + .unwrap() }; - - assert_unloaded("test_uprobe"); - prog.load().unwrap(); - - assert_loaded("test_uprobe"); - prog.attach("uprobe_function", "/proc/self/exe", None, None) - .unwrap(); - - assert_loaded("test_uprobe"); - prog.unload().unwrap(); - - assert_unloaded("test_uprobe"); + run_unload_program_test(crate::TEST, program_name, attach); } #[test_log::test] fn basic_flow_dissector() { - let mut bpf = Ebpf::load(crate::TEST).unwrap(); - let prog: &mut FlowDissector = bpf.program_mut("test_flow").unwrap().try_into().unwrap(); - - prog.load().unwrap(); - assert_loaded("test_flow"); + type P = FlowDissector; - let net_ns = std::fs::File::open("/proc/self/ns/net").unwrap(); - let link = prog.attach(net_ns.try_clone().unwrap()).unwrap(); - { - let _link_owned = prog.take_link(link).unwrap(); - prog.unload().unwrap(); - assert_loaded_and_linked("test_flow"); + let program_name = "test_flow"; + let attach = |prog: &mut P| { + let net_ns = std::fs::File::open("/proc/self/ns/net").unwrap(); + prog.attach(net_ns).unwrap() }; - - assert_unloaded("test_flow"); - prog.load().unwrap(); - - assert_loaded("test_flow"); - prog.attach(net_ns).unwrap(); - - assert_loaded("test_flow"); - prog.unload().unwrap(); - - assert_unloaded("test_flow"); + run_unload_program_test(crate::TEST, program_name, attach); } #[test_log::test] fn pin_link() { + type P = Xdp; + + let program_name = "pass"; + let attach = |prog: &mut P| prog.attach("lo", XdpFlags::default()).unwrap(); + let kernel_version = KernelVersion::current().unwrap(); if kernel_version < KernelVersion::new(5, 9, 0) { eprintln!("skipping test on kernel {kernel_version:?}, XDP uses netlink"); @@ -347,30 +332,62 @@ fn pin_link() { } let mut bpf = Ebpf::load(crate::TEST).unwrap(); - let prog: &mut Xdp = bpf.program_mut("pass").unwrap().try_into().unwrap(); + let prog: &mut P = bpf.program_mut(program_name).unwrap().try_into().unwrap(); prog.load().unwrap(); - let link_id = prog.attach("lo", XdpFlags::default()).unwrap(); + let link_id = attach(prog); let link = prog.take_link(link_id).unwrap(); - assert_loaded("pass"); + assert_loaded(program_name); let fd_link: FdLink = link.try_into().unwrap(); let pinned = fd_link.pin("/sys/fs/bpf/aya-xdp-test-lo").unwrap(); // because of the pin, the program is still attached prog.unload().unwrap(); - assert_loaded("pass"); + assert_loaded(program_name); // delete the pin, but the program is still attached let new_link = pinned.unpin().unwrap(); - assert_loaded("pass"); + assert_loaded(program_name); // finally when new_link is dropped we're detached drop(new_link); - assert_unloaded("pass"); + assert_unloaded(program_name); +} + +trait PinProgramOps { + fn pin>(&mut self, path: P) -> Result<(), PinError>; + fn unpin(self) -> Result<(), std::io::Error>; } +macro_rules! impl_pin_program_ops { + ($program:ty) => { + impl PinProgramOps for $program { + fn pin>(&mut self, path: P) -> Result<(), PinError> { + <$program>::pin(self, path) + } + + fn unpin(self) -> Result<(), std::io::Error> { + <$program>::unpin(self) + } + } + }; +} + +impl_pin_program_ops!(Xdp); +impl_pin_program_ops!(KProbe); +impl_pin_program_ops!(TracePoint); +impl_pin_program_ops!(UProbe); + #[test_log::test] fn pin_lifecycle() { + type P = Xdp; + + let program_name = "pass"; + let attach = |prog: &mut P| prog.attach("lo", XdpFlags::default()).unwrap(); + let program_pin = "/sys/fs/bpf/aya-xdp-test-prog"; + let link_pin = "/sys/fs/bpf/aya-xdp-test-lo"; + let from_pin = |program_pin: &str| P::from_pin(program_pin, XdpAttachType::Interface).unwrap(); + let kernel_version = KernelVersion::current().unwrap(); if kernel_version < KernelVersion::new(5, 18, 0) { eprintln!( @@ -378,170 +395,120 @@ fn pin_lifecycle() { ); return; } + run_pin_program_lifecycle_test( + crate::PASS, + program_name, + program_pin, + link_pin, + from_pin, + attach, + Some(|prog: &mut P, pinned: FdLink| { + prog.attach_to_link(pinned.try_into().unwrap()).unwrap() + }), + ); +} +fn run_pin_program_lifecycle_test

( + bpf_image: &[u8], + program_name: &str, + program_pin: &str, + link_pin: &str, + from_pin: fn(&str) -> P, + attach: fn(&mut P) -> P::LinkId, + attach_to_link: Option P::LinkId>, +) where + P: UnloadProgramOps + PinProgramOps, + P::OwnedLink: TryInto, + for<'a> &'a mut Program: TryInto<&'a mut P, Error = ProgramError>, +{ // 1. Load Program and Pin { - let mut bpf = Ebpf::load(crate::PASS).unwrap(); - let prog: &mut Xdp = bpf.program_mut("pass").unwrap().try_into().unwrap(); + let mut bpf = Ebpf::load(bpf_image).unwrap(); + let prog: &mut P = bpf.program_mut(program_name).unwrap().try_into().unwrap(); prog.load().unwrap(); - prog.pin("/sys/fs/bpf/aya-xdp-test-prog").unwrap(); + prog.pin(program_pin).unwrap(); } // should still be loaded since prog was pinned - assert_loaded("pass"); + assert_loaded(program_name); // 2. Load program from bpffs but don't attach it { - let _ = Xdp::from_pin("/sys/fs/bpf/aya-xdp-test-prog", XdpAttachType::Interface).unwrap(); + let _: P = from_pin(program_pin); } // should still be loaded since prog was pinned - assert_loaded("pass"); + assert_loaded(program_name); // 3. Load program from bpffs and attach { - let mut prog = - Xdp::from_pin("/sys/fs/bpf/aya-xdp-test-prog", XdpAttachType::Interface).unwrap(); - let link_id = prog.attach("lo", XdpFlags::default()).unwrap(); + let mut prog = from_pin(program_pin); + let link_id = attach(&mut prog); let link = prog.take_link(link_id).unwrap(); let fd_link: FdLink = link.try_into().unwrap(); - fd_link.pin("/sys/fs/bpf/aya-xdp-test-lo").unwrap(); + fd_link.pin(link_pin).unwrap(); // Unpin the program. It will stay attached since its links were pinned. prog.unpin().unwrap(); } // should still be loaded since link was pinned - assert_loaded_and_linked("pass"); + assert_loaded_and_linked(program_name); // 4. Load a new version of the program, unpin link, and atomically replace old program { - let mut bpf = Ebpf::load(crate::PASS).unwrap(); - let prog: &mut Xdp = bpf.program_mut("pass").unwrap().try_into().unwrap(); - prog.load().unwrap(); - - let link = PinnedLink::from_pin("/sys/fs/bpf/aya-xdp-test-lo") - .unwrap() - .unpin() - .unwrap(); - prog.attach_to_link(link.try_into().unwrap()).unwrap(); - assert_loaded("pass"); + let link = PinnedLink::from_pin(link_pin).unwrap().unpin().unwrap(); + if let Some(attach_to_link) = attach_to_link { + let mut bpf = Ebpf::load(bpf_image).unwrap(); + let prog: &mut P = bpf.program_mut(program_name).unwrap().try_into().unwrap(); + prog.load().unwrap(); + attach_to_link(prog, link); + assert_loaded(program_name); + } } // program should be unloaded - assert_unloaded("pass"); + assert_unloaded(program_name); } #[test_log::test] fn pin_lifecycle_tracepoint() { - // 1. Load Program and Pin - { - let mut bpf = Ebpf::load(crate::TEST).unwrap(); - let prog: &mut TracePoint = bpf - .program_mut("test_tracepoint") - .unwrap() - .try_into() - .unwrap(); - prog.load().unwrap(); - prog.pin("/sys/fs/bpf/aya-tracepoint-test-prog").unwrap(); - } - - // should still be loaded since prog was pinned - assert_loaded("test_tracepoint"); - - // 2. Load program from bpffs but don't attach it - { - let _ = TracePoint::from_pin("/sys/fs/bpf/aya-tracepoint-test-prog").unwrap(); - } - - // should still be loaded since prog was pinned - assert_loaded("test_tracepoint"); - - // 3. Load program from bpffs and attach - { - let mut prog = TracePoint::from_pin("/sys/fs/bpf/aya-tracepoint-test-prog").unwrap(); - let link_id = prog.attach("syscalls", "sys_enter_kill").unwrap(); - let link = prog.take_link(link_id).unwrap(); - let fd_link: FdLink = link.try_into().unwrap(); - fd_link - .pin("/sys/fs/bpf/aya-tracepoint-test-sys-enter-kill") - .unwrap(); - - // Unpin the program. It will stay attached since its links were pinned. - prog.unpin().unwrap(); - } - - // should still be loaded since link was pinned - assert_loaded_and_linked("test_tracepoint"); - - // 4. unpin link, and make sure everything is unloaded - { - PinnedLink::from_pin("/sys/fs/bpf/aya-tracepoint-test-sys-enter-kill") - .unwrap() - .unpin() - .unwrap(); - } - - // program should be unloaded - assert_unloaded("test_tracepoint"); + type P = TracePoint; + + let program_name = "test_tracepoint"; + let attach = |prog: &mut P| prog.attach("syscalls", "sys_enter_kill").unwrap(); + let program_pin = "/sys/fs/bpf/aya-tracepoint-test-prog"; + let link_pin = "/sys/fs/bpf/aya-tracepoint-test-sys-enter-kill"; + let from_pin = |program_pin: &str| P::from_pin(program_pin).unwrap(); + run_pin_program_lifecycle_test( + crate::TEST, + program_name, + program_pin, + link_pin, + from_pin, + attach, + None, + ); } #[test_log::test] fn pin_lifecycle_kprobe() { - // 1. Load Program and Pin - { - let mut bpf = Ebpf::load(crate::TEST).unwrap(); - let prog: &mut KProbe = bpf.program_mut("test_kprobe").unwrap().try_into().unwrap(); - prog.load().unwrap(); - prog.pin("/sys/fs/bpf/aya-kprobe-test-prog").unwrap(); - } - - // should still be loaded since prog was pinned - assert_loaded("test_kprobe"); - - // 2. Load program from bpffs but don't attach it - { - let _ = KProbe::from_pin( - "/sys/fs/bpf/aya-kprobe-test-prog", - aya::programs::ProbeKind::KProbe, - ) - .unwrap(); - } - - // should still be loaded since prog was pinned - assert_loaded("test_kprobe"); - - // 3. Load program from bpffs and attach - { - let mut prog = KProbe::from_pin( - "/sys/fs/bpf/aya-kprobe-test-prog", - aya::programs::ProbeKind::KProbe, - ) - .unwrap(); - let link_id = prog.attach("try_to_wake_up", 0).unwrap(); - let link = prog.take_link(link_id).unwrap(); - let fd_link: FdLink = link.try_into().unwrap(); - fd_link - .pin("/sys/fs/bpf/aya-kprobe-test-try-to-wake-up") - .unwrap(); - - // Unpin the program. It will stay attached since its links were pinned. - prog.unpin().unwrap(); - } - - // should still be loaded since link was pinned - assert_loaded_and_linked("test_kprobe"); - - // 4. unpin link, and make sure everything is unloaded - { - PinnedLink::from_pin("/sys/fs/bpf/aya-kprobe-test-try-to-wake-up") - .unwrap() - .unpin() - .unwrap(); - } - - // program should be unloaded - assert_unloaded("test_kprobe"); + type P = KProbe; + + let program_name = "test_kprobe"; + let attach = |prog: &mut P| prog.attach("try_to_wake_up", 0).unwrap(); + let program_pin = "/sys/fs/bpf/aya-kprobe-test-prog"; + let link_pin = "/sys/fs/bpf/aya-kprobe-test-try-to-wake-up"; + let from_pin = |program_pin: &str| P::from_pin(program_pin, ProbeKind::KProbe).unwrap(); + run_pin_program_lifecycle_test( + crate::TEST, + program_name, + program_pin, + link_pin, + from_pin, + attach, + None, + ); } #[unsafe(no_mangle)] @@ -552,55 +519,25 @@ extern "C" fn uprobe_function() { #[test_log::test] fn pin_lifecycle_uprobe() { - const FIRST_PIN_PATH: &str = "/sys/fs/bpf/aya-uprobe-test-prog-1"; - const SECOND_PIN_PATH: &str = "/sys/fs/bpf/aya-uprobe-test-prog-2"; - - // 1. Load Program and Pin - { - let mut bpf = Ebpf::load(crate::TEST).unwrap(); - let prog: &mut UProbe = bpf.program_mut("test_uprobe").unwrap().try_into().unwrap(); - prog.load().unwrap(); - prog.pin(FIRST_PIN_PATH).unwrap(); - } - - // should still be loaded since prog was pinned - assert_loaded("test_uprobe"); + type P = UProbe; - // 2. Load program from bpffs but don't attach it - { - let _ = UProbe::from_pin(FIRST_PIN_PATH, aya::programs::ProbeKind::UProbe).unwrap(); - } - - // should still be loaded since prog was pinned - assert_loaded("test_uprobe"); - - // 3. Load program from bpffs and attach - { - let mut prog = UProbe::from_pin(FIRST_PIN_PATH, aya::programs::ProbeKind::UProbe).unwrap(); - let link_id = prog - .attach("uprobe_function", "/proc/self/exe", None, None) - .unwrap(); - let link = prog.take_link(link_id).unwrap(); - let fd_link: FdLink = link.try_into().unwrap(); - fd_link.pin(SECOND_PIN_PATH).unwrap(); - - // Unpin the program. It will stay attached since its links were pinned. - prog.unpin().unwrap(); - } - - // should still be loaded since link was pinned - assert_loaded_and_linked("test_uprobe"); - - // 4. unpin link, and make sure everything is unloaded - { - PinnedLink::from_pin(SECOND_PIN_PATH) + let program_name = "test_uprobe"; + let attach = |prog: &mut P| { + prog.attach("uprobe_function", "/proc/self/exe", None, None) .unwrap() - .unpin() - .unwrap(); - } - - // program should be unloaded - assert_unloaded("test_uprobe"); + }; + let program_pin = "/sys/fs/bpf/aya-uprobe-test-prog"; + let link_pin = "/sys/fs/bpf/aya-uprobe-test-uprobe-function"; + let from_pin = |program_pin: &str| P::from_pin(program_pin, ProbeKind::UProbe).unwrap(); + run_pin_program_lifecycle_test( + crate::TEST, + program_name, + program_pin, + link_pin, + from_pin, + attach, + None, + ); // Make sure the function isn't optimized out. uprobe_function(); diff --git a/test/integration-test/src/tests/log.rs b/test/integration-test/src/tests/log.rs index e1659d54..1973bf84 100644 --- a/test/integration-test/src/tests/log.rs +++ b/test/integration-test/src/tests/log.rs @@ -187,6 +187,15 @@ fn log() { }) ); + assert_eq!( + records.next(), + Some(&CapturedLog { + body: "ptr: 0xdeadbeef".into(), + level: Level::Debug, + target: "log".into(), + }) + ); + assert_eq!( records.next(), Some(&CapturedLog { diff --git a/xtask/public-api/aya-log-common.txt b/xtask/public-api/aya-log-common.txt index 481b273e..7187da8e 100644 --- a/xtask/public-api/aya-log-common.txt +++ b/xtask/public-api/aya-log-common.txt @@ -15,6 +15,7 @@ pub aya_log_common::ArgumentKind::I8 pub aya_log_common::ArgumentKind::Ipv4Addr pub aya_log_common::ArgumentKind::Ipv6Addr pub aya_log_common::ArgumentKind::Isize +pub aya_log_common::ArgumentKind::Pointer pub aya_log_common::ArgumentKind::Str pub aya_log_common::ArgumentKind::U16 pub aya_log_common::ArgumentKind::U32 @@ -57,6 +58,7 @@ pub aya_log_common::DisplayHint::Default = 1 pub aya_log_common::DisplayHint::Ip pub aya_log_common::DisplayHint::LowerHex pub aya_log_common::DisplayHint::LowerMac +pub aya_log_common::DisplayHint::Pointer pub aya_log_common::DisplayHint::UpperHex pub aya_log_common::DisplayHint::UpperMac impl aya_log_common::Argument for aya_log_common::DisplayHint @@ -215,6 +217,10 @@ impl aya_log_common::Argument for u8 pub fn u8::as_argument(&self) -> (aya_log_common::ArgumentKind, impl core::convert::AsRef<[u8]>) impl aya_log_common::Argument for usize pub fn usize::as_argument(&self) -> (aya_log_common::ArgumentKind, impl core::convert::AsRef<[u8]>) +impl aya_log_common::Argument for *const T +pub fn *const T::as_argument(&self) -> (aya_log_common::ArgumentKind, impl core::convert::AsRef<[u8]>) +impl aya_log_common::Argument for *mut T +pub fn *mut T::as_argument(&self) -> (aya_log_common::ArgumentKind, impl core::convert::AsRef<[u8]>) impl aya_log_common::Argument for [u8; N] pub fn [u8; N]::as_argument(&self) -> (aya_log_common::ArgumentKind, impl core::convert::AsRef<[u8]>) pub trait aya_log_common::DefaultFormatter @@ -260,6 +266,9 @@ impl aya_log_common::LowerHexFormatter for usize impl aya_log_common::LowerHexFormatter for &[u8; N] pub trait aya_log_common::LowerMacFormatter impl aya_log_common::LowerMacFormatter for [u8; 6] +pub trait aya_log_common::PointerFormatter +impl aya_log_common::PointerFormatter for *const T +impl aya_log_common::PointerFormatter for *mut T pub trait aya_log_common::UpperHexFormatter impl aya_log_common::UpperHexFormatter for &[u8] impl aya_log_common::UpperHexFormatter for i16 diff --git a/xtask/public-api/aya-log.txt b/xtask/public-api/aya-log.txt index 3583315e..a469879c 100644 --- a/xtask/public-api/aya-log.txt +++ b/xtask/public-api/aya-log.txt @@ -223,6 +223,33 @@ impl core::borrow::BorrowMut for aya_log::LowerMacFormatter where T: ?core pub fn aya_log::LowerMacFormatter::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya_log::LowerMacFormatter pub fn aya_log::LowerMacFormatter::from(t: T) -> T +pub struct aya_log::PointerFormatter +impl aya_log::Formatter<*const T> for aya_log::PointerFormatter +pub fn aya_log::PointerFormatter::format(v: *const T) -> alloc::string::String +impl aya_log::Formatter<*mut T> for aya_log::PointerFormatter +pub fn aya_log::PointerFormatter::format(v: *mut T) -> alloc::string::String +impl core::marker::Freeze for aya_log::PointerFormatter +impl core::marker::Send for aya_log::PointerFormatter +impl core::marker::Sync for aya_log::PointerFormatter +impl core::marker::Unpin for aya_log::PointerFormatter +impl core::panic::unwind_safe::RefUnwindSafe for aya_log::PointerFormatter +impl core::panic::unwind_safe::UnwindSafe for aya_log::PointerFormatter +impl core::convert::Into for aya_log::PointerFormatter where U: core::convert::From +pub fn aya_log::PointerFormatter::into(self) -> U +impl core::convert::TryFrom for aya_log::PointerFormatter where U: core::convert::Into +pub type aya_log::PointerFormatter::Error = core::convert::Infallible +pub fn aya_log::PointerFormatter::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_log::PointerFormatter where U: core::convert::TryFrom +pub type aya_log::PointerFormatter::Error = >::Error +pub fn aya_log::PointerFormatter::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_log::PointerFormatter where T: 'static + ?core::marker::Sized +pub fn aya_log::PointerFormatter::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_log::PointerFormatter where T: ?core::marker::Sized +pub fn aya_log::PointerFormatter::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_log::PointerFormatter where T: ?core::marker::Sized +pub fn aya_log::PointerFormatter::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_log::PointerFormatter +pub fn aya_log::PointerFormatter::from(t: T) -> T pub struct aya_log::UpperHexBytesFormatter impl aya_log::Formatter<&[u8]> for aya_log::UpperHexBytesFormatter pub fn aya_log::UpperHexBytesFormatter::format(v: &[u8]) -> alloc::string::String @@ -309,6 +336,10 @@ impl aya_log::Formatter<[u8; 6]> for aya_log::LowerMacFormatter pub fn aya_log::LowerMacFormatter::format(v: [u8; 6]) -> alloc::string::String impl aya_log::Formatter<[u8; 6]> for aya_log::UpperMacFormatter pub fn aya_log::UpperMacFormatter::format(v: [u8; 6]) -> alloc::string::String +impl aya_log::Formatter<*const T> for aya_log::PointerFormatter +pub fn aya_log::PointerFormatter::format(v: *const T) -> alloc::string::String +impl aya_log::Formatter<*mut T> for aya_log::PointerFormatter +pub fn aya_log::PointerFormatter::format(v: *mut T) -> alloc::string::String impl aya_log::Formatter for aya_log::DefaultFormatter where T: alloc::string::ToString pub fn aya_log::DefaultFormatter::format(v: T) -> alloc::string::String impl aya_log::Formatter for aya_log::Ipv4Formatter where T: core::convert::Into diff --git a/xtask/public-api/aya.txt b/xtask/public-api/aya.txt index 2390f95d..57a32141 100644 --- a/xtask/public-api/aya.txt +++ b/xtask/public-api/aya.txt @@ -3828,6 +3828,9 @@ pub fn aya::programs::flow_dissector::FlowDissectorLink::id(&self) -> Self::Id impl core::cmp::Eq for aya::programs::flow_dissector::FlowDissectorLink impl core::cmp::PartialEq for aya::programs::flow_dissector::FlowDissectorLink pub fn aya::programs::flow_dissector::FlowDissectorLink::eq(&self, other: &Self) -> bool +impl core::convert::TryFrom for aya::programs::links::FdLink +pub type aya::programs::links::FdLink::Error = aya::programs::links::LinkError +pub fn aya::programs::links::FdLink::try_from(value: aya::programs::flow_dissector::FlowDissectorLink) -> core::result::Result impl core::fmt::Debug for aya::programs::flow_dissector::FlowDissectorLink pub fn aya::programs::flow_dissector::FlowDissectorLink::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl core::hash::Hash for aya::programs::flow_dissector::FlowDissectorLink @@ -4416,6 +4419,9 @@ pub fn aya::programs::links::FdLink::try_from(value: aya::programs::cgroup_sock: impl core::convert::TryFrom for aya::programs::links::FdLink pub type aya::programs::links::FdLink::Error = aya::programs::links::LinkError pub fn aya::programs::links::FdLink::try_from(value: aya::programs::cgroup_sock_addr::CgroupSockAddrLink) -> core::result::Result +impl core::convert::TryFrom for aya::programs::links::FdLink +pub type aya::programs::links::FdLink::Error = aya::programs::links::LinkError +pub fn aya::programs::links::FdLink::try_from(value: aya::programs::flow_dissector::FlowDissectorLink) -> core::result::Result impl core::convert::TryFrom for aya::programs::links::FdLink pub type aya::programs::links::FdLink::Error = aya::programs::links::LinkError pub fn aya::programs::links::FdLink::try_from(value: aya::programs::iter::IterLink) -> core::result::Result diff --git a/xtask/src/main.rs b/xtask/src/main.rs index b085cc12..99c48c0e 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -3,9 +3,9 @@ mod docs; mod public_api; mod run; -use std::process::Command; +use std::process::{Command, Output}; -use anyhow::{Context as _, Result}; +use anyhow::{Context as _, Result, bail}; use cargo_metadata::{Metadata, MetadataCommand}; use clap::Parser; use xtask::{LIBBPF_DIR, exec}; @@ -33,12 +33,29 @@ fn main() -> Result<()> { .context("failed to run cargo metadata")?; let Metadata { workspace_root, .. } = &metadata; - // Initialize the submodules. - exec(Command::new("git").arg("-C").arg(workspace_root).args([ - "submodule", - "update", - "--init", - ]))?; + let mut libbpf_submodule_status = Command::new("git"); + let output = libbpf_submodule_status + .arg("-C") + .arg(workspace_root) + .arg("submodule") + .arg("status") + .arg(LIBBPF_DIR) + .output() + .with_context(|| format!("failed to run {libbpf_submodule_status:?}"))?; + let Output { status, .. } = &output; + if !status.success() { + bail!("{libbpf_submodule_status:?} failed: {output:?}") + } + let Output { stdout, .. } = output; + if !stdout.starts_with(b" ") { + // Initialize the submodules. + exec(Command::new("git").arg("-C").arg(workspace_root).args([ + "submodule", + "update", + "--init", + ]))?; + } + let libbpf_dir = workspace_root.join(LIBBPF_DIR); let libbpf_dir = libbpf_dir.as_std_path(); diff --git a/xtask/src/run.rs b/xtask/src/run.rs index f656bcdf..93597fab 100644 --- a/xtask/src/run.rs +++ b/xtask/src/run.rs @@ -245,7 +245,7 @@ pub(crate) fn run(opts: Options) -> Result<()> { let etag_path_exists = etag_path.try_exists().with_context(|| { format!("failed to check existence of {}", etag_path.display()) })?; - if !dest_path_exists && etag_path_exists { + if dest_path_exists != etag_path_exists { println!( "cargo:warning=({}).exists()={} != ({})={} (mismatch)", dest_path.display(), @@ -274,7 +274,14 @@ pub(crate) fn run(opts: Options) -> Result<()> { .with_context(|| format!("failed to run {curl:?}"))?; let Output { status, .. } = &output; if status.code() != Some(0) { - bail!("{curl:?} failed: {output:?}") + if dest_path_exists { + println!( + "cargo:warning={curl:?} failed ({status:?}); using cached {}", + dest_path.display() + ); + } else { + bail!("{curl:?} failed: {output:?}") + } } let mut patch = Command::new("patch");