Program type BPF_PROG_TYPE_SYSCALL
Syscall programs can be used to execute syscalls from eBPF.
Usage
The abstract purpose of the syscall program type is to execute syscalls from eBPF. The initial use case for this program type was to offload some of the work of loader libraries to syscall eBPF programs. The program type can also be used by for "HID-BPF" to register a BPF program as a HID device driver.
Loading with light skeletons
This use case revolves around using a BPF_PROG_TYPE_SYSCALL
program to load one or more eBPF programs. The reason behind this is two-fold. First, with a bit of automation in the form of generation tools, loading a program can be made easier. Second, this new structure would make it easier to implement a form of code signing for eBPF programs. However, the code signing use case so far has not been successful.
The way this works is that you write and compile your primary eBPF program as normal. You then feed it to bpftool
with the gen skeleton -L {prog}.o > {prog}.skel.h
command. This will generate a "light skeleton" for the program. Essentially a header file which can be included by a custom userspace program as dependency. It exposes pre-defined function to then load the eBPF program. The header file embeds the essential parts of the primary ELF file and a generated BPF_PROG_TYPE_SYSCALL
program. Parts of the primary program such as its instructions, map definitions, and initial keys/values are part of the generated program or provided as data via existing mechanisms. The syscall program then uses a series of bpf_sys_bpf
helper calls to load the primary program just like a loader would normally do from userspace.
HID-BPF
The use case of HID-BPF is to implement HID device drivers in eBPF, at least partially. This allows HID drivers implemented this way for new devices to work on older kernels without the need for a kernel module.
No special program type was created for this use case, rather the FMOD_RET
tracing program type is repurposed. However, normally these attach to a single instance of a kernel function. For the HID-BPF use case, we want to attach to a specific HID device. This is done by using the hid_bpf_attach_prog
kfunc to attach the program to the HID device. Which bring us to the BPF_PROG_TYPE_SYSCALL
program which is used to actually execute this kfunc.
Context
This program type does not have a set context type, so as long as your eBPF program and userspace are aligned, you can use any context type you want.
Attachment
Syscall programs are never attached to any hook. They can only be executed from the BPF_PROG_RUN
syscall command.
Example
BPF-HID
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2022 Benjamin Tissoires
*/
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "hid_bpf_helpers.h"
struct attach_prog_args {
int prog_fd;
unsigned int hid;
int retval;
};
SEC("syscall")
int attach_prog(struct attach_prog_args *ctx)
{
ctx->retval = hid_bpf_attach_prog(ctx->hid,
ctx->prog_fd,
0);
return 0;
}
Helper functions
Not all helper functions are available in all program types. These are the helper calls available for syscall programs:
Supported helper functions
- bpf_sys_bpf
- bpf_btf_find_by_name_kind
- bpf_sys_close
- bpf_kallsyms_lookup_name
- bpf_map_lookup_elem
- bpf_map_update_elem
- bpf_map_delete_elem
- bpf_map_push_elem
- bpf_map_pop_elem
- bpf_map_peek_elem
- bpf_map_lookup_percpu_elem
- bpf_ktime_get_ns
- bpf_ktime_get_boot_ns
- bpf_tail_call
- bpf_get_current_pid_tgid
- bpf_get_current_task
- bpf_get_current_task_btf
- bpf_task_pt_regs
- bpf_get_current_uid_gid
- bpf_get_current_comm
- bpf_trace_printk
- bpf_get_smp_processor_id
- bpf_get_numa_node_id
- bpf_perf_event_read
- bpf_current_task_under_cgroup
- bpf_get_prandom_u32
- bpf_probe_write_user
- bpf_probe_read_user
- bpf_probe_read_kernel
- bpf_probe_read_user_str
- bpf_probe_read_kernel_str
- bpf_probe_read
- bpf_probe_read_str
- bpf_get_current_cgroup_id
- bpf_get_current_ancestor_cgroup_id
- bpf_send_signal
- bpf_send_signal_thread
- bpf_perf_event_read_value
- bpf_get_ns_current_pid_tgid
- bpf_ringbuf_output
- bpf_ringbuf_reserve
- bpf_ringbuf_submit
- bpf_ringbuf_discard
- bpf_ringbuf_query
- bpf_jiffies64
- bpf_get_task_stack
- bpf_copy_from_user
- bpf_copy_from_user_task
- bpf_snprintf_btf
- bpf_per_cpu_ptr
- bpf_this_cpu_ptr
- bpf_task_storage_get
- bpf_task_storage_delete
- bpf_for_each_map_elem
- bpf_snprintf
- bpf_get_func_ip
- bpf_get_branch_snapshot
- bpf_find_vma
- bpf_trace_vprintk
- bpf_map_lookup_elem
- bpf_map_update_elem
- bpf_map_delete_elem
- bpf_map_push_elem
- bpf_map_pop_elem
- bpf_map_peek_elem
- bpf_map_lookup_percpu_elem
- bpf_get_prandom_u32
- bpf_get_smp_processor_id
- bpf_get_numa_node_id
- bpf_tail_call
- bpf_ktime_get_ns
- bpf_ktime_get_boot_ns
- bpf_ringbuf_output
- bpf_ringbuf_reserve
- bpf_ringbuf_submit
- bpf_ringbuf_discard
- bpf_ringbuf_query
- bpf_for_each_map_elem
- bpf_loop
- bpf_strncmp
- bpf_spin_lock
- bpf_spin_unlock
- bpf_jiffies64
- bpf_per_cpu_ptr
- bpf_this_cpu_ptr
- bpf_timer_init
- bpf_timer_set_callback
- bpf_timer_start
- bpf_timer_cancel
- bpf_trace_printk
- bpf_get_current_task
- bpf_get_current_task_btf
- bpf_probe_read_user
- bpf_probe_read_kernel
- bpf_probe_read_user_str
- bpf_probe_read_kernel_str
- bpf_snprintf_btf
- bpf_snprintf
- bpf_task_pt_regs
- bpf_trace_vprintk
KFuncs
Supported kfuncs
- bpf_cast_to_kern_ctx
- bpf_dynptr_adjust
- bpf_dynptr_clone
- bpf_dynptr_is_null
- bpf_dynptr_is_rdonly
- bpf_dynptr_size
- bpf_dynptr_slice
- bpf_dynptr_slice_rdwr
- bpf_iter_css_destroy
- bpf_iter_css_new
- bpf_iter_css_next
- bpf_iter_css_task_destroy
- bpf_iter_css_task_new
- bpf_iter_css_task_next
- bpf_iter_num_destroy
- bpf_iter_num_new
- bpf_iter_num_next
- bpf_iter_task_destroy
- bpf_iter_task_new
- bpf_iter_task_next
- bpf_iter_task_vma_destroy
- bpf_iter_task_vma_new
- bpf_iter_task_vma_next
- bpf_map_sum_elem_count
- bpf_rcu_read_lock
- bpf_rcu_read_unlock
- bpf_rdonly_cast
- hid_bpf_allocate_context
- hid_bpf_attach_prog
- hid_bpf_hw_request
- hid_bpf_release_context