Skip to main content

starnix_core/bpf/
syscalls.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// TODO(https://github.com/rust-lang/rust/issues/39371): remove
6#![allow(non_upper_case_globals)]
7
8use super::BpfMap;
9use crate::bpf::attachments::{BpfAttachAttr, bpf_prog_attach, bpf_prog_detach};
10use crate::bpf::fs::{BpfFsDir, BpfHandle, get_bpf_object, resolve_pinned_bpf_object};
11use crate::bpf::program::{Program, ProgramInfo};
12use crate::mm::{MemoryAccessor, MemoryAccessorExt};
13use crate::security;
14use crate::task::CurrentTask;
15use crate::vfs::{
16    Anon, FdFlags, FdNumber, FileObject, LookupContext, NamespaceNode, OutputBuffer,
17    UserBuffersOutputBuffer,
18};
19use ebpf::{EbpfInstruction, MapFlags, MapSchema};
20use ebpf_api::{Map, MapError, MapKey};
21use smallvec::smallvec;
22use starnix_logging::{log_error, log_trace, track_stub};
23use starnix_sync::{Locked, Unlocked};
24use starnix_syscalls::{SUCCESS, SyscallResult};
25use starnix_types::user_buffer::UserBuffer;
26use starnix_uapi::errors::Errno;
27use starnix_uapi::open_flags::OpenFlags;
28use starnix_uapi::user_address::{UserAddress, UserCString, UserRef};
29use starnix_uapi::{
30    BPF_F_RDONLY, BPF_F_WRONLY, bpf_attr__bindgen_ty_1, bpf_attr__bindgen_ty_2,
31    bpf_attr__bindgen_ty_4, bpf_attr__bindgen_ty_5, bpf_attr__bindgen_ty_8, bpf_attr__bindgen_ty_9,
32    bpf_attr__bindgen_ty_10, bpf_attr__bindgen_ty_12, bpf_cmd, bpf_cmd_BPF_BTF_GET_FD_BY_ID,
33    bpf_cmd_BPF_BTF_GET_NEXT_ID, bpf_cmd_BPF_BTF_LOAD, bpf_cmd_BPF_ENABLE_STATS,
34    bpf_cmd_BPF_ITER_CREATE, bpf_cmd_BPF_LINK_CREATE, bpf_cmd_BPF_LINK_DETACH,
35    bpf_cmd_BPF_LINK_GET_FD_BY_ID, bpf_cmd_BPF_LINK_GET_NEXT_ID, bpf_cmd_BPF_LINK_UPDATE,
36    bpf_cmd_BPF_MAP_CREATE, bpf_cmd_BPF_MAP_DELETE_BATCH, bpf_cmd_BPF_MAP_DELETE_ELEM,
37    bpf_cmd_BPF_MAP_FREEZE, bpf_cmd_BPF_MAP_GET_FD_BY_ID, bpf_cmd_BPF_MAP_GET_NEXT_ID,
38    bpf_cmd_BPF_MAP_GET_NEXT_KEY, bpf_cmd_BPF_MAP_LOOKUP_AND_DELETE_BATCH,
39    bpf_cmd_BPF_MAP_LOOKUP_AND_DELETE_ELEM, bpf_cmd_BPF_MAP_LOOKUP_BATCH,
40    bpf_cmd_BPF_MAP_LOOKUP_ELEM, bpf_cmd_BPF_MAP_UPDATE_BATCH, bpf_cmd_BPF_MAP_UPDATE_ELEM,
41    bpf_cmd_BPF_OBJ_GET, bpf_cmd_BPF_OBJ_GET_INFO_BY_FD, bpf_cmd_BPF_OBJ_PIN,
42    bpf_cmd_BPF_PROG_ATTACH, bpf_cmd_BPF_PROG_BIND_MAP, bpf_cmd_BPF_PROG_DETACH,
43    bpf_cmd_BPF_PROG_GET_FD_BY_ID, bpf_cmd_BPF_PROG_GET_NEXT_ID, bpf_cmd_BPF_PROG_LOAD,
44    bpf_cmd_BPF_PROG_QUERY, bpf_cmd_BPF_PROG_RUN, bpf_cmd_BPF_RAW_TRACEPOINT_OPEN,
45    bpf_cmd_BPF_TASK_FD_QUERY, bpf_cmd_BPF_TOKEN_CREATE, bpf_map_info,
46    bpf_map_type_BPF_MAP_TYPE_DEVMAP, bpf_map_type_BPF_MAP_TYPE_DEVMAP_HASH, bpf_prog_info, errno,
47    error,
48};
49use zerocopy::{FromBytes, IntoBytes};
50
51/// Read the arguments for a BPF command. The ABI works like this: If the arguments struct
52/// passed is larger than the kernel knows about, the excess must be zeros. Similarly, if the
53/// arguments struct is smaller than the kernel knows about, the kernel fills the excess with
54/// zero.
55fn read_attr<Attr: FromBytes>(
56    current_task: &CurrentTask,
57    attr_addr: UserAddress,
58    attr_size: u32,
59) -> Result<Attr, Errno> {
60    let mut attr_size = attr_size as usize;
61    let sizeof_attr = std::mem::size_of::<Attr>();
62
63    // Verify that the extra is all zeros.
64    if attr_size > sizeof_attr {
65        let tail_addr = attr_addr.checked_add(sizeof_attr).ok_or_else(|| errno!(EFAULT))?;
66        let tail = current_task.read_memory_to_vec(tail_addr, attr_size - sizeof_attr)?;
67        if tail.into_iter().any(|byte| byte != 0) {
68            return error!(E2BIG);
69        }
70
71        attr_size = sizeof_attr;
72    }
73
74    // If the struct passed is smaller than our definition of the struct, let whatever is not
75    // passed be zero.
76    current_task.read_object_partial(UserRef::new(attr_addr), attr_size)
77}
78
79fn reopen_bpf_fd(
80    locked: &mut Locked<Unlocked>,
81    current_task: &CurrentTask,
82    node: NamespaceNode,
83    handle: BpfHandle,
84    open_flags: OpenFlags,
85) -> Result<SyscallResult, Errno> {
86    // All BPF FDs have the CLOEXEC flag turned on by default.
87    let file = FileObject::new(
88        locked,
89        current_task,
90        Box::new(handle),
91        node,
92        open_flags | OpenFlags::CLOEXEC,
93    )?;
94    Ok(current_task.add_file(locked, file, FdFlags::CLOEXEC)?.into())
95}
96
97fn install_bpf_fd(
98    locked: &mut Locked<Unlocked>,
99    current_task: &CurrentTask,
100    obj: impl Into<BpfHandle>,
101) -> Result<SyscallResult, Errno> {
102    let handle: BpfHandle = obj.into();
103    let name = handle.type_name();
104    handle.security_check_open_fd(current_task, None)?;
105
106    // All BPF FDs have the CLOEXEC flag turned on by default.
107    let file = Anon::new_private_file(
108        locked,
109        current_task,
110        Box::new(handle),
111        OpenFlags::RDWR | OpenFlags::CLOEXEC,
112        name,
113    );
114    Ok(current_task.add_file(locked, file, FdFlags::CLOEXEC)?.into())
115}
116
117#[derive(Debug, Clone)]
118pub struct BpfTypeFormat {
119    #[allow(dead_code)]
120    data: Vec<u8>,
121}
122
123fn read_map_key(
124    current_task: &CurrentTask,
125    addr: UserAddress,
126    key_size: u32,
127) -> Result<MapKey, Errno> {
128    let key_size = key_size as usize;
129    current_task.read_objects_to_smallvec(UserRef::<u8>::new(addr), key_size as usize)
130}
131
132fn map_error_to_errno(e: MapError) -> Errno {
133    match e {
134        MapError::InvalidParam => errno!(EINVAL),
135        MapError::InvalidKey => errno!(ENOENT),
136        MapError::EntryExists => errno!(EEXIST),
137        MapError::NoMemory => errno!(ENOMEM),
138        MapError::SizeLimit => errno!(E2BIG),
139        MapError::MapTypeNotSupported | MapError::NotSupported => errno!(ENOSYS),
140        MapError::InvalidVmo | MapError::Internal => errno!(EIO),
141    }
142}
143
144fn validate_bpf_name(name: &[u8]) -> Result<&str, Errno> {
145    let name = std::ffi::CStr::from_bytes_until_nul(name)
146        .map_err(|_| errno!(EINVAL))?
147        .to_str()
148        .map_err(|_| errno!(EINVAL))?;
149    // Only alphanumeric characters, '_' and '.' are allowed in map names (see
150    // https://docs.kernel.org/bpf/maps.html).
151    if !name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '.') {
152        return error!(EINVAL);
153    }
154    Ok(name)
155}
156
157pub fn sys_bpf(
158    locked: &mut Locked<Unlocked>,
159    current_task: &CurrentTask,
160    cmd: bpf_cmd,
161    attr_addr: UserAddress,
162    attr_size: u32,
163) -> Result<SyscallResult, Errno> {
164    // TODO: https://fxbug.dev/322874504 - Allow containers to configure the kernel's initial
165    // "unprivileged_bpf_disabled" setting, and apply capability checks appropriately.
166
167    // The best available documentation on the various BPF commands is at
168    // https://www.kernel.org/doc/html/latest/userspace-api/ebpf/syscall.html.
169    // Comments on commands are copied from there.
170
171    match cmd {
172        // Create a map and return a file descriptor that refers to the map.
173        bpf_cmd_BPF_MAP_CREATE => {
174            let map_attr: bpf_attr__bindgen_ty_1 = read_attr(current_task, attr_addr, attr_size)?;
175            log_trace!("BPF_MAP_CREATE {:?}", map_attr);
176            security::check_bpf_access(current_task, cmd, &map_attr, attr_size)?;
177
178            let map_type = map_attr.map_type;
179            let mut flags =
180                MapFlags::from_bits(map_attr.map_flags).ok_or_else(|| errno!(EINVAL))?;
181            // To quote
182            // https://cs.android.com/android/platform/superproject/+/master:system/bpf/libbpf_android/Loader.cpp;l=670;drc=28e295395471b33e662b7116378d15f1e88f0864
183            // "DEVMAPs are readonly from the bpf program side's point of view, as such the kernel
184            // in kernel/bpf/devmap.c dev_map_init_map() will set the flag"
185            if map_type == bpf_map_type_BPF_MAP_TYPE_DEVMAP
186                || map_type == bpf_map_type_BPF_MAP_TYPE_DEVMAP_HASH
187            {
188                flags |= MapFlags::ProgReadOnly;
189            }
190
191            let schema = MapSchema {
192                map_type,
193                key_size: map_attr.key_size,
194                value_size: map_attr.value_size,
195                max_entries: map_attr.max_entries,
196                flags,
197            };
198
199            let map = Map::new(schema, validate_bpf_name(map_attr.map_name.as_bytes())?)
200                .map_err(map_error_to_errno)?;
201            let map = BpfMap::new(locked, current_task, map, security::bpf_map_alloc(current_task));
202            install_bpf_fd(locked, current_task, map)
203        }
204
205        bpf_cmd_BPF_MAP_LOOKUP_ELEM => {
206            let elem_attr: bpf_attr__bindgen_ty_2 = read_attr(current_task, attr_addr, attr_size)?;
207            log_trace!("BPF_MAP_LOOKUP_ELEM");
208            security::check_bpf_access(current_task, cmd, &elem_attr, attr_size)?;
209            let map_fd = FdNumber::from_raw(elem_attr.map_fd as i32);
210            let map = get_bpf_object(current_task, map_fd)?;
211            let map = map.as_map()?;
212
213            if map.schema.flags.contains(MapFlags::SyscallWriteOnly) {
214                return error!(EPERM);
215            }
216
217            let key =
218                read_map_key(current_task, UserAddress::from(elem_attr.key), map.schema.key_size)?;
219
220            // SAFETY: this union object was created with FromBytes so it's safe to access any
221            // variant because all variants must be valid with all bit patterns.
222            let user_value = UserAddress::from(unsafe { elem_attr.__bindgen_anon_1.value });
223
224            let _suspend_lock =
225                current_task.kernel().suspend_resume_manager.acquire_ebpf_suspend_lock(locked);
226
227            let value = map.load(&key).ok_or_else(|| errno!(ENOENT))?;
228            current_task.write_memory(user_value, &value)?;
229
230            Ok(SUCCESS)
231        }
232
233        // Create or update an element (key/value pair) in a specified map.
234        bpf_cmd_BPF_MAP_UPDATE_ELEM => {
235            let elem_attr: bpf_attr__bindgen_ty_2 = read_attr(current_task, attr_addr, attr_size)?;
236            log_trace!("BPF_MAP_UPDATE_ELEM");
237            security::check_bpf_access(current_task, cmd, &elem_attr, attr_size)?;
238            let map_fd = FdNumber::from_raw(elem_attr.map_fd as i32);
239            let map = get_bpf_object(current_task, map_fd)?;
240            let map = map.as_map()?;
241
242            // Get the frozen state and keep the lock to prevent a race.
243            let (frozen, locked) = map.frozen(locked);
244
245            if *frozen || map.schema.flags.contains(MapFlags::SyscallReadOnly) {
246                return error!(EPERM);
247            }
248
249            let flags = elem_attr.flags;
250            let key =
251                read_map_key(current_task, UserAddress::from(elem_attr.key), map.schema.key_size)?;
252
253            // SAFETY: this union object was created with FromBytes so it's safe to access any
254            // variant because all variants must be valid with all bit patterns.
255            let user_value = UserAddress::from(unsafe { elem_attr.__bindgen_anon_1.value });
256            let value =
257                current_task.read_memory_to_vec(user_value, map.schema.value_size as usize)?;
258
259            let _suspend_lock =
260                current_task.kernel().suspend_resume_manager.acquire_ebpf_suspend_lock(locked);
261
262            map.update(key, &value, flags).map_err(map_error_to_errno)?;
263            Ok(SUCCESS)
264        }
265
266        bpf_cmd_BPF_MAP_DELETE_ELEM => {
267            let elem_attr: bpf_attr__bindgen_ty_2 = read_attr(current_task, attr_addr, attr_size)?;
268            log_trace!("BPF_MAP_DELETE_ELEM");
269            security::check_bpf_access(current_task, cmd, &elem_attr, attr_size)?;
270            let map_fd = FdNumber::from_raw(elem_attr.map_fd as i32);
271            let map = get_bpf_object(current_task, map_fd)?;
272            let map = map.as_map()?;
273
274            // Get the frozen state and keep the lock to prevent a race.
275            let (frozen, locked) = map.frozen(locked);
276
277            if *frozen || map.schema.flags.contains(MapFlags::SyscallReadOnly) {
278                return error!(EPERM);
279            }
280
281            let key =
282                read_map_key(current_task, UserAddress::from(elem_attr.key), map.schema.key_size)?;
283
284            let _suspend_lock =
285                current_task.kernel().suspend_resume_manager.acquire_ebpf_suspend_lock(locked);
286
287            map.delete(&key).map_err(map_error_to_errno)?;
288            Ok(SUCCESS)
289        }
290
291        // Look up an element by key in a specified map and return the key of the next element. Can
292        // be used to iterate over all elements in the map.
293        bpf_cmd_BPF_MAP_GET_NEXT_KEY => {
294            let elem_attr: bpf_attr__bindgen_ty_2 = read_attr(current_task, attr_addr, attr_size)?;
295            log_trace!("BPF_MAP_GET_NEXT_KEY");
296            security::check_bpf_access(current_task, cmd, &elem_attr, attr_size)?;
297            let map_fd = FdNumber::from_raw(elem_attr.map_fd as i32);
298            let map = get_bpf_object(current_task, map_fd)?;
299            let map = map.as_map()?;
300
301            if map.schema.flags.contains(MapFlags::SyscallWriteOnly) {
302                return error!(EPERM);
303            }
304
305            let key = if elem_attr.key != 0 {
306                Some(read_map_key(
307                    current_task,
308                    UserAddress::from(elem_attr.key),
309                    map.schema.key_size,
310                )?)
311            } else {
312                None
313            };
314
315            let next_key =
316                map.get_next_key(key.as_ref().map(|k| &k[..])).map_err(map_error_to_errno)?;
317
318            // SAFETY: this union object was created with FromBytes so it's safe to access any
319            // variant (right?)
320            let user_next_key = UserAddress::from(unsafe { elem_attr.__bindgen_anon_1.next_key });
321            current_task.write_memory(user_next_key, &next_key)?;
322
323            Ok(SUCCESS)
324        }
325
326        // Verify and load an eBPF program, returning a new file descriptor associated with the
327        // program.
328        bpf_cmd_BPF_PROG_LOAD => {
329            let prog_attr: bpf_attr__bindgen_ty_4 = read_attr(current_task, attr_addr, attr_size)?;
330            log_trace!("BPF_PROG_LOAD");
331            security::check_bpf_access(current_task, cmd, &prog_attr, attr_size)?;
332
333            let user_code = UserRef::<EbpfInstruction>::new(UserAddress::from(prog_attr.insns));
334            let code = current_task.read_objects_to_vec(user_code, prog_attr.insn_cnt as usize)?;
335
336            let mut log_buffer = if prog_attr.log_buf != 0 && prog_attr.log_size > 1 {
337                UserBuffersOutputBuffer::unified_new(
338                    current_task,
339                    smallvec![UserBuffer {
340                        address: prog_attr.log_buf.into(),
341                        length: (prog_attr.log_size - 1) as usize
342                    }],
343                )?
344            } else {
345                UserBuffersOutputBuffer::unified_new(current_task, smallvec![])?
346            };
347            let name = validate_bpf_name(prog_attr.prog_name.as_bytes())?;
348            let program = ProgramInfo::try_from(&prog_attr)
349                .and_then(|info| Program::new(locked, current_task, info, &mut log_buffer, code));
350            let program_or_stub = match program {
351                Ok(program) => BpfHandle::Program(program),
352                Err(e) => {
353                    if current_task.kernel().features.bpf_v2 {
354                        return Err(e.into());
355                    }
356                    // if bpf_v2 is not enabled, only log the error and return a stub. In the
357                    // future, return the error unconditionally.
358                    log_error!("Unable to load bpf program {name}: {e:?}");
359                    BpfHandle::ProgramStub(prog_attr.prog_type)
360                }
361            };
362            // Ensures the log buffer ends with a 0.
363            log_buffer.write(b"\0")?;
364            install_bpf_fd(locked, current_task, program_or_stub)
365        }
366
367        // Attach an eBPF program to a target_fd at the specified attach_type hook.
368        bpf_cmd_BPF_PROG_ATTACH => {
369            let attach_attr: BpfAttachAttr = read_attr(current_task, attr_addr, attr_size)?;
370            security::check_bpf_access(current_task, cmd, &attach_attr, attr_size)?;
371            bpf_prog_attach(locked, current_task, attach_attr)
372        }
373
374        // Obtain information about eBPF programs associated with the specified attach_type hook.
375        bpf_cmd_BPF_PROG_QUERY => {
376            let mut prog_attr: bpf_attr__bindgen_ty_10 =
377                read_attr(current_task, attr_addr, attr_size)?;
378            log_trace!("BPF_PROG_QUERY");
379            security::check_bpf_access(current_task, cmd, &prog_attr, attr_size)?;
380            track_stub!(TODO("https://fxbug.dev/322873416"), "Bpf::BPF_PROG_QUERY");
381            current_task.write_memory(UserAddress::from(prog_attr.prog_ids), 1.as_bytes())?;
382            prog_attr.__bindgen_anon_2.prog_cnt = std::mem::size_of::<u64>() as u32;
383            current_task.write_memory(attr_addr, prog_attr.as_bytes())?;
384            Ok(SUCCESS)
385        }
386
387        // Pin an eBPF program or map referred by the specified bpf_fd to the provided pathname on
388        // the filesystem.
389        bpf_cmd_BPF_OBJ_PIN => {
390            let result = (|| {
391                let pin_attr: bpf_attr__bindgen_ty_5 = read_attr(current_task, attr_addr, attr_size)?;
392                log_trace!("BPF_OBJ_PIN {:?}", pin_attr);
393                security::check_bpf_access(current_task, cmd, &pin_attr, attr_size)?;
394                let bpf_fd = FdNumber::from_raw(pin_attr.bpf_fd as i32);
395                let object = get_bpf_object(current_task, bpf_fd)?;
396                let path_addr = UserCString::new(current_task, UserAddress::from(pin_attr.pathname));
397                let pathname = current_task.read_path(path_addr)?;
398                let (parent, basename) = current_task.lookup_parent_at(
399                    locked,
400                    &mut LookupContext::default(),
401                    FdNumber::AT_FDCWD,
402                    pathname.as_ref(),
403                )?;
404                let bpf_dir =
405                    parent.entry.node.downcast_ops::<BpfFsDir>().ok_or_else(|| errno!(EINVAL))?;
406                bpf_dir.register_pin(locked, current_task, &parent, basename, object)?;
407                Ok(SUCCESS)
408            })();
409            // Log the error to aid debugging https://fxbug.dev/476409330.
410            if result.is_err() {
411                log_error!("BPF_OBJ_PIN failed: {:?}", result);
412            }
413            result
414        }
415
416        // Open a file descriptor for the eBPF object pinned to the specified pathname.
417        bpf_cmd_BPF_OBJ_GET => {
418            let path_attr: bpf_attr__bindgen_ty_5 = read_attr(current_task, attr_addr, attr_size)?;
419            log_trace!("BPF_OBJ_GET {:?}", path_attr);
420            security::check_bpf_access(current_task, cmd, &path_attr, attr_size)?;
421            let path_addr = UserCString::new(current_task, UserAddress::from(path_attr.pathname));
422            let open_flags = match path_attr.file_flags {
423                BPF_F_RDONLY => OpenFlags::RDONLY,
424                BPF_F_WRONLY => OpenFlags::WRONLY,
425                0 => OpenFlags::RDWR,
426                _ => return error!(EINVAL),
427            };
428            let pathname = current_task.read_path(path_addr)?;
429            let (handle, node) =
430                resolve_pinned_bpf_object(locked, current_task, pathname.as_ref(), open_flags)?;
431            reopen_bpf_fd(locked, current_task, node, handle, open_flags)
432        }
433
434        // Obtain information about the eBPF object corresponding to bpf_fd.
435        bpf_cmd_BPF_OBJ_GET_INFO_BY_FD => {
436            let mut get_info_attr: bpf_attr__bindgen_ty_9 =
437                read_attr(current_task, attr_addr, attr_size)?;
438            log_trace!("BPF_OBJ_GET_INFO_BY_FD {:?}", get_info_attr);
439            security::check_bpf_access(current_task, cmd, &get_info_attr, attr_size)?;
440            let bpf_fd = FdNumber::from_raw(get_info_attr.bpf_fd as i32);
441            let object = get_bpf_object(current_task, bpf_fd)?;
442
443            let mut info = match object {
444                BpfHandle::Map(map) => bpf_map_info {
445                    type_: map.schema.map_type,
446                    id: map.id(),
447                    key_size: map.schema.key_size,
448                    value_size: map.schema.value_size,
449                    max_entries: map.schema.max_entries,
450                    map_flags: map.schema.flags.bits(),
451                    ..Default::default()
452                }
453                .as_bytes()
454                .to_owned(),
455                BpfHandle::Program(prog) => {
456                    #[allow(unknown_lints, clippy::unnecessary_struct_initialization)]
457                    bpf_prog_info {
458                        type_: prog.info.program_type.into(),
459                        id: prog.id(),
460                        // TODO: https://fxbug.dev/397389704 - return actual length.
461                        jited_prog_len: 1,
462                        ..Default::default()
463                    }
464                    .as_bytes()
465                    .to_owned()
466                }
467                BpfHandle::ProgramStub(type_) => {
468                    #[allow(unknown_lints, clippy::unnecessary_struct_initialization)]
469                    bpf_prog_info {
470                        type_,
471                        // TODO: https://fxbug.dev/397389704 - return actual length.
472                        jited_prog_len: 1,
473                        ..Default::default()
474                    }
475                    .as_bytes()
476                    .to_owned()
477                }
478                _ => {
479                    return error!(EINVAL);
480                }
481            };
482
483            // If info_len is larger than info, write out the full length of info and write the
484            // smaller size into info_len. If info_len is smaller, truncate info.
485            // TODO(tbodt): This is just a guess for the behavior. Works with BpfSyscallWrappers.h,
486            // but could be wrong.
487            info.truncate(get_info_attr.info_len as usize);
488            get_info_attr.info_len = info.len() as u32;
489            current_task.write_memory(UserAddress::from(get_info_attr.info), &info)?;
490            current_task.write_memory(attr_addr, get_info_attr.as_bytes())?;
491            Ok(SUCCESS)
492        }
493
494        // Verify and load BPF Type Format (BTF) metadata into the kernel, returning a new file
495        // descriptor associated with the metadata. BTF is described in more detail at
496        // https://www.kernel.org/doc/html/latest/bpf/btf.html.
497        bpf_cmd_BPF_BTF_LOAD => {
498            let btf_attr: bpf_attr__bindgen_ty_12 = read_attr(current_task, attr_addr, attr_size)?;
499            log_trace!("BPF_BTF_LOAD {:?}", btf_attr);
500            security::check_bpf_access(current_task, cmd, &btf_attr, attr_size)?;
501            let data = current_task
502                .read_memory_to_vec(UserAddress::from(btf_attr.btf), btf_attr.btf_size as usize)?;
503            install_bpf_fd(locked, current_task, BpfTypeFormat { data })
504        }
505        bpf_cmd_BPF_PROG_DETACH => {
506            let attach_attr: BpfAttachAttr = read_attr(current_task, attr_addr, attr_size)?;
507            security::check_bpf_access(current_task, cmd, &attach_attr, attr_size)?;
508            bpf_prog_detach(locked, current_task, attach_attr)
509        }
510        bpf_cmd_BPF_PROG_RUN => {
511            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_PROG_RUN");
512            error!(EINVAL)
513        }
514        bpf_cmd_BPF_PROG_GET_NEXT_ID => {
515            let mut get_next_attr: bpf_attr__bindgen_ty_8 =
516                read_attr(current_task, attr_addr, attr_size)?;
517            // SAFETY: Reading u32 value from a union is safe.
518            let start_id = unsafe { get_next_attr.__bindgen_anon_1.start_id };
519            get_next_attr.next_id = current_task
520                .kernel()
521                .ebpf_state
522                .get_next_program_id(locked, start_id)
523                .ok_or_else(|| errno!(ENOENT))?;
524            current_task.write_object(UserRef::new(attr_addr), &get_next_attr)?;
525            Ok(SUCCESS)
526        }
527        bpf_cmd_BPF_MAP_GET_NEXT_ID => {
528            let mut get_next_attr: bpf_attr__bindgen_ty_8 =
529                read_attr(current_task, attr_addr, attr_size)?;
530            // SAFETY: Reading u32 value from a union is safe.
531            let start_id = unsafe { get_next_attr.__bindgen_anon_1.start_id };
532            get_next_attr.next_id = current_task
533                .kernel()
534                .ebpf_state
535                .get_next_map_id(locked, start_id)
536                .ok_or_else(|| errno!(ENOENT))?;
537            current_task.write_object(UserRef::new(attr_addr), &get_next_attr)?;
538            Ok(SUCCESS)
539        }
540        bpf_cmd_BPF_PROG_GET_FD_BY_ID => {
541            let get_by_id_attr: bpf_attr__bindgen_ty_8 =
542                read_attr(current_task, attr_addr, attr_size)?;
543            // SAFETY: Reading u32 value from a union is safe.
544            let prog_id = unsafe { get_by_id_attr.__bindgen_anon_1.prog_id };
545            let prog = current_task
546                .kernel()
547                .ebpf_state
548                .get_program_by_id(locked, prog_id)
549                .ok_or_else(|| errno!(ENOENT))?;
550            install_bpf_fd(locked, current_task, prog)
551        }
552        bpf_cmd_BPF_MAP_GET_FD_BY_ID => {
553            let get_by_id_attr: bpf_attr__bindgen_ty_8 =
554                read_attr(current_task, attr_addr, attr_size)?;
555            // SAFETY: Reading u32 value from a union is safe.
556            let map_id = unsafe { get_by_id_attr.__bindgen_anon_1.map_id };
557            let map = current_task
558                .kernel()
559                .ebpf_state
560                .get_map_by_id(locked, map_id)
561                .ok_or_else(|| errno!(ENOENT))?;
562            install_bpf_fd(locked, current_task, map)
563        }
564        bpf_cmd_BPF_RAW_TRACEPOINT_OPEN => {
565            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_RAW_TRACEPOINT_OPEN");
566            error!(EINVAL)
567        }
568        bpf_cmd_BPF_BTF_GET_FD_BY_ID => {
569            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_BTF_GET_FD_BY_ID");
570            error!(EINVAL)
571        }
572        bpf_cmd_BPF_TASK_FD_QUERY => {
573            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_TASK_FD_QUERY");
574            error!(EINVAL)
575        }
576        bpf_cmd_BPF_MAP_LOOKUP_AND_DELETE_ELEM => {
577            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_MAP_LOOKUP_AND_DELETE_ELEM");
578            error!(EINVAL)
579        }
580        bpf_cmd_BPF_MAP_FREEZE => {
581            let elem_attr: bpf_attr__bindgen_ty_2 = read_attr(current_task, attr_addr, attr_size)?;
582            log_trace!("BPF_MAP_FREEZE");
583            let map_fd = FdNumber::from_raw(elem_attr.map_fd as i32);
584            let map = get_bpf_object(current_task, map_fd)?;
585            let map = map.as_map()?;
586            map.freeze(locked)?;
587            Ok(SUCCESS)
588        }
589        bpf_cmd_BPF_BTF_GET_NEXT_ID => {
590            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_BTF_GET_NEXT_ID");
591            error!(EINVAL)
592        }
593        bpf_cmd_BPF_MAP_LOOKUP_BATCH => {
594            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_MAP_LOOKUP_BATCH");
595            error!(EINVAL)
596        }
597        bpf_cmd_BPF_MAP_LOOKUP_AND_DELETE_BATCH => {
598            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_MAP_LOOKUP_AND_DELETE_BATCH");
599            error!(EINVAL)
600        }
601        bpf_cmd_BPF_MAP_UPDATE_BATCH => {
602            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_MAP_UPDATE_BATCH");
603            error!(EINVAL)
604        }
605        bpf_cmd_BPF_MAP_DELETE_BATCH => {
606            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_MAP_DELETE_BATCH");
607            error!(EINVAL)
608        }
609        bpf_cmd_BPF_LINK_CREATE => {
610            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_LINK_CREATE");
611            error!(EINVAL)
612        }
613        bpf_cmd_BPF_LINK_UPDATE => {
614            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_LINK_UPDATE");
615            error!(EINVAL)
616        }
617        bpf_cmd_BPF_LINK_GET_FD_BY_ID => {
618            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_LINK_GET_FD_BY_ID");
619            error!(EINVAL)
620        }
621        bpf_cmd_BPF_LINK_GET_NEXT_ID => {
622            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_LINK_GET_NEXT_ID");
623            error!(EINVAL)
624        }
625        bpf_cmd_BPF_ENABLE_STATS => {
626            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_ENABLE_STATS");
627            error!(EINVAL)
628        }
629        bpf_cmd_BPF_ITER_CREATE => {
630            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_ITER_CREATE");
631            error!(EINVAL)
632        }
633        bpf_cmd_BPF_LINK_DETACH => {
634            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_LINK_DETACH");
635            error!(EINVAL)
636        }
637        bpf_cmd_BPF_PROG_BIND_MAP => {
638            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_PROG_BIND_MAP");
639            error!(EINVAL)
640        }
641        bpf_cmd_BPF_TOKEN_CREATE => {
642            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_TOKEN_CREATE");
643            error!(EINVAL)
644        }
645        _ => {
646            track_stub!(TODO("https://fxbug.dev/322874055"), "bpf", cmd);
647            error!(EINVAL)
648        }
649    }
650}
651
652// Syscalls for arch32 usage
653#[cfg(target_arch = "aarch64")]
654mod arch32 {
655    pub use super::sys_bpf as sys_arch32_bpf;
656}
657
658#[cfg(target_arch = "aarch64")]
659pub use arch32::*;