1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
//! Error types for the aa-ebpf userspace loader.
use thiserror::Error;
/// Errors that can occur while loading, attaching, or reading eBPF programs.
#[derive(Debug, Error)]
pub enum EbpfError {
// ── aya-native variants (Linux only, used by uprobe/ringbuf) ────────
/// Failed to load the eBPF program ELF object.
#[cfg(target_os = "linux")]
#[error("failed to load eBPF object: {0}")]
Load(#[from] aya::EbpfError),
/// An eBPF map operation failed (e.g. writing to a PID filter map).
#[cfg(target_os = "linux")]
#[error("eBPF map operation failed: {0}")]
Map(#[from] aya::maps::MapError),
/// An eBPF program operation failed (e.g. load or attach).
#[cfg(target_os = "linux")]
#[error("eBPF program operation failed: {0}")]
Program(#[from] aya::programs::ProgramError),
/// Failed to attach an uprobe or kprobe to a target symbol.
#[error("failed to attach probe to `{symbol}`: {source}")]
Attach {
/// Name of the target symbol (e.g. `SSL_write`).
symbol: String,
/// Underlying error.
#[source]
source: Box<dyn std::error::Error + Send + Sync + 'static>,
},
/// The ring buffer returned an unexpected event size.
#[error("ring buffer event size mismatch: expected {expected}, got {got}")]
EventSize {
/// Expected byte length.
expected: usize,
/// Actual byte length received.
got: usize,
},
/// A required eBPF map was not found in the loaded object.
#[error("eBPF map `{name}` not found in object")]
MapNotFound {
/// Name of the missing map.
name: String,
},
/// A required eBPF program was not found in the loaded object.
#[error("eBPF program `{name}` not found in object")]
ProgramNotFound {
/// Name of the missing program.
name: String,
},
/// Insufficient permissions to load or attach eBPF programs.
#[error("permission denied: {detail}")]
PermissionDenied {
/// Human-readable description of the required capability.
detail: String,
},
/// The embedded BPF bytecode did not match the digest CI signed
/// (AAASM-3602). This is a hard, fail-closed error: a probe whose bytecode
/// has been tampered with — or is an empty stub — is refused, never loaded
/// blindly. Closes the "loads and emits plausible telemetry but is
/// deliberately blind" supply-chain scenario.
#[error("eBPF bytecode integrity check failed for `{object}`: expected sha256 {expected}, got {actual}")]
IntegrityMismatch {
/// The probe object whose digest mismatched.
object: String,
/// The pinned digest baked in at build time.
expected: String,
/// The digest actually computed over the embedded bytes.
actual: String,
},
/// OpenSSL shared library could not be located for the target process.
#[error("could not find OpenSSL library for pid {pid:?}")]
OpenSslNotFound {
/// Target PID, or `None` for system-wide search.
pid: Option<i32>,
},
/// An I/O error occurred during async ring-buffer polling or /proc parsing.
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
// ── string-based variants (used by file I/O loader, cross-platform) ─
/// Failed to load the compiled eBPF bytecode into the kernel.
#[error("eBPF program load failed: {0}")]
ProgramLoad(String),
/// Failed to attach a kprobe to the target syscall.
#[error("kprobe attach failed: {0}")]
ProbeAttach(String),
/// Failed to update a BPF map from userspace.
#[error("BPF map update failed: {0}")]
MapUpdate(String),
/// Failed to parse an event received from the BPF perf event array.
#[error("event parse failed: {0}")]
EventParse(String),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn display_program_load() {
let err = EbpfError::ProgramLoad("missing privileges".into());
assert_eq!(err.to_string(), "eBPF program load failed: missing privileges");
}
#[test]
fn display_probe_attach() {
let err = EbpfError::ProbeAttach("sys_openat not found".into());
assert_eq!(err.to_string(), "kprobe attach failed: sys_openat not found");
}
#[test]
fn display_map_update() {
let err = EbpfError::MapUpdate("map full".into());
assert_eq!(err.to_string(), "BPF map update failed: map full");
}
#[test]
fn display_event_parse() {
let err = EbpfError::EventParse("truncated buffer".into());
assert_eq!(err.to_string(), "event parse failed: truncated buffer");
}
#[test]
fn display_map_not_found() {
let err = EbpfError::MapNotFound {
name: "PID_FILTER".into(),
};
assert_eq!(err.to_string(), "eBPF map `PID_FILTER` not found in object");
}
#[test]
fn display_program_not_found() {
let err = EbpfError::ProgramNotFound {
name: "ssl_write".into(),
};
assert_eq!(err.to_string(), "eBPF program `ssl_write` not found in object");
}
#[test]
fn display_permission_denied() {
let err = EbpfError::PermissionDenied {
detail: "requires CAP_BPF".into(),
};
assert_eq!(err.to_string(), "permission denied: requires CAP_BPF");
}
#[test]
fn display_openssl_not_found() {
let err = EbpfError::OpenSslNotFound { pid: Some(1234) };
assert_eq!(err.to_string(), "could not find OpenSSL library for pid Some(1234)");
}
#[test]
fn implements_std_error() {
let err = EbpfError::ProgramLoad("test".into());
let _: &dyn std::error::Error = &err;
}
}