Skip to content

Commit ff282d6

Browse files
lucabtamird
authored andcommitted
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 73a34e1 commit ff282d6

File tree

2 files changed

+178
-4
lines changed

2 files changed

+178
-4
lines changed

aya/src/programs/mod.rs

+114-3
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ pub mod uprobe;
7171
pub mod xdp;
7272

7373
use std::{
74-
ffi::CString,
74+
ffi::{CStr, CString},
7575
io,
7676
os::fd::{AsFd, BorrowedFd},
7777
path::{Path, PathBuf},
@@ -81,7 +81,7 @@ use std::{
8181
use aya_obj::{
8282
VerifierLog,
8383
btf::BtfError,
84-
generated::{bpf_attach_type, bpf_link_info, bpf_prog_info, bpf_prog_type},
84+
generated::{bpf_attach_type, bpf_link_info, bpf_prog_info, bpf_prog_type, bpf_task_fd_type},
8585
};
8686
use info::impl_info;
8787
pub use info::{ProgramInfo, ProgramType, loaded_programs};
@@ -127,7 +127,7 @@ use crate::{
127127
sys::{
128128
EbpfLoadProgramAttrs, NetlinkError, ProgQueryTarget, SyscallError, bpf_btf_get_fd_by_id,
129129
bpf_get_object, bpf_link_get_fd_by_id, bpf_link_get_info_by_fd, bpf_load_program,
130-
bpf_pin_object, bpf_prog_get_fd_by_id, bpf_prog_query, iter_link_ids,
130+
bpf_pin_object, bpf_prog_get_fd_by_id, bpf_prog_query, bpf_task_fd_query, iter_link_ids,
131131
retry_with_verifier_logs,
132132
},
133133
util::KernelVersion,
@@ -1089,3 +1089,114 @@ pub fn loaded_links() -> impl Iterator<Item = Result<bpf_link_info, ProgramError
10891089
})
10901090
.map(|result| result.map_err(Into::into))
10911091
}
1092+
1093+
/// Kind of a probe program from a process FD.
1094+
#[repr(u32)]
1095+
#[non_exhaustive]
1096+
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
1097+
pub enum TaskFdKind {
1098+
/// Raw tracepoint.
1099+
RawTracePoint = bpf_task_fd_type::BPF_FD_TYPE_RAW_TRACEPOINT as u32,
1100+
/// Tracepoint.
1101+
TracePoint = bpf_task_fd_type::BPF_FD_TYPE_TRACEPOINT as u32,
1102+
/// Kernel probe.
1103+
KProbe = bpf_task_fd_type::BPF_FD_TYPE_KPROBE as u32,
1104+
/// Kernel return probe.
1105+
KRetProbe = bpf_task_fd_type::BPF_FD_TYPE_KRETPROBE as u32,
1106+
/// User space probe.
1107+
UProbe = bpf_task_fd_type::BPF_FD_TYPE_UPROBE as u32,
1108+
/// User space return probe.
1109+
URetProbe = bpf_task_fd_type::BPF_FD_TYPE_URETPROBE as u32,
1110+
}
1111+
1112+
impl TryFrom<u32> for TaskFdKind {
1113+
type Error = ProgramError;
1114+
1115+
fn try_from(v: u32) -> Result<Self, Self::Error> {
1116+
use TaskFdKind::*;
1117+
use bpf_task_fd_type::*;
1118+
Ok(match v {
1119+
x if x == BPF_FD_TYPE_RAW_TRACEPOINT as u32 => RawTracePoint,
1120+
x if x == BPF_FD_TYPE_TRACEPOINT as u32 => TracePoint,
1121+
x if x == BPF_FD_TYPE_KPROBE as u32 => KProbe,
1122+
x if x == BPF_FD_TYPE_KRETPROBE as u32 => KRetProbe,
1123+
x if x == BPF_FD_TYPE_UPROBE as u32 => UProbe,
1124+
x if x == BPF_FD_TYPE_URETPROBE as u32 => URetProbe,
1125+
_ => return Err(ProgramError::UnexpectedProgramType),
1126+
})
1127+
}
1128+
}
1129+
1130+
/// Details of a probe program from a process FD.
1131+
#[derive(Debug)]
1132+
pub struct TaskFdDetails<'a> {
1133+
/// Program ID.
1134+
id: u32,
1135+
/// Program kind.
1136+
kind: TaskFdKind,
1137+
/// Program name.
1138+
name: Option<&'a CStr>,
1139+
/// Optional program probe address.
1140+
probe_addr: Option<u64>,
1141+
/// Optional program probe offset.
1142+
probe_offset: Option<u64>,
1143+
}
1144+
1145+
impl TaskFdDetails<'_> {
1146+
/// Query the details of an FD in a process (task), looking for probe programs.
1147+
///
1148+
/// `Ok(None)` is returned if the target FD does not exist or is not a probe program.
1149+
pub fn query_task_fd(
1150+
pid: u32,
1151+
target_fd: BorrowedFd<'_>,
1152+
out_name_buf: Option<&mut [u8]>,
1153+
) -> Result<Option<Self>, ProgramError> {
1154+
let out = match bpf_task_fd_query(pid, target_fd, out_name_buf) {
1155+
Ok(v) => v,
1156+
Err(io_error) => {
1157+
return match io_error.raw_os_error() {
1158+
// ENOTSUPP (errno 95) means that the target FD is not a perf program.
1159+
Some(95) => Ok(None),
1160+
// ENOENT (errno 2) means that the target FD or PID does not exist.
1161+
Some(2) => Ok(None),
1162+
_ => Err(SyscallError {
1163+
call: "bpf_task_fd_query",
1164+
io_error,
1165+
}
1166+
.into()),
1167+
};
1168+
}
1169+
};
1170+
let kind = TaskFdKind::try_from(out.fd_type as u32)?;
1171+
Ok(Some(TaskFdDetails {
1172+
id: out.prog_id,
1173+
kind,
1174+
name: out.name,
1175+
probe_addr: out.probe_addr,
1176+
probe_offset: out.probe_offset,
1177+
}))
1178+
}
1179+
1180+
/// Return the program ID.
1181+
pub fn id(&self) -> u32 {
1182+
self.id
1183+
}
1184+
1185+
/// Return the program kind.
1186+
pub fn kind(&self) -> TaskFdKind {
1187+
self.kind
1188+
}
1189+
1190+
/// Return the program name.
1191+
pub fn name(&self) -> Option<&CStr> {
1192+
self.name
1193+
}
1194+
1195+
/// Return the program probe address and offset.
1196+
pub fn address_and_offset(&self) -> Option<(u64, u64)> {
1197+
match (self.probe_addr, self.probe_offset) {
1198+
(Some(a), Some(o)) => Some((a, o)),
1199+
_ => None,
1200+
}
1201+
}
1202+
}

