Skip to content

Commit 587d04a

Browse files
committed
aya: add a safe wrapper for 'BPF_TASK_FD_QUERY'
This wraps the logic for `BPF_TASK_FD_QUERY` in a safe helper.
1 parent d4cb58e commit 587d04a

File tree

2 files changed

+175
-4
lines changed

2 files changed

+175
-4
lines changed

aya/src/programs/mod.rs

+112-3
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ pub mod xdp;
6666

6767
use libc::ENOSPC;
6868
use std::{
69-
ffi::CString,
69+
ffi::{CStr, CString},
7070
io,
7171
os::unix::io::{AsRawFd, RawFd},
7272
path::Path,
@@ -103,13 +103,14 @@ pub use uprobe::{UProbe, UProbeError};
103103
pub use xdp::{Xdp, XdpError, XdpFlags};
104104

105105
use crate::{
106-
generated::{bpf_attach_type, bpf_prog_info, bpf_prog_type},
106+
generated::{bpf_attach_type, bpf_prog_info, bpf_prog_type, bpf_task_fd_type},
107107
maps::MapError,
108108
obj::{self, btf::BtfError, Function, KernelVersion},
109109
pin::PinError,
110110
sys::{
111111
bpf_get_object, bpf_load_program, bpf_pin_object, bpf_prog_get_fd_by_id,
112-
bpf_prog_get_info_by_fd, bpf_prog_query, retry_with_verifier_logs, BpfLoadProgramAttrs,
112+
bpf_prog_get_info_by_fd, bpf_prog_query, bpf_task_fd_query, retry_with_verifier_logs,
113+
BpfLoadProgramAttrs,
113114
},
114115
util::VerifierLog,
115116
};
@@ -849,3 +850,111 @@ impl ProgramInfo {
849850
Ok(ProgramInfo(info))
850851
}
851852
}
853+
854+
/// Kind of a probe program from a process FD.
855+
#[repr(u32)]
856+
#[non_exhaustive]
857+
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
858+
pub enum TaskFdKind {
859+
/// Raw tracepoint.
860+
RawTracePoint = bpf_task_fd_type::BPF_FD_TYPE_RAW_TRACEPOINT as u32,
861+
/// Tracepoint.
862+
TracePoint = bpf_task_fd_type::BPF_FD_TYPE_TRACEPOINT as u32,
863+
/// Kernel probe.
864+
KProbe = bpf_task_fd_type::BPF_FD_TYPE_KPROBE as u32,
865+
/// Kernel return probe.
866+
KRetProbe = bpf_task_fd_type::BPF_FD_TYPE_KRETPROBE as u32,
867+
/// User space probe.
868+
UProbe = bpf_task_fd_type::BPF_FD_TYPE_UPROBE as u32,
869+
/// User space return probe.
870+
URetProbe = bpf_task_fd_type::BPF_FD_TYPE_URETPROBE as u32,
871+
}
872+
873+
impl TryFrom<u32> for TaskFdKind {
874+
type Error = ProgramError;
875+
876+
fn try_from(v: u32) -> Result<Self, Self::Error> {
877+
use bpf_task_fd_type::*;
878+
use TaskFdKind::*;
879+
Ok(match v {
880+
x if x == BPF_FD_TYPE_RAW_TRACEPOINT as u32 => RawTracePoint,
881+
x if x == BPF_FD_TYPE_TRACEPOINT as u32 => TracePoint,
882+
x if x == BPF_FD_TYPE_KPROBE as u32 => KProbe,
883+
x if x == BPF_FD_TYPE_KRETPROBE as u32 => KRetProbe,
884+
x if x == BPF_FD_TYPE_UPROBE as u32 => UProbe,
885+
x if x == BPF_FD_TYPE_URETPROBE as u32 => URetProbe,
886+
_ => return Err(ProgramError::UnexpectedProgramType),
887+
})
888+
}
889+
}
890+
891+
/// Details of a probe program from a process FD.
892+
#[derive(Debug)]
893+
pub struct TaskFdDetails<'buf> {
894+
/// Program ID.
895+
id: u32,
896+
/// Program kind.
897+
kind: TaskFdKind,
898+
/// Program name.
899+
name: Option<&'buf CStr>,
900+
/// Optional program probe address.
901+
probe_addr: Option<u64>,
902+
/// Optional program probe offset.
903+
probe_offset: Option<u64>,
904+
}
905+
906+
impl<'buf> TaskFdDetails<'buf> {
907+
/// Query the details of an FD in a process (task), looking for probe programs.
908+
///
909+
/// `Ok(None)` is returned if the target FD does not exist or is not a probe program.
910+
pub fn query_task_fd(
911+
pid: u32,
912+
target_fd: RawFd,
913+
out_name_buf: Option<&mut [u8]>,
914+
) -> Result<Option<TaskFdDetails<'buf>>, ProgramError> {
915+
let out = match bpf_task_fd_query(pid, target_fd, out_name_buf) {
916+
Ok(v) => v,
917+
// ENOTSUPP (errno 95) means that the target FD is not a perf program.
918+
Err(e) if e.raw_os_error() == Some(95) => return Ok(None),
919+
// ENOENT (errno 2) means that the target FD or PID does not exist.
920+
Err(e) if e.raw_os_error() == Some(2) => return Ok(None),
921+
Err(e) => {
922+
return Err(ProgramError::SyscallError {
923+
call: "bpf_task_fd_query".to_owned(),
924+
io_error: e,
925+
})
926+
}
927+
};
928+
let kind = TaskFdKind::try_from(out.fd_type as u32)?;
929+
Ok(Some(TaskFdDetails {
930+
id: out.prog_id,
931+
kind,
932+
name: out.name,
933+
probe_addr: out.probe_addr,
934+
probe_offset: out.probe_offset,
935+
}))
936+
}
937+
938+
/// Return the program ID.
939+
pub fn id(&self) -> u32 {
940+
self.id
941+
}
942+
943+
/// Return the program kind.
944+
pub fn kind(&self) -> TaskFdKind {
945+
self.kind
946+
}
947+
948+
/// Return the program name.
949+
pub fn name(&self) -> Option<&CStr> {
950+
self.name
951+
}
952+
953+
/// Return the program probe address and offset.
954+
pub fn address_and_offset(&self) -> Option<(u64, u64)> {
955+
match (self.probe_addr, self.probe_offset) {
956+
(Some(a), Some(o)) => Some((a, o)),
957+
_ => None,
958+
}
959+
}
960+
}

