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