aya/src/sys/bpf.rs

+64-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use aya_obj::{
1818
BPF_ALU64, BPF_CALL, BPF_DW, BPF_EXIT, BPF_F_REPLACE, BPF_IMM, BPF_JMP, BPF_K, BPF_LD,
1919
BPF_MEM, BPF_MOV, BPF_PSEUDO_MAP_VALUE, BPF_ST, BPF_SUB, BPF_X, bpf_attach_type, bpf_attr,
2020
bpf_btf_info, bpf_cmd, bpf_func_id::*, bpf_insn, bpf_link_info, bpf_map_info, bpf_map_type,
21-
bpf_prog_info, bpf_prog_type, bpf_stats_type,
21+
bpf_prog_info, bpf_prog_type, bpf_stats_type, bpf_task_fd_type,
2222
},
2323
maps::{LegacyMap, bpf_map_def},
2424
};
@@ -601,6 +601,69 @@ pub(crate) fn bpf_prog_get_info_by_fd(
601601
})
602602
}
603603

604+
#[derive(Debug)]
605+
pub(crate) struct TaskFdQueryOutput<'buf> {
606+
pub(crate) prog_id: u32,
607+
pub(crate) fd_type: bpf_task_fd_type,
608+
pub(crate) name: Option<&'buf CStr>,
609+
pub(crate) probe_offset: Option<u64>,
610+
pub(crate) probe_addr: Option<u64>,
611+
}
612+
613+
pub(crate) fn bpf_task_fd_query<'buf>(
614+
pid: u32,
615+
target_fd: BorrowedFd<'_>,
616+
out_name_buf: Option<&mut [u8]>,
617+
) -> Result<TaskFdQueryOutput<'buf>, io::Error> {
618+
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
619+
620+
attr.task_fd_query.pid = pid;
621+
attr.task_fd_query.fd = target_fd.as_raw_fd() as u32;
622+
let mut out_name_buf = out_name_buf;
623+
if let Some(buf) = &mut out_name_buf {
624+
attr.task_fd_query.buf = buf.as_mut_ptr() as u64;
625+
attr.task_fd_query.buf_len = buf.len() as u32;
626+
};
627+
628+
if let Err(io_error) = unit_sys_bpf(bpf_cmd::BPF_TASK_FD_QUERY, &mut attr) {
629+
// The kernel here may leak an internal ENOTSUPP code (524), so
630+
// this needs to translate it back to POSIX-defined ENOTSUPP (95).
631+
return match io_error.raw_os_error() {
632+
Some(524) => Err(io::Error::from_raw_os_error(95)),
633+
_ => Err(io_error),
634+
};
635+
}
636+
637+
let fd_type =
638+
unsafe { std::mem::transmute::<u32, bpf_task_fd_type>(attr.task_fd_query.fd_type) };
639+
let name = out_name_buf.map(|buf| unsafe {
640+
CStr::from_bytes_with_nul_unchecked(std::slice::from_raw_parts(
641+
buf.as_ptr(),
642+
attr.task_fd_query.buf_len as usize + 1,
643+
))
644+
});
645+
let (probe_offset, probe_addr) = match fd_type {
646+
bpf_task_fd_type::BPF_FD_TYPE_KPROBE
647+
| bpf_task_fd_type::BPF_FD_TYPE_KRETPROBE
648+
| bpf_task_fd_type::BPF_FD_TYPE_UPROBE
649+
| bpf_task_fd_type::BPF_FD_TYPE_URETPROBE => unsafe {
650+
(
651+
Some(attr.task_fd_query.probe_offset),
652+
Some(attr.task_fd_query.probe_addr),
653+
)
654+
},
655+
_ => (None, None),
656+
};
657+
658+
Ok(TaskFdQueryOutput {
659+
prog_id: unsafe { attr.task_fd_query.prog_id },
660+
fd_type,
661+
name,
662+
probe_offset,
663+
probe_addr,
664+
})
665+
}
666+
604667
/// Introduced in kernel v4.13.
605668
pub(crate) fn bpf_map_get_fd_by_id(map_id: u32) -> Result<crate::MockableFd, SyscallError> {
606669
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };

0 commit comments

Comments
 (0)