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