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 pin_attr: bpf_attr__bindgen_ty_5 = read_attr(current_task, attr_addr, attr_size)?;
391            log_trace!("BPF_OBJ_PIN {:?}", pin_attr);
392            security::check_bpf_access(current_task, cmd, &pin_attr, attr_size)?;
393            let bpf_fd = FdNumber::from_raw(pin_attr.bpf_fd as i32);
394            let object = get_bpf_object(current_task, bpf_fd)?;
395            let path_addr = UserCString::new(current_task, UserAddress::from(pin_attr.pathname));
396            let pathname = current_task.read_path(path_addr)?;
397            let (parent, basename) = current_task.lookup_parent_at(
398                locked,
399                &mut LookupContext::default(),
400                FdNumber::AT_FDCWD,
401                pathname.as_ref(),
402            )?;
403            let bpf_dir =
404                parent.entry.node.downcast_ops::<BpfFsDir>().ok_or_else(|| errno!(EINVAL))?;
405            bpf_dir.register_pin(locked, current_task, &parent, basename, object)?;
406            Ok(SUCCESS)
407        }
408
409        // Open a file descriptor for the eBPF object pinned to the specified pathname.
410        bpf_cmd_BPF_OBJ_GET => {
411            let path_attr: bpf_attr__bindgen_ty_5 = read_attr(current_task, attr_addr, attr_size)?;
412            log_trace!("BPF_OBJ_GET {:?}", path_attr);
413            security::check_bpf_access(current_task, cmd, &path_attr, attr_size)?;
414            let path_addr = UserCString::new(current_task, UserAddress::from(path_attr.pathname));
415            let open_flags = match path_attr.file_flags {
416                BPF_F_RDONLY => OpenFlags::RDONLY,
417                BPF_F_WRONLY => OpenFlags::WRONLY,
418                0 => OpenFlags::RDWR,
419                _ => return error!(EINVAL),
420            };
421            let pathname = current_task.read_path(path_addr)?;
422            let (handle, node) =
423                resolve_pinned_bpf_object(locked, current_task, pathname.as_ref(), open_flags)?;
424            reopen_bpf_fd(locked, current_task, node, handle, open_flags)
425        }
426
427        // Obtain information about the eBPF object corresponding to bpf_fd.
428        bpf_cmd_BPF_OBJ_GET_INFO_BY_FD => {
429            let mut get_info_attr: bpf_attr__bindgen_ty_9 =
430                read_attr(current_task, attr_addr, attr_size)?;
431            log_trace!("BPF_OBJ_GET_INFO_BY_FD {:?}", get_info_attr);
432            security::check_bpf_access(current_task, cmd, &get_info_attr, attr_size)?;
433            let bpf_fd = FdNumber::from_raw(get_info_attr.bpf_fd as i32);
434            let object = get_bpf_object(current_task, bpf_fd)?;
435
436            let mut info = match object {
437                BpfHandle::Map(map) => bpf_map_info {
438                    type_: map.schema.map_type,
439                    id: map.id(),
440                    key_size: map.schema.key_size,
441                    value_size: map.schema.value_size,
442                    max_entries: map.schema.max_entries,
443                    map_flags: map.schema.flags.bits(),
444                    ..Default::default()
445                }
446                .as_bytes()
447                .to_owned(),
448                BpfHandle::Program(prog) => {
449                    #[allow(unknown_lints, clippy::unnecessary_struct_initialization)]
450                    bpf_prog_info {
451                        type_: prog.info.program_type.into(),
452                        id: prog.id(),
453                        // TODO: https://fxbug.dev/397389704 - return actual length.
454                        jited_prog_len: 1,
455                        ..Default::default()
456                    }
457                    .as_bytes()
458                    .to_owned()
459                }
460                BpfHandle::ProgramStub(type_) => {
461                    #[allow(unknown_lints, clippy::unnecessary_struct_initialization)]
462                    bpf_prog_info {
463                        type_,
464                        // TODO: https://fxbug.dev/397389704 - return actual length.
465                        jited_prog_len: 1,
466                        ..Default::default()
467                    }
468                    .as_bytes()
469                    .to_owned()
470                }
471                _ => {
472                    return error!(EINVAL);
473                }
474            };
475
476            // If info_len is larger than info, write out the full length of info and write the
477            // smaller size into info_len. If info_len is smaller, truncate info.
478            // TODO(tbodt): This is just a guess for the behavior. Works with BpfSyscallWrappers.h,
479            // but could be wrong.
480            info.truncate(get_info_attr.info_len as usize);
481            get_info_attr.info_len = info.len() as u32;
482            current_task.write_memory(UserAddress::from(get_info_attr.info), &info)?;
483            current_task.write_memory(attr_addr, get_info_attr.as_bytes())?;
484            Ok(SUCCESS)
485        }
486
487        // Verify and load BPF Type Format (BTF) metadata into the kernel, returning a new file
488        // descriptor associated with the metadata. BTF is described in more detail at
489        // https://www.kernel.org/doc/html/latest/bpf/btf.html.
490        bpf_cmd_BPF_BTF_LOAD => {
491            let btf_attr: bpf_attr__bindgen_ty_12 = read_attr(current_task, attr_addr, attr_size)?;
492            log_trace!("BPF_BTF_LOAD {:?}", btf_attr);
493            security::check_bpf_access(current_task, cmd, &btf_attr, attr_size)?;
494            let data = current_task
495                .read_memory_to_vec(UserAddress::from(btf_attr.btf), btf_attr.btf_size as usize)?;
496            install_bpf_fd(locked, current_task, BpfTypeFormat { data })
497        }
498        bpf_cmd_BPF_PROG_DETACH => {
499            let attach_attr: BpfAttachAttr = read_attr(current_task, attr_addr, attr_size)?;
500            security::check_bpf_access(current_task, cmd, &attach_attr, attr_size)?;
501            bpf_prog_detach(locked, current_task, attach_attr)
502        }
503        bpf_cmd_BPF_PROG_RUN => {
504            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_PROG_RUN");
505            error!(EINVAL)
506        }
507        bpf_cmd_BPF_PROG_GET_NEXT_ID => {
508            let mut get_next_attr: bpf_attr__bindgen_ty_8 =
509                read_attr(current_task, attr_addr, attr_size)?;
510            // SAFETY: Reading u32 value from a union is safe.
511            let start_id = unsafe { get_next_attr.__bindgen_anon_1.start_id };
512            get_next_attr.next_id = current_task
513                .kernel()
514                .ebpf_state
515                .get_next_program_id(locked, start_id)
516                .ok_or_else(|| errno!(ENOENT))?;
517            current_task.write_object(UserRef::new(attr_addr), &get_next_attr)?;
518            Ok(SUCCESS)
519        }
520        bpf_cmd_BPF_MAP_GET_NEXT_ID => {
521            let mut get_next_attr: bpf_attr__bindgen_ty_8 =
522                read_attr(current_task, attr_addr, attr_size)?;
523            // SAFETY: Reading u32 value from a union is safe.
524            let start_id = unsafe { get_next_attr.__bindgen_anon_1.start_id };
525            get_next_attr.next_id = current_task
526                .kernel()
527                .ebpf_state
528                .get_next_map_id(locked, start_id)
529                .ok_or_else(|| errno!(ENOENT))?;
530            current_task.write_object(UserRef::new(attr_addr), &get_next_attr)?;
531            Ok(SUCCESS)
532        }
533        bpf_cmd_BPF_PROG_GET_FD_BY_ID => {
534            let get_by_id_attr: bpf_attr__bindgen_ty_8 =
535                read_attr(current_task, attr_addr, attr_size)?;
536            // SAFETY: Reading u32 value from a union is safe.
537            let prog_id = unsafe { get_by_id_attr.__bindgen_anon_1.prog_id };
538            let prog = current_task
539                .kernel()
540                .ebpf_state
541                .get_program_by_id(locked, prog_id)
542                .ok_or_else(|| errno!(ENOENT))?;
543            install_bpf_fd(locked, current_task, prog)
544        }
545        bpf_cmd_BPF_MAP_GET_FD_BY_ID => {
546            let get_by_id_attr: bpf_attr__bindgen_ty_8 =
547                read_attr(current_task, attr_addr, attr_size)?;
548            // SAFETY: Reading u32 value from a union is safe.
549            let map_id = unsafe { get_by_id_attr.__bindgen_anon_1.map_id };
550            let map = current_task
551                .kernel()
552                .ebpf_state
553                .get_map_by_id(locked, map_id)
554                .ok_or_else(|| errno!(ENOENT))?;
555            install_bpf_fd(locked, current_task, map)
556        }
557        bpf_cmd_BPF_RAW_TRACEPOINT_OPEN => {
558            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_RAW_TRACEPOINT_OPEN");
559            error!(EINVAL)
560        }
561        bpf_cmd_BPF_BTF_GET_FD_BY_ID => {
562            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_BTF_GET_FD_BY_ID");
563            error!(EINVAL)
564        }
565        bpf_cmd_BPF_TASK_FD_QUERY => {
566            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_TASK_FD_QUERY");
567            error!(EINVAL)
568        }
569        bpf_cmd_BPF_MAP_LOOKUP_AND_DELETE_ELEM => {
570            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_MAP_LOOKUP_AND_DELETE_ELEM");
571            error!(EINVAL)
572        }
573        bpf_cmd_BPF_MAP_FREEZE => {
574            let elem_attr: bpf_attr__bindgen_ty_2 = read_attr(current_task, attr_addr, attr_size)?;
575            log_trace!("BPF_MAP_FREEZE");
576            let map_fd = FdNumber::from_raw(elem_attr.map_fd as i32);
577            let map = get_bpf_object(current_task, map_fd)?;
578            let map = map.as_map()?;
579            map.freeze(locked)?;
580            Ok(SUCCESS)
581        }
582        bpf_cmd_BPF_BTF_GET_NEXT_ID => {
583            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_BTF_GET_NEXT_ID");
584            error!(EINVAL)
585        }
586        bpf_cmd_BPF_MAP_LOOKUP_BATCH => {
587            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_MAP_LOOKUP_BATCH");
588            error!(EINVAL)
589        }
590        bpf_cmd_BPF_MAP_LOOKUP_AND_DELETE_BATCH => {
591            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_MAP_LOOKUP_AND_DELETE_BATCH");
592            error!(EINVAL)
593        }
594        bpf_cmd_BPF_MAP_UPDATE_BATCH => {
595            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_MAP_UPDATE_BATCH");
596            error!(EINVAL)
597        }
598        bpf_cmd_BPF_MAP_DELETE_BATCH => {
599            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_MAP_DELETE_BATCH");
600            error!(EINVAL)
601        }
602        bpf_cmd_BPF_LINK_CREATE => {
603            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_LINK_CREATE");
604            error!(EINVAL)
605        }
606        bpf_cmd_BPF_LINK_UPDATE => {
607            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_LINK_UPDATE");
608            error!(EINVAL)
609        }
610        bpf_cmd_BPF_LINK_GET_FD_BY_ID => {
611            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_LINK_GET_FD_BY_ID");
612            error!(EINVAL)
613        }
614        bpf_cmd_BPF_LINK_GET_NEXT_ID => {
615            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_LINK_GET_NEXT_ID");
616            error!(EINVAL)
617        }
618        bpf_cmd_BPF_ENABLE_STATS => {
619            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_ENABLE_STATS");
620            error!(EINVAL)
621        }
622        bpf_cmd_BPF_ITER_CREATE => {
623            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_ITER_CREATE");
624            error!(EINVAL)
625        }
626        bpf_cmd_BPF_LINK_DETACH => {
627            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_LINK_DETACH");
628            error!(EINVAL)
629        }
630        bpf_cmd_BPF_PROG_BIND_MAP => {
631            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_PROG_BIND_MAP");
632            error!(EINVAL)
633        }
634        bpf_cmd_BPF_TOKEN_CREATE => {
635            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_TOKEN_CREATE");
636            error!(EINVAL)
637        }
638        _ => {
639            track_stub!(TODO("https://fxbug.dev/322874055"), "bpf", cmd);
640            error!(EINVAL)
641        }
642    }
643}
644
645// Syscalls for arch32 usage
646#[cfg(target_arch = "aarch64")]
647mod arch32 {
648    pub use super::sys_bpf as sys_arch32_bpf;
649}
650
651#[cfg(target_arch = "aarch64")]
652pub use arch32::*;