1use crate::bpf::fs::get_bpf_object;
6use crate::bpf::{BpfMapHandle, check_cap_or_sys_admin};
7use crate::security;
8use crate::task::{CurrentTask, Kernel, register_delayed_release};
9use crate::vfs::{FdNumber, OutputBuffer};
10use ebpf::{
11 BPF_LDDW, BPF_PSEUDO_BTF_ID, BPF_PSEUDO_FUNC, BPF_PSEUDO_MAP_FD, BPF_PSEUDO_MAP_IDX,
12 BPF_PSEUDO_MAP_IDX_VALUE, BPF_PSEUDO_MAP_VALUE, EbpfError, EbpfInstruction, EbpfProgram,
13 EbpfProgramContext, StaticHelperSet, VerifiedEbpfProgram, VerifierError, VerifierLogger,
14 link_program, verify_program,
15};
16use ebpf_api::{AttachType, EbpfApiError, MapsContext, PinnedMap, ProgramType, StructId};
17use fidl_fuchsia_ebpf as febpf;
18use starnix_lifecycle::{AtomicCounter, ObjectReleaser, ReleaserAction};
19use starnix_logging::{log_warn, track_stub};
20use starnix_types::ownership::{Releasable, ReleaseGuard};
21use starnix_uapi::auth::{CAP_BPF, CAP_NET_ADMIN, CAP_PERFMON};
22use starnix_uapi::errors::{Errno, ErrnoCode};
23use starnix_uapi::{bpf_attr__bindgen_ty_4, errno, error};
24use std::sync::{Arc, Weak};
25
26#[derive(Clone, Debug)]
27pub struct ProgramInfo {
28 pub program_type: ProgramType,
29 pub expected_attach_type: AttachType,
30}
31
32impl TryFrom<&bpf_attr__bindgen_ty_4> for ProgramInfo {
33 type Error = Errno;
34
35 fn try_from(info: &bpf_attr__bindgen_ty_4) -> Result<Self, Self::Error> {
36 Ok(Self {
37 program_type: info.prog_type.try_into().map_err(map_ebpf_api_error)?,
38 expected_attach_type: info.expected_attach_type.into(),
39 })
40 }
41}
42pub type ProgramId = u32;
43
44static NEXT_PROGRAM_ID: AtomicCounter<u32> = AtomicCounter::<u32>::new_const(1);
45fn new_program_id() -> ProgramId {
46 NEXT_PROGRAM_ID.next()
47}
48
49#[derive(Debug)]
50pub struct Program {
51 pub info: ProgramInfo,
53
54 program: VerifiedEbpfProgram,
56
57 maps: Vec<BpfMapHandle>,
60
61 id: ProgramId,
64
65 fidl_handle: febpf::ProgramHandle,
67
68 fidl_id: febpf::ProgramId,
70
71 #[allow(dead_code)]
74 service_handle: zx::EventPair,
75
76 kernel: Weak<Kernel>,
78
79 pub security_state: security::BpfProgState,
81}
82
83fn map_ebpf_api_error(e: EbpfApiError) -> Errno {
84 match e {
85 EbpfApiError::InvalidProgramType(_) | EbpfApiError::InvalidExpectedAttachType(_) => {
86 errno!(EINVAL, e)
87 }
88 EbpfApiError::UnsupportedProgramType(_) => errno!(ENOTSUP, e),
89 }
90}
91
92#[track_caller]
93fn verifier_error_to_errno(e: VerifierError) -> Errno {
94 Errno::with_context(ErrnoCode::from_error_code(e.errno() as i16), e)
95}
96
97fn map_ebpf_error(e: EbpfError) -> Errno {
98 match e {
99 EbpfError::ProgramVerifyError(v) => verifier_error_to_errno(v),
100 _ => errno!(EINVAL, e),
101 }
102}
103
104impl Program {
105 pub fn new(
106 current_task: &CurrentTask,
107 info: ProgramInfo,
108 logger: &mut dyn OutputBuffer,
109 mut code: Vec<EbpfInstruction>,
110 ) -> Result<ProgramHandle, Errno> {
111 let maps = link_maps_fds(current_task, &mut code)?;
112 let maps_schema = maps.iter().map(|m| m.schema).collect();
113 let mut logger = BufferVeriferLogger::new(logger);
114 let calling_context = info
115 .program_type
116 .create_calling_context(info.expected_attach_type, maps_schema)
117 .map_err(map_ebpf_api_error)?;
118 let program =
119 verify_program(code, calling_context, &mut logger).map_err(verifier_error_to_errno)?;
120
121 let (fidl_handle, service_handle) = zx::EventPair::create();
122 let fidl_id =
123 febpf::ProgramId { id: fidl_handle.koid().expect("Failed to get koid").raw_koid() };
124 let fidl_handle = febpf::ProgramHandle { handle: fidl_handle };
125
126 let program = ProgramHandle::new(
127 Self {
128 info,
129 program,
130 maps,
131 id: new_program_id(),
132 fidl_handle,
133 fidl_id,
134 service_handle,
135 kernel: Arc::downgrade(current_task.kernel()),
136 security_state: security::bpf_prog_alloc(current_task),
137 }
138 .into(),
139 );
140 current_task.kernel().ebpf_state.register_program(&program);
141
142 Ok(program)
143 }
144
145 pub fn id(&self) -> ProgramId {
146 self.id
147 }
148
149 pub fn link<C: EbpfProgramContext<Map = PinnedMap> + StaticHelperSet>(
150 &self,
151 program_type: ProgramType,
152 ) -> Result<EbpfProgram<C>, Errno>
153 where
154 for<'a> C::RunContext<'a>: MapsContext<'a>,
155 {
156 if program_type != self.info.program_type {
157 return error!(EINVAL);
158 }
159
160 let maps = self.maps.iter().map(|map| map.get_inner()).collect();
161 let program = link_program(&self.program, maps).map_err(map_ebpf_error)?;
162
163 Ok(program)
164 }
165
166 pub(super) fn check_load_access(
167 current_task: &CurrentTask,
168 info: &ProgramInfo,
169 ) -> Result<(), Errno> {
170 if matches!(info.program_type, ProgramType::CgroupSkb | ProgramType::SocketFilter)
171 && current_task.kernel().allow_unprivileged_bpf()
172 {
173 return Ok(());
174 }
175
176 check_cap_or_sys_admin(current_task, CAP_BPF)?;
177
178 match info.program_type {
179 ProgramType::Kprobe
181 | ProgramType::Tracepoint
182 | ProgramType::PerfEvent
183 | ProgramType::RawTracepoint
184 | ProgramType::RawTracepointWritable
185 | ProgramType::Tracing => check_cap_or_sys_admin(current_task, CAP_PERFMON),
186
187 ProgramType::SchedCls
189 | ProgramType::SchedAct
190 | ProgramType::Xdp
191 | ProgramType::SockOps
192 | ProgramType::SkSkb
193 | ProgramType::SkMsg
194 | ProgramType::SkLookup
195 | ProgramType::SkReuseport
196 | ProgramType::FlowDissector
197 | ProgramType::Netfilter => check_cap_or_sys_admin(current_task, CAP_NET_ADMIN),
198
199 ProgramType::CgroupDevice
201 | ProgramType::CgroupSkb
202 | ProgramType::CgroupSock
203 | ProgramType::CgroupSockAddr
204 | ProgramType::CgroupSockopt
205 | ProgramType::CgroupSysctl
206 | ProgramType::Ext
207 | ProgramType::LircMode2
208 | ProgramType::Lsm
209 | ProgramType::LwtIn
210 | ProgramType::LwtOut
211 | ProgramType::LwtSeg6Local
212 | ProgramType::LwtXmit
213 | ProgramType::SocketFilter
214 | ProgramType::StructOps
215 | ProgramType::Syscall
216 | ProgramType::Unspec => Ok(()),
217 }
218 }
219
220 pub fn fidl_id(&self) -> febpf::ProgramId {
221 self.fidl_id
222 }
223
224 pub fn fidl_handle(&self) -> febpf::ProgramHandle {
225 let handle = self
226 .fidl_handle
227 .handle
228 .duplicate_handle(zx::Rights::TRANSFER | zx::Rights::SIGNAL | zx::Rights::WAIT)
229 .expect("Failed to duplicate handle");
230 febpf::ProgramHandle { handle }
231 }
232}
233
234impl Releasable for Program {
235 type Context<'a> = &'a CurrentTask;
236
237 fn release<'a>(self, _current_task: &'a CurrentTask) {
238 if let Some(kernel) = self.kernel.upgrade() {
239 kernel.ebpf_state.unregister_program(self.id);
240 }
241
242 self.fidl_handle
245 .handle
246 .signal(
247 zx::Signals::NONE,
248 zx::Signals::from_bits_truncate(febpf::PROGRAM_DEFUNCT_SIGNAL),
249 )
250 .unwrap();
251 }
252}
253
254pub enum ProgramReleaserAction {}
255impl ReleaserAction<Program> for ProgramReleaserAction {
256 fn release(program: ReleaseGuard<Program>) {
257 register_delayed_release(program);
258 }
259}
260pub type ProgramReleaser = ObjectReleaser<Program, ProgramReleaserAction>;
261pub type ProgramHandle = Arc<ProgramReleaser>;
262pub type WeakProgramHandle = Weak<ProgramReleaser>;
263
264impl TryFrom<&Program> for febpf::VerifiedProgram {
265 type Error = Errno;
266
267 fn try_from(program: &Program) -> Result<febpf::VerifiedProgram, Errno> {
268 let mut maps = Vec::with_capacity(program.maps.len());
269 for map in program.maps.iter() {
270 maps.push(map.share().map_err(|_| errno!(EIO))?);
271 }
272
273 let code_u64: &[u64] = zerocopy::transmute_ref!(program.program.code());
274
275 let mut struct_access_instructions =
276 Vec::with_capacity(program.program.struct_access_instructions().len());
277 for v in program.program.struct_access_instructions() {
278 let struct_id = StructId::try_from(&v.memory_id).map_err(|()| errno!(EINVAL))?.into();
279 struct_access_instructions.push(febpf::StructAccess {
280 pc: v.pc.try_into().unwrap(),
281 struct_id,
282 field_offset: v.field_offset.try_into().unwrap(),
283 is_32_bit_ptr_load: v.is_32_bit_ptr_load,
284 })
285 }
286 Ok(febpf::VerifiedProgram {
287 code: Some(code_u64.to_vec()),
288 struct_access_instructions: Some(struct_access_instructions),
289 maps: Some(maps),
290 ..Default::default()
291 })
292 }
293}
294
295fn link_maps_fds(
297 current_task: &CurrentTask,
298 code: &mut Vec<EbpfInstruction>,
299) -> Result<Vec<BpfMapHandle>, Errno> {
300 let code_len = code.len();
301 let mut maps = Vec::<BpfMapHandle>::new();
302 for (pc, instruction) in code.iter_mut().enumerate() {
303 if instruction.code() == BPF_LDDW {
304 if pc >= code_len - 1 {
306 return error!(EINVAL);
307 }
308
309 match instruction.src_reg() {
310 0 => {}
311 BPF_PSEUDO_MAP_FD | BPF_PSEUDO_MAP_VALUE => {
312 let lddw_type = if instruction.src_reg() == BPF_PSEUDO_MAP_FD {
313 BPF_PSEUDO_MAP_IDX
314 } else {
315 BPF_PSEUDO_MAP_IDX_VALUE
316 };
317 instruction.set_src_reg(lddw_type);
320
321 let fd = FdNumber::from_raw(instruction.imm());
322 let object = get_bpf_object(current_task, fd)?;
323 let map: &BpfMapHandle = object.as_map()?;
324
325 let maybe_index = maps.iter().position(|v| Arc::ptr_eq(v, map));
327 let index = match maybe_index {
328 Some(index) => index,
329 None => {
330 let index = maps.len();
331 maps.push(map.clone());
332 index
333 }
334 };
335
336 instruction.set_imm(index.try_into().unwrap());
337 }
338 BPF_PSEUDO_MAP_IDX
339 | BPF_PSEUDO_MAP_IDX_VALUE
340 | BPF_PSEUDO_BTF_ID
341 | BPF_PSEUDO_FUNC => {
342 track_stub!(
343 TODO("https://fxbug.dev/378564467"),
344 "unsupported pseudo src for ldimm64",
345 instruction.src_reg()
346 );
347 return error!(ENOTSUP);
348 }
349 _ => {
350 return error!(EINVAL);
351 }
352 }
353 }
354 }
355 Ok(maps)
356}
357
358struct BufferVeriferLogger<'a> {
359 buffer: &'a mut dyn OutputBuffer,
360 full: bool,
361}
362
363impl BufferVeriferLogger<'_> {
364 fn new<'a>(buffer: &'a mut dyn OutputBuffer) -> BufferVeriferLogger<'a> {
365 let full = buffer.available() == 0;
366 BufferVeriferLogger { buffer, full }
367 }
368}
369
370impl VerifierLogger for BufferVeriferLogger<'_> {
371 fn is_enabled(&self) -> bool {
372 !self.full
373 }
374
375 fn log(&mut self, line: &[u8]) {
376 debug_assert!(line.is_ascii());
377
378 if self.full {
379 return;
380 }
381 if line.len() + 1 > self.buffer.available() {
382 self.full = true;
383 return;
384 }
385 match self.buffer.write(line) {
386 Err(e) => {
387 log_warn!("Unable to write verifier log: {e:?}");
388 self.full = true;
389 }
390 _ => {}
391 }
392 match self.buffer.write(b"\n") {
393 Err(e) => {
394 log_warn!("Unable to write verifier log: {e:?}");
395 self.full = true;
396 }
397 _ => {}
398 }
399 if self.buffer.available() == 0 {
400 self.full = true;
401 }
402 }
403}