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