aya/src/sys/bpf.rs

+63-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use libc::{c_char, c_long, close, ENOENT, ENOSPC};
1212
use crate::{
1313
generated::{
1414
bpf_attach_type, bpf_attr, bpf_btf_info, bpf_cmd, bpf_insn, bpf_link_info, bpf_map_info,
15-
bpf_prog_info, bpf_prog_type, BPF_F_REPLACE,
15+
bpf_prog_info, bpf_prog_type, bpf_task_fd_type, BPF_F_REPLACE,
1616
},
1717
maps::PerCpuValues,
1818
obj::{
@@ -451,6 +451,68 @@ pub(crate) fn bpf_prog_get_info_by_fd(prog_fd: RawFd) -> Result<bpf_prog_info, i
451451
}
452452
}
453453

454+
#[derive(Debug)]
455+
pub(crate) struct TaskFdQueryOutput<'buf> {
456+
pub(crate) prog_id: u32,
457+
pub(crate) fd_type: bpf_task_fd_type,
458+
pub(crate) name: Option<&'buf CStr>,
459+
pub(crate) probe_offset: Option<u64>,
460+
pub(crate) probe_addr: Option<u64>,
461+
}
462+
463+
pub(crate) fn bpf_task_fd_query<'buf>(
464+
pid: u32,
465+
target_fd: RawFd,
466+
out_name_buf: Option<&mut [u8]>,
467+
) -> Result<TaskFdQueryOutput<'buf>, io::Error> {
468+
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
469+
470+
attr.task_fd_query.pid = pid;
471+
attr.task_fd_query.fd = target_fd as u32;
472+
let mut out_name_buf = out_name_buf;
473+
if let Some(buf) = &mut out_name_buf {
474+
attr.task_fd_query.buf = buf.as_mut_ptr() as u64;
475+
attr.task_fd_query.buf_len = buf.len() as u32;
476+
};
477+
478+
if let Err((_, err)) = sys_bpf(bpf_cmd::BPF_TASK_FD_QUERY, &attr) {
479+
// The kernel here may leak an internal ENOTSUPP code (524), so
480+
// this needs to translate it back to POSIX-defined ENOTSUPP (95).
481+
return match err.raw_os_error() {
482+
Some(524) => Err(io::Error::from_raw_os_error(95)),
483+
_ => Err(err),
484+
};
485+
}
486+
487+
let fd_type = unsafe { std::mem::transmute(attr.task_fd_query.fd_type) };
488+
let name = out_name_buf.map(|buf| unsafe {
489+
CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(
490+
buf.as_ptr(),
491+
attr.task_fd_query.buf_len as usize + 1,
492+
))
493+
});
494+
let (probe_offset, probe_addr) = match fd_type {
495+
bpf_task_fd_type::BPF_FD_TYPE_KPROBE
496+
| bpf_task_fd_type::BPF_FD_TYPE_KRETPROBE
497+
| bpf_task_fd_type::BPF_FD_TYPE_UPROBE
498+
| bpf_task_fd_type::BPF_FD_TYPE_URETPROBE => unsafe {
499+
(
500+
Some(attr.task_fd_query.probe_offset),
501+
Some(attr.task_fd_query.probe_addr),
502+
)
503+
},
504+
_ => (None, None),
505+
};
506+
507+
Ok(TaskFdQueryOutput {
508+
prog_id: unsafe { attr.task_fd_query.prog_id },
509+
fd_type,
510+
name,
511+
probe_offset,
512+
probe_addr,
513+
})
514+
}
515+
454516
pub(crate) fn bpf_map_get_info_by_fd(prog_fd: RawFd) -> Result<bpf_map_info, io::Error> {
455517
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
456518
// info gets entirely populated by the kernel

0 commit comments

Comments
 (0)