1#![allow(non_upper_case_globals)]
7
8use crate::bpf::syscalls::BpfTypeFormat;
9use crate::bpf::{BpfMapHandle, ProgramHandle};
10use crate::mm::memory::MemoryObject;
11use crate::mm::{DesiredAddress, MappingOptions, PAGE_SIZE, ProtectionFlags};
12use crate::security::{self, PermissionFlags};
13use crate::task::{
14 CurrentTask, EventHandler, SignalHandler, SignalHandlerInner, Task, WaitCanceler, Waiter,
15};
16use crate::vfs::buffers::{InputBuffer, OutputBuffer};
17use crate::vfs::{
18 CacheMode, CheckAccessReason, FdNumber, FileObject, FileOps, FileSystem, FileSystemHandle,
19 FileSystemOps, FileSystemOptions, FsNode, FsNodeHandle, FsNodeInfo, FsNodeOps, FsStr,
20 MemoryDirectoryFile, MemoryXattrStorage, NamespaceNode, RenameContext, XattrStorage as _,
21 default_mmap, fileops_impl_nonseekable, fileops_impl_noop_sync, fs_node_impl_not_dir,
22 fs_node_impl_xattr_delegate,
23};
24use bstr::BStr;
25use ebpf::{MapFlags, MapSchema};
26use ebpf_api::{RINGBUF_SIGNAL, compute_map_storage_size};
27use starnix_logging::track_stub;
28use starnix_types::vfs::default_statfs;
29use starnix_uapi::auth::FsCred;
30use starnix_uapi::device_id::DeviceId;
31use starnix_uapi::errors::Errno;
32use starnix_uapi::file_mode::{FileMode, mode};
33use starnix_uapi::math::round_up_to_increment;
34use starnix_uapi::open_flags::OpenFlags;
35use starnix_uapi::user_address::UserAddress;
36use starnix_uapi::vfs::FdEvents;
37use starnix_uapi::{
38 BPF_FS_MAGIC, bpf_map_type_BPF_MAP_TYPE_ARRAY, bpf_map_type_BPF_MAP_TYPE_RINGBUF, errno, error,
39 statfs,
40};
41use std::sync::Arc;
42
43#[derive(Debug, Clone)]
46pub enum BpfHandle {
47 Program(ProgramHandle),
48
49 ProgramStub(u32),
51
52 Map(BpfMapHandle),
53 BpfTypeFormat(Arc<BpfTypeFormat>),
54}
55
56impl BpfHandle {
57 pub fn as_map(&self) -> Result<&BpfMapHandle, Errno> {
58 match self {
59 Self::Map(map) => Ok(map),
60 _ => error!(EINVAL),
61 }
62 }
63 pub fn as_program(&self) -> Result<&ProgramHandle, Errno> {
64 match self {
65 Self::Program(program) => Ok(program),
66 _ => error!(EINVAL),
67 }
68 }
69
70 pub fn into_program(self) -> Result<ProgramHandle, Errno> {
71 match self {
72 Self::Program(program) => Ok(program),
73 _ => error!(EINVAL),
74 }
75 }
76
77 fn get_map_vmo(&self) -> Result<(&Arc<zx::Vmo>, MapSchema), Errno> {
79 match self {
80 Self::Map(map) => Ok((map.vmo(), map.schema)),
81 _ => error!(ENODEV),
82 }
83 }
84
85 pub fn type_name(&self) -> &'static str {
86 match self {
87 Self::Map(_) => "bpf-map",
88 Self::Program(_) | Self::ProgramStub(_) => "bpf-prog",
89 Self::BpfTypeFormat(_) => "bpf-type",
90 }
91 }
92
93 pub(super) fn security_check_open_fd(
97 &self,
98 current_task: &CurrentTask,
99 permission_flags: Option<PermissionFlags>,
100 ) -> Result<(), Errno> {
101 match self {
102 Self::Map(bpf_map) => security::check_bpf_map_access(
103 current_task,
104 &bpf_map.security_state,
105 permission_flags.unwrap_or_else(|| bpf_map.schema.flags.into()),
106 ),
107 Self::Program(program) => {
108 security::check_bpf_prog_access(current_task, &program.security_state)
109 }
110 _ => Ok(()),
111 }
112 }
113}
114
115impl From<ProgramHandle> for BpfHandle {
116 fn from(program: ProgramHandle) -> Self {
117 Self::Program(program)
118 }
119}
120
121impl From<BpfMapHandle> for BpfHandle {
122 fn from(map: BpfMapHandle) -> Self {
123 Self::Map(map)
124 }
125}
126
127impl From<BpfTypeFormat> for BpfHandle {
128 fn from(format: BpfTypeFormat) -> Self {
129 Self::BpfTypeFormat(Arc::new(format))
130 }
131}
132
133impl FileOps for BpfHandle {
134 fileops_impl_nonseekable!();
135 fileops_impl_noop_sync!();
136 fn read(
137 &self,
138 _file: &FileObject,
139 _current_task: &crate::task::CurrentTask,
140 _offset: usize,
141 _data: &mut dyn OutputBuffer,
142 ) -> Result<usize, Errno> {
143 track_stub!(TODO("https://fxbug.dev/322874229"), "bpf handle read");
144 error!(EINVAL)
145 }
146 fn write(
147 &self,
148 _file: &FileObject,
149 _current_task: &crate::task::CurrentTask,
150 _offset: usize,
151 _data: &mut dyn InputBuffer,
152 ) -> Result<usize, Errno> {
153 track_stub!(TODO("https://fxbug.dev/322873841"), "bpf handle write");
154 error!(EINVAL)
155 }
156
157 fn get_memory(
158 &self,
159 _file: &FileObject,
160 _current_task: &CurrentTask,
161 length: Option<usize>,
162 prot: ProtectionFlags,
163 ) -> Result<Arc<MemoryObject>, Errno> {
164 let (vmo, schema) = self.get_map_vmo()?;
165
166 let length = length.ok_or_else(|| errno!(EINVAL))?;
168
169 if prot.contains(ProtectionFlags::EXEC) {
171 return error!(EPERM);
172 }
173
174 match schema.map_type {
175 bpf_map_type_BPF_MAP_TYPE_RINGBUF => {
176 let page_size = *PAGE_SIZE as usize;
177 if length > page_size {
179 if prot.contains(ProtectionFlags::WRITE) {
180 return error!(EPERM);
181 }
182 if length > 2 * page_size + 2 * schema.max_entries as usize {
184 return error!(EINVAL);
185 }
186 }
187
188 self.as_map()?.get_memory(|| {
189 let clone_size = 2 * page_size + schema.max_entries as usize;
193 let vmo_dup = vmo
194 .create_child(
195 zx::VmoChildOptions::SLICE,
196 page_size as u64,
197 clone_size as u64,
198 )
199 .map_err(|_| errno!(EIO))?
200 .into();
201 Ok(Arc::new(MemoryObject::RingBuf(vmo_dup)))
202 })
203 }
204
205 bpf_map_type_BPF_MAP_TYPE_ARRAY => {
206 if !schema.flags.contains(MapFlags::Mmapable) {
207 return error!(EPERM);
208 }
209
210 let array_size = round_up_to_increment(
211 compute_map_storage_size(&schema).map_err(|_| errno!(EINVAL))?,
212 *PAGE_SIZE as usize,
213 )?;
214 if length > array_size {
215 return error!(EINVAL);
216 }
217
218 self.as_map()?.get_memory(|| {
219 let vmo_dup: zx::Vmo = vmo
220 .as_handle_ref()
221 .duplicate_handle(zx::Rights::SAME_RIGHTS)
222 .map_err(|_| errno!(EIO))?
223 .into();
224 Ok(Arc::new(MemoryObject::from(vmo_dup)))
225 })
226 }
227
228 _ => error!(ENODEV),
230 }
231 }
232
233 fn mmap(
234 &self,
235 file: &FileObject,
236 current_task: &CurrentTask,
237 addr: DesiredAddress,
238 memory_offset: u64,
239 length: usize,
240 prot_flags: ProtectionFlags,
241 options: MappingOptions,
242 filename: NamespaceNode,
243 ) -> Result<UserAddress, Errno> {
244 let BpfHandle::Map(bpf_map) = &self else {
245 return error!(EINVAL);
246 };
247 security::check_bpf_map_access(
248 current_task,
249 &bpf_map.security_state,
250 PermissionFlags::READ | PermissionFlags::WRITE,
251 )?;
252 default_mmap(file, current_task, addr, memory_offset, length, prot_flags, options, filename)
253 }
254
255 fn wait_async(
256 &self,
257 _file: &FileObject,
258 _current_task: &CurrentTask,
259 waiter: &Waiter,
260 events: FdEvents,
261 handler: EventHandler,
262 ) -> Option<WaitCanceler> {
263 let (vmo, schema) = self.get_map_vmo().ok()?;
264
265 if schema.map_type != bpf_map_type_BPF_MAP_TYPE_RINGBUF
267 || !events.contains(FdEvents::POLLIN)
268 {
269 return Some(WaitCanceler::new_noop());
270 }
271
272 let handler = SignalHandler {
273 inner: SignalHandlerInner::ZxHandle(|signals| {
274 if signals.contains(RINGBUF_SIGNAL) { FdEvents::POLLIN } else { FdEvents::empty() }
275 }),
276 event_handler: handler,
277 err_code: None,
278 };
279
280 vmo.as_handle_ref()
283 .signal(RINGBUF_SIGNAL, zx::Signals::empty())
284 .expect("Failed to set signal or a ring buffer VMO");
285
286 let canceler = waiter
287 .wake_on_zircon_signals(&vmo.as_handle_ref(), RINGBUF_SIGNAL, handler)
288 .expect("Failed to wait for signals on ringbuf VMO");
289 Some(WaitCanceler::new_port(canceler))
290 }
291
292 fn query_events(
293 &self,
294 _file: &FileObject,
295 _current_task: &CurrentTask,
296 ) -> Result<FdEvents, Errno> {
297 match self {
298 Self::Map(map) => {
299 let events = match map.can_read() {
300 Some(true) => FdEvents::POLLIN,
301 Some(false) => FdEvents::empty(),
302 None => FdEvents::POLLERR,
303 };
304 Ok(events)
305 }
306 _ => error!(EPERM),
307 }
308 }
309}
310
311pub fn get_bpf_object(task: &Task, fd: FdNumber) -> Result<BpfHandle, Errno> {
312 Ok((*task.files()?.get(fd)?.downcast_file::<BpfHandle>().ok_or_else(|| errno!(EBADF))?).clone())
313}
314pub struct BpfFs;
315impl BpfFs {
316 pub fn new_fs(
317 current_task: &CurrentTask,
318 options: FileSystemOptions,
319 ) -> Result<FileSystemHandle, Errno> {
320 let kernel = current_task.kernel();
321 let fs = FileSystem::new(kernel, CacheMode::Permanent, BpfFs, options)?;
322 let root_ino = fs.allocate_ino();
323 fs.create_root_with_info(
324 root_ino,
325 BpfFsDir::new(),
326 FsNodeInfo::new(mode!(IFDIR, 0o777) | FileMode::ISVTX, FsCred::root()),
327 );
328 Ok(fs)
329 }
330}
331
332impl FileSystemOps for BpfFs {
333 fn statfs(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<statfs, Errno> {
334 Ok(default_statfs(BPF_FS_MAGIC))
335 }
336 fn name(&self) -> &'static FsStr {
337 "bpf".into()
338 }
339
340 fn rename(
341 &self,
342 _fs: &FileSystem,
343 _current_task: &CurrentTask,
344 _context: &mut RenameContext<'_>,
345 _old_name: &FsStr,
346 _new_name: &FsStr,
347 ) -> Result<(), Errno> {
348 Ok(())
349 }
350}
351
352pub struct BpfFsDir {
353 xattrs: MemoryXattrStorage,
354}
355
356impl BpfFsDir {
357 fn new() -> Self {
358 Self { xattrs: MemoryXattrStorage::default() }
359 }
360
361 pub fn register_pin(
362 &self,
363 current_task: &CurrentTask,
364 node: &NamespaceNode,
365 name: &FsStr,
366 object: BpfHandle,
367 ) -> Result<(), Errno> {
368 node.entry.create_entry(current_task, &node.mount, name, |dir, _mount, _name| {
369 Ok(dir.fs().create_node_and_allocate_node_id(
370 BpfFsObject::new(object),
371 FsNodeInfo::new(mode!(IFREG, 0o600), current_task.current_fscred()),
372 ))
373 })?;
374 Ok(())
375 }
376}
377
378impl FsNodeOps for BpfFsDir {
379 fs_node_impl_xattr_delegate!(self, self.xattrs);
380
381 fn create_file_ops(
382 &self,
383 _node: &FsNode,
384 _current_task: &CurrentTask,
385 _flags: OpenFlags,
386 ) -> Result<Box<dyn FileOps>, Errno> {
387 Ok(Box::new(MemoryDirectoryFile::new()))
388 }
389
390 fn mkdir(
391 &self,
392 node: &FsNode,
393 _current_task: &CurrentTask,
394 _name: &FsStr,
395 mode: FileMode,
396 owner: FsCred,
397 ) -> Result<FsNodeHandle, Errno> {
398 Ok(node.fs().create_node_and_allocate_node_id(
399 BpfFsDir::new(),
400 FsNodeInfo::new(mode | FileMode::ISVTX, owner),
401 ))
402 }
403
404 fn mknod(
405 &self,
406 _node: &FsNode,
407 _current_task: &CurrentTask,
408 _name: &FsStr,
409 _mode: FileMode,
410 _dev: DeviceId,
411 _owner: FsCred,
412 ) -> Result<FsNodeHandle, Errno> {
413 error!(EPERM)
414 }
415
416 fn create_symlink(
417 &self,
418 _node: &FsNode,
419 _current_task: &CurrentTask,
420 _name: &FsStr,
421 _target: &FsStr,
422 _owner: FsCred,
423 ) -> Result<FsNodeHandle, Errno> {
424 error!(EPERM)
425 }
426
427 fn link(
428 &self,
429 _node: &FsNode,
430 _current_task: &CurrentTask,
431 _name: &FsStr,
432 _child: &FsNodeHandle,
433 ) -> Result<(), Errno> {
434 Ok(())
435 }
436
437 fn unlink(
438 &self,
439 _node: &FsNode,
440 _current_task: &CurrentTask,
441 _name: &FsStr,
442 _child: &FsNodeHandle,
443 ) -> Result<(), Errno> {
444 Ok(())
445 }
446}
447
448pub struct BpfFsObject {
449 pub handle: BpfHandle,
450 xattrs: MemoryXattrStorage,
451}
452
453impl BpfFsObject {
454 fn new(handle: BpfHandle) -> Self {
455 Self { handle, xattrs: MemoryXattrStorage::default() }
456 }
457}
458
459impl FsNodeOps for BpfFsObject {
460 fs_node_impl_not_dir!();
461 fs_node_impl_xattr_delegate!(self, self.xattrs);
462
463 fn create_file_ops(
464 &self,
465 _node: &FsNode,
466 _current_task: &CurrentTask,
467 _flags: OpenFlags,
468 ) -> Result<Box<dyn FileOps>, Errno> {
469 error!(EIO)
470 }
471}
472
473pub fn resolve_pinned_bpf_object(
477 current_task: &CurrentTask,
478 path: &BStr,
479 open_flags: OpenFlags,
480) -> Result<BpfHandle, Errno> {
481 let node = current_task.lookup_path_from_root(path.as_ref())?;
482
483 let permission_flags = PermissionFlags::from(open_flags);
484 node.check_access(current_task, permission_flags, CheckAccessReason::Access)?;
485
486 let object = node.entry.node.downcast_ops::<BpfFsObject>().ok_or_else(|| errno!(EPERM))?;
487 object.handle.security_check_open_fd(current_task, Some(permission_flags))?;
488
489 if !open_flags.contains(OpenFlags::NOATIME) {
490 node.update_atime();
491 }
492
493 Ok(object.handle.clone())
494}