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