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