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 =
327                if prog_attr.log_level != 0 && prog_attr.log_buf != 0 && prog_attr.log_size > 1 {
328                    UserBuffersOutputBuffer::unified_new(
329                        current_task,
330                        smallvec![UserBuffer {
331                            address: prog_attr.log_buf.into(),
332                            length: prog_attr.log_size as usize,
333                        }],
334                    )?
335                } else {
336                    UserBuffersOutputBuffer::unified_new(current_task, smallvec![])?
337                };
338            let name = validate_bpf_name(prog_attr.prog_name.as_bytes())?;
339            let info = ProgramInfo::try_from(&prog_attr)?;
340            Program::check_load_access(current_task, &info)?;
341            let program_type = info.program_type;
342            let program = Program::new(current_task, info, &mut log_buffer, code);
343            if log_buffer.available() > 0 && log_buffer.bytes_written() == 0 {
344                log_buffer.write_all(b"\0")?;
345            }
346            let program_or_stub = match (program, program_type) {
347                (Ok(program), _) => BpfHandle::Program(program),
348
349                // Create a stub only if it's allowed for the `program_type`
350                // and bpf_v2 is not enabled.
351                (Err(e), ProgramType::SockOps | ProgramType::SchedCls | ProgramType::Kprobe)
352                    if !current_task.kernel().features.bpf_v2 =>
353                {
354                    log_warn!(
355                        "Creating a stub for eBPF program {name}, type={program_type:?}: {e:?}"
356                    );
357                    BpfHandle::ProgramStub(prog_attr.prog_type)
358                }
359
360                (Err(e), _) => {
361                    log_error!("Unable to load eBPF program {name}, type={program_type:?}: {e:?}");
362                    return Err(e.into());
363                }
364            };
365            install_bpf_fd(current_task, program_or_stub)
366        }
367
368        // Attach an eBPF program to a target_fd at the specified attach_type hook.
369        bpf_cmd_BPF_PROG_ATTACH => {
370            let attach_attr: BpfAttachAttr = read_attr(current_task, attr_addr, attr_size)?;
371            security::check_bpf_access(current_task, cmd, &attach_attr, attr_size)?;
372            bpf_prog_attach(current_task, attach_attr)
373        }
374
375        // Obtain information about eBPF programs associated with the specified attach_type hook.
376        bpf_cmd_BPF_PROG_QUERY => {
377            let mut prog_attr: bpf_attr__bindgen_ty_10 =
378                read_attr(current_task, attr_addr, attr_size)?;
379            log_trace!("BPF_PROG_QUERY");
380            security::check_bpf_access(current_task, cmd, &prog_attr, attr_size)?;
381            track_stub!(TODO("https://fxbug.dev/322873416"), "Bpf::BPF_PROG_QUERY");
382            current_task.write_memory(UserAddress::from(prog_attr.prog_ids), 1.as_bytes())?;
383            prog_attr.__bindgen_anon_2.prog_cnt = std::mem::size_of::<u64>() as u32;
384            current_task.write_memory(attr_addr, prog_attr.as_bytes())?;
385            Ok(SUCCESS)
386        }
387
388        // Pin an eBPF program or map referred by the specified bpf_fd to the provided pathname on
389        // the filesystem.
390        bpf_cmd_BPF_OBJ_PIN => {
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                &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(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 = resolve_pinned_bpf_object(current_task, pathname.as_ref(), open_flags)?;
423            reopen_bpf_fd(current_task, handle, open_flags)
424        }
425
426        // Obtain information about the eBPF object corresponding to bpf_fd.
427        bpf_cmd_BPF_OBJ_GET_INFO_BY_FD => {
428            let mut get_info_attr: bpf_attr__bindgen_ty_9 =
429                read_attr(current_task, attr_addr, attr_size)?;
430            log_trace!("BPF_OBJ_GET_INFO_BY_FD {:?}", get_info_attr);
431            security::check_bpf_access(current_task, cmd, &get_info_attr, attr_size)?;
432            let bpf_fd = FdNumber::from_raw(get_info_attr.bpf_fd as i32);
433            let object = get_bpf_object(current_task, bpf_fd)?;
434
435            let mut info = match object {
436                BpfHandle::Map(map) => bpf_map_info {
437                    type_: map.schema.map_type,
438                    id: map.id(),
439                    key_size: map.schema.key_size,
440                    value_size: map.schema.value_size,
441                    max_entries: map.schema.max_entries,
442                    map_flags: map.schema.flags.bits(),
443                    ..Default::default()
444                }
445                .as_bytes()
446                .to_owned(),
447                BpfHandle::Program(prog) => {
448                    #[allow(unknown_lints, clippy::unnecessary_struct_initialization)]
449                    bpf_prog_info {
450                        type_: prog.info.program_type.into(),
451                        id: prog.id(),
452                        // TODO: https://fxbug.dev/397389704 - return actual length.
453                        jited_prog_len: 1,
454                        ..Default::default()
455                    }
456                    .as_bytes()
457                    .to_owned()
458                }
459                BpfHandle::ProgramStub(type_) => {
460                    #[allow(unknown_lints, clippy::unnecessary_struct_initialization)]
461                    bpf_prog_info {
462                        type_,
463                        // TODO: https://fxbug.dev/397389704 - return actual length.
464                        jited_prog_len: 1,
465                        ..Default::default()
466                    }
467                    .as_bytes()
468                    .to_owned()
469                }
470                _ => {
471                    return error!(EINVAL);
472                }
473            };
474
475            // If info_len is larger than info, write out the full length of info and write the
476            // smaller size into info_len. If info_len is smaller, truncate info.
477            // TODO(tbodt): This is just a guess for the behavior. Works with BpfSyscallWrappers.h,
478            // but could be wrong.
479            info.truncate(get_info_attr.info_len as usize);
480            get_info_attr.info_len = info.len() as u32;
481            current_task.write_memory(UserAddress::from(get_info_attr.info), &info)?;
482            current_task.write_memory(attr_addr, get_info_attr.as_bytes())?;
483            Ok(SUCCESS)
484        }
485
486        // Verify and load BPF Type Format (BTF) metadata into the kernel, returning a new file
487        // descriptor associated with the metadata. BTF is described in more detail at
488        // https://www.kernel.org/doc/html/latest/bpf/btf.html.
489        bpf_cmd_BPF_BTF_LOAD => {
490            let btf_attr: bpf_attr__bindgen_ty_12 = read_attr(current_task, attr_addr, attr_size)?;
491            log_trace!("BPF_BTF_LOAD {:?}", btf_attr);
492            security::check_bpf_access(current_task, cmd, &btf_attr, attr_size)?;
493            let data = current_task
494                .read_memory_to_vec(UserAddress::from(btf_attr.btf), btf_attr.btf_size as usize)?;
495            install_bpf_fd(current_task, BpfTypeFormat { data })
496        }
497        bpf_cmd_BPF_PROG_DETACH => {
498            let attach_attr: BpfAttachAttr = read_attr(current_task, attr_addr, attr_size)?;
499            security::check_bpf_access(current_task, cmd, &attach_attr, attr_size)?;
500            bpf_prog_detach(current_task, attach_attr)
501        }
502        bpf_cmd_BPF_PROG_RUN => {
503            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_PROG_RUN");
504            error!(EINVAL)
505        }
506        bpf_cmd_BPF_PROG_GET_NEXT_ID => {
507            security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
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(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            security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
522            let mut get_next_attr: bpf_attr__bindgen_ty_8 =
523                read_attr(current_task, attr_addr, attr_size)?;
524            // SAFETY: Reading u32 value from a union is safe.
525            let start_id = unsafe { get_next_attr.__bindgen_anon_1.start_id };
526            get_next_attr.next_id = current_task
527                .kernel()
528                .ebpf_state
529                .get_next_map_id(start_id)
530                .ok_or_else(|| errno!(ENOENT))?;
531            current_task.write_object(UserRef::new(attr_addr), &get_next_attr)?;
532            Ok(SUCCESS)
533        }
534        bpf_cmd_BPF_PROG_GET_FD_BY_ID => {
535            security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
536            let get_by_id_attr: bpf_attr__bindgen_ty_8 =
537                read_attr(current_task, attr_addr, attr_size)?;
538            // SAFETY: Reading u32 value from a union is safe.
539            let prog_id = unsafe { get_by_id_attr.__bindgen_anon_1.prog_id };
540            let prog = current_task
541                .kernel()
542                .ebpf_state
543                .get_program_by_id(prog_id)
544                .ok_or_else(|| errno!(ENOENT))?;
545            install_bpf_fd(current_task, prog)
546        }
547        bpf_cmd_BPF_MAP_GET_FD_BY_ID => {
548            security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
549            let get_by_id_attr: bpf_attr__bindgen_ty_8 =
550                read_attr(current_task, attr_addr, attr_size)?;
551            // SAFETY: Reading u32 value from a union is safe.
552            let map_id = unsafe { get_by_id_attr.__bindgen_anon_1.map_id };
553            let map = current_task
554                .kernel()
555                .ebpf_state
556                .get_map_by_id(map_id)
557                .ok_or_else(|| errno!(ENOENT))?;
558            install_bpf_fd(current_task, map)
559        }
560        bpf_cmd_BPF_RAW_TRACEPOINT_OPEN => {
561            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_RAW_TRACEPOINT_OPEN");
562            error!(EINVAL)
563        }
564        bpf_cmd_BPF_BTF_GET_FD_BY_ID => {
565            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_BTF_GET_FD_BY_ID");
566            error!(EINVAL)
567        }
568        bpf_cmd_BPF_TASK_FD_QUERY => {
569            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_TASK_FD_QUERY");
570            error!(EINVAL)
571        }
572        bpf_cmd_BPF_MAP_LOOKUP_AND_DELETE_ELEM => {
573            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_MAP_LOOKUP_AND_DELETE_ELEM");
574            error!(EINVAL)
575        }
576        bpf_cmd_BPF_MAP_FREEZE => {
577            let elem_attr: bpf_attr__bindgen_ty_2 = read_attr(current_task, attr_addr, attr_size)?;
578            log_trace!("BPF_MAP_FREEZE");
579            let map_fd = FdNumber::from_raw(elem_attr.map_fd as i32);
580            let map = get_bpf_object(current_task, map_fd)?;
581            let map = map.as_map()?;
582            map.freeze()?;
583            Ok(SUCCESS)
584        }
585        bpf_cmd_BPF_BTF_GET_NEXT_ID => {
586            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_BTF_GET_NEXT_ID");
587            error!(EINVAL)
588        }
589        bpf_cmd_BPF_MAP_LOOKUP_BATCH => {
590            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_MAP_LOOKUP_BATCH");
591            error!(EINVAL)
592        }
593        bpf_cmd_BPF_MAP_LOOKUP_AND_DELETE_BATCH => {
594            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_MAP_LOOKUP_AND_DELETE_BATCH");
595            error!(EINVAL)
596        }
597        bpf_cmd_BPF_MAP_UPDATE_BATCH => {
598            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_MAP_UPDATE_BATCH");
599            error!(EINVAL)
600        }
601        bpf_cmd_BPF_MAP_DELETE_BATCH => {
602            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_MAP_DELETE_BATCH");
603            error!(EINVAL)
604        }
605        bpf_cmd_BPF_LINK_CREATE => {
606            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_LINK_CREATE");
607            error!(EINVAL)
608        }
609        bpf_cmd_BPF_LINK_UPDATE => {
610            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_LINK_UPDATE");
611            error!(EINVAL)
612        }
613        bpf_cmd_BPF_LINK_GET_FD_BY_ID => {
614            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_LINK_GET_FD_BY_ID");
615            error!(EINVAL)
616        }
617        bpf_cmd_BPF_LINK_GET_NEXT_ID => {
618            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_LINK_GET_NEXT_ID");
619            error!(EINVAL)
620        }
621        bpf_cmd_BPF_ENABLE_STATS => {
622            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_ENABLE_STATS");
623            error!(EINVAL)
624        }
625        bpf_cmd_BPF_ITER_CREATE => {
626            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_ITER_CREATE");
627            error!(EINVAL)
628        }
629        bpf_cmd_BPF_LINK_DETACH => {
630            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_LINK_DETACH");
631            error!(EINVAL)
632        }
633        bpf_cmd_BPF_PROG_BIND_MAP => {
634            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_PROG_BIND_MAP");
635            error!(EINVAL)
636        }
637        bpf_cmd_BPF_TOKEN_CREATE => {
638            track_stub!(TODO("https://fxbug.dev/322874055"), "BPF_TOKEN_CREATE");
639            error!(EINVAL)
640        }
641        _ => {
642            track_stub!(TODO("https://fxbug.dev/322874055"), "bpf", cmd);
643            error!(EINVAL)
644        }
645    }
646}
647
648// Syscalls for arch32 usage
649#[cfg(target_arch = "aarch64")]
650mod arch32 {
651    pub use super::sys_bpf as sys_arch32_bpf;
652}
653
654#[cfg(target_arch = "aarch64")]
655pub use arch32::*;