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