Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
Daniel Borkmann says: ==================== pull-request: bpf-next 2020-11-14 1) Add BTF generation for kernel modules and extend BTF infra in kernel e.g. support for split BTF loading and validation, from Andrii Nakryiko. 2) Support for pointers beyond pkt_end to recognize LLVM generated patterns on inlined branch conditions, from Alexei Starovoitov. 3) Implements bpf_local_storage for task_struct for BPF LSM, from KP Singh. 4) Enable FENTRY/FEXIT/RAW_TP tracing program to use the bpf_sk_storage infra, from Martin KaFai Lau. 5) Add XDP bulk APIs that introduce a defer/flush mechanism to optimize the XDP_REDIRECT path, from Lorenzo Bianconi. 6) Fix a potential (although rather theoretical) deadlock of hashtab in NMI context, from Song Liu. 7) Fixes for cross and out-of-tree build of bpftool and runqslower allowing build for different target archs on same source tree, from Jean-Philippe Brucker. 8) Fix error path in htab_map_alloc() triggered from syzbot, from Eric Dumazet. 9) Move functionality from test_tcpbpf_user into the test_progs framework so it can run in BPF CI, from Alexander Duyck. 10) Lift hashtab key_size limit to be larger than MAX_BPF_STACK, from Florian Lehner. Note that for the fix from Song we have seen a sparse report on context imbalance which requires changes in sparse itself for proper annotation detection where this is currently being discussed on linux-sparse among developers [0]. Once we have more clarification/guidance after their fix, Song will follow-up. [0] https://lore.kernel.org/linux-sparse/CAHk-=wh4bx8A8dHnX612MsDO13st6uzAz1mJ1PaHHVevJx_ZCw@mail.gmail.com/T/ https://lore.kernel.org/linux-sparse/20201109221345.uklbp3lzgq6g42zb@ltop.local/T/ * git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next: (66 commits) net: mlx5: Add xdp tx return bulking support net: mvpp2: Add xdp tx return bulking support net: mvneta: Add xdp tx return bulking support net: page_pool: Add bulk support for ptr_ring net: xdp: Introduce bulking for xdp tx return path bpf: Expose bpf_d_path helper to sleepable LSM hooks bpf: Augment the set of sleepable LSM hooks bpf: selftest: Use bpf_sk_storage in FENTRY/FEXIT/RAW_TP bpf: Allow using bpf_sk_storage in FENTRY/FEXIT/RAW_TP bpf: Rename some functions in bpf_sk_storage bpf: Folding omem_charge() into sk_storage_charge() selftests/bpf: Add asm tests for pkt vs pkt_end comparison. selftests/bpf: Add skb_pkt_end test bpf: Support for pointers beyond pkt_end. tools/bpf: Always run the *-clean recipes tools/bpf: Add bootstrap/ to .gitignore bpf: Fix NULL dereference in bpf_task_storage tools/bpftool: Fix build slowdown tools/runqslower: Build bpftool using HOSTCC tools/runqslower: Enable out-of-tree build ... ==================== Link: https://lore.kernel.org/r/20201114020819.29584-1-daniel@iogearbox.net Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -10,6 +10,7 @@ obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o bpf_i
|
||||
obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o
|
||||
obj-$(CONFIG_BPF_SYSCALL) += local_storage.o queue_stack_maps.o ringbuf.o
|
||||
obj-${CONFIG_BPF_LSM} += bpf_inode_storage.o
|
||||
obj-${CONFIG_BPF_LSM} += bpf_task_storage.o
|
||||
obj-$(CONFIG_BPF_SYSCALL) += disasm.o
|
||||
obj-$(CONFIG_BPF_JIT) += trampoline.o
|
||||
obj-$(CONFIG_BPF_SYSCALL) += btf.o
|
||||
|
||||
@@ -67,6 +67,15 @@ static void bpf_iter_done_stop(struct seq_file *seq)
|
||||
iter_priv->done_stop = true;
|
||||
}
|
||||
|
||||
static bool bpf_iter_support_resched(struct seq_file *seq)
|
||||
{
|
||||
struct bpf_iter_priv_data *iter_priv;
|
||||
|
||||
iter_priv = container_of(seq->private, struct bpf_iter_priv_data,
|
||||
target_private);
|
||||
return iter_priv->tinfo->reg_info->feature & BPF_ITER_RESCHED;
|
||||
}
|
||||
|
||||
/* maximum visited objects before bailing out */
|
||||
#define MAX_ITER_OBJECTS 1000000
|
||||
|
||||
@@ -83,6 +92,7 @@ static ssize_t bpf_seq_read(struct file *file, char __user *buf, size_t size,
|
||||
struct seq_file *seq = file->private_data;
|
||||
size_t n, offs, copied = 0;
|
||||
int err = 0, num_objs = 0;
|
||||
bool can_resched;
|
||||
void *p;
|
||||
|
||||
mutex_lock(&seq->lock);
|
||||
@@ -135,6 +145,7 @@ static ssize_t bpf_seq_read(struct file *file, char __user *buf, size_t size,
|
||||
goto done;
|
||||
}
|
||||
|
||||
can_resched = bpf_iter_support_resched(seq);
|
||||
while (1) {
|
||||
loff_t pos = seq->index;
|
||||
|
||||
@@ -180,6 +191,9 @@ static ssize_t bpf_seq_read(struct file *file, char __user *buf, size_t size,
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (can_resched)
|
||||
cond_resched();
|
||||
}
|
||||
stop:
|
||||
offs = seq->count;
|
||||
|
||||
@@ -63,11 +63,99 @@ bpf_lsm_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
||||
return &bpf_sk_storage_get_proto;
|
||||
case BPF_FUNC_sk_storage_delete:
|
||||
return &bpf_sk_storage_delete_proto;
|
||||
case BPF_FUNC_spin_lock:
|
||||
return &bpf_spin_lock_proto;
|
||||
case BPF_FUNC_spin_unlock:
|
||||
return &bpf_spin_unlock_proto;
|
||||
case BPF_FUNC_task_storage_get:
|
||||
return &bpf_task_storage_get_proto;
|
||||
case BPF_FUNC_task_storage_delete:
|
||||
return &bpf_task_storage_delete_proto;
|
||||
default:
|
||||
return tracing_prog_func_proto(func_id, prog);
|
||||
}
|
||||
}
|
||||
|
||||
/* The set of hooks which are called without pagefaults disabled and are allowed
|
||||
* to "sleep" and thus can be used for sleeable BPF programs.
|
||||
*/
|
||||
BTF_SET_START(sleepable_lsm_hooks)
|
||||
BTF_ID(func, bpf_lsm_bpf)
|
||||
BTF_ID(func, bpf_lsm_bpf_map)
|
||||
BTF_ID(func, bpf_lsm_bpf_map_alloc_security)
|
||||
BTF_ID(func, bpf_lsm_bpf_map_free_security)
|
||||
BTF_ID(func, bpf_lsm_bpf_prog)
|
||||
BTF_ID(func, bpf_lsm_bprm_check_security)
|
||||
BTF_ID(func, bpf_lsm_bprm_committed_creds)
|
||||
BTF_ID(func, bpf_lsm_bprm_committing_creds)
|
||||
BTF_ID(func, bpf_lsm_bprm_creds_for_exec)
|
||||
BTF_ID(func, bpf_lsm_bprm_creds_from_file)
|
||||
BTF_ID(func, bpf_lsm_capget)
|
||||
BTF_ID(func, bpf_lsm_capset)
|
||||
BTF_ID(func, bpf_lsm_cred_prepare)
|
||||
BTF_ID(func, bpf_lsm_file_ioctl)
|
||||
BTF_ID(func, bpf_lsm_file_lock)
|
||||
BTF_ID(func, bpf_lsm_file_open)
|
||||
BTF_ID(func, bpf_lsm_file_receive)
|
||||
BTF_ID(func, bpf_lsm_inet_conn_established)
|
||||
BTF_ID(func, bpf_lsm_inode_create)
|
||||
BTF_ID(func, bpf_lsm_inode_free_security)
|
||||
BTF_ID(func, bpf_lsm_inode_getattr)
|
||||
BTF_ID(func, bpf_lsm_inode_getxattr)
|
||||
BTF_ID(func, bpf_lsm_inode_mknod)
|
||||
BTF_ID(func, bpf_lsm_inode_need_killpriv)
|
||||
BTF_ID(func, bpf_lsm_inode_post_setxattr)
|
||||
BTF_ID(func, bpf_lsm_inode_readlink)
|
||||
BTF_ID(func, bpf_lsm_inode_rename)
|
||||
BTF_ID(func, bpf_lsm_inode_rmdir)
|
||||
BTF_ID(func, bpf_lsm_inode_setattr)
|
||||
BTF_ID(func, bpf_lsm_inode_setxattr)
|
||||
BTF_ID(func, bpf_lsm_inode_symlink)
|
||||
BTF_ID(func, bpf_lsm_inode_unlink)
|
||||
BTF_ID(func, bpf_lsm_kernel_module_request)
|
||||
BTF_ID(func, bpf_lsm_kernfs_init_security)
|
||||
BTF_ID(func, bpf_lsm_key_free)
|
||||
BTF_ID(func, bpf_lsm_mmap_file)
|
||||
BTF_ID(func, bpf_lsm_netlink_send)
|
||||
BTF_ID(func, bpf_lsm_path_notify)
|
||||
BTF_ID(func, bpf_lsm_release_secctx)
|
||||
BTF_ID(func, bpf_lsm_sb_alloc_security)
|
||||
BTF_ID(func, bpf_lsm_sb_eat_lsm_opts)
|
||||
BTF_ID(func, bpf_lsm_sb_kern_mount)
|
||||
BTF_ID(func, bpf_lsm_sb_mount)
|
||||
BTF_ID(func, bpf_lsm_sb_remount)
|
||||
BTF_ID(func, bpf_lsm_sb_set_mnt_opts)
|
||||
BTF_ID(func, bpf_lsm_sb_show_options)
|
||||
BTF_ID(func, bpf_lsm_sb_statfs)
|
||||
BTF_ID(func, bpf_lsm_sb_umount)
|
||||
BTF_ID(func, bpf_lsm_settime)
|
||||
BTF_ID(func, bpf_lsm_socket_accept)
|
||||
BTF_ID(func, bpf_lsm_socket_bind)
|
||||
BTF_ID(func, bpf_lsm_socket_connect)
|
||||
BTF_ID(func, bpf_lsm_socket_create)
|
||||
BTF_ID(func, bpf_lsm_socket_getpeername)
|
||||
BTF_ID(func, bpf_lsm_socket_getpeersec_dgram)
|
||||
BTF_ID(func, bpf_lsm_socket_getsockname)
|
||||
BTF_ID(func, bpf_lsm_socket_getsockopt)
|
||||
BTF_ID(func, bpf_lsm_socket_listen)
|
||||
BTF_ID(func, bpf_lsm_socket_post_create)
|
||||
BTF_ID(func, bpf_lsm_socket_recvmsg)
|
||||
BTF_ID(func, bpf_lsm_socket_sendmsg)
|
||||
BTF_ID(func, bpf_lsm_socket_shutdown)
|
||||
BTF_ID(func, bpf_lsm_socket_socketpair)
|
||||
BTF_ID(func, bpf_lsm_syslog)
|
||||
BTF_ID(func, bpf_lsm_task_alloc)
|
||||
BTF_ID(func, bpf_lsm_task_getsecid)
|
||||
BTF_ID(func, bpf_lsm_task_prctl)
|
||||
BTF_ID(func, bpf_lsm_task_setscheduler)
|
||||
BTF_ID(func, bpf_lsm_task_to_inode)
|
||||
BTF_SET_END(sleepable_lsm_hooks)
|
||||
|
||||
bool bpf_lsm_is_sleepable_hook(u32 btf_id)
|
||||
{
|
||||
return btf_id_set_contains(&sleepable_lsm_hooks, btf_id);
|
||||
}
|
||||
|
||||
const struct bpf_prog_ops lsm_prog_ops = {
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2020 Facebook
|
||||
* Copyright 2020 Google LLC.
|
||||
*/
|
||||
|
||||
#include <linux/pid.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/hash.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/bpf_local_storage.h>
|
||||
#include <linux/filter.h>
|
||||
#include <uapi/linux/btf.h>
|
||||
#include <linux/bpf_lsm.h>
|
||||
#include <linux/btf_ids.h>
|
||||
#include <linux/fdtable.h>
|
||||
|
||||
DEFINE_BPF_STORAGE_CACHE(task_cache);
|
||||
|
||||
static struct bpf_local_storage __rcu **task_storage_ptr(void *owner)
|
||||
{
|
||||
struct task_struct *task = owner;
|
||||
struct bpf_storage_blob *bsb;
|
||||
|
||||
bsb = bpf_task(task);
|
||||
if (!bsb)
|
||||
return NULL;
|
||||
return &bsb->storage;
|
||||
}
|
||||
|
||||
static struct bpf_local_storage_data *
|
||||
task_storage_lookup(struct task_struct *task, struct bpf_map *map,
|
||||
bool cacheit_lockit)
|
||||
{
|
||||
struct bpf_local_storage *task_storage;
|
||||
struct bpf_local_storage_map *smap;
|
||||
struct bpf_storage_blob *bsb;
|
||||
|
||||
bsb = bpf_task(task);
|
||||
if (!bsb)
|
||||
return NULL;
|
||||
|
||||
task_storage = rcu_dereference(bsb->storage);
|
||||
if (!task_storage)
|
||||
return NULL;
|
||||
|
||||
smap = (struct bpf_local_storage_map *)map;
|
||||
return bpf_local_storage_lookup(task_storage, smap, cacheit_lockit);
|
||||
}
|
||||
|
||||
void bpf_task_storage_free(struct task_struct *task)
|
||||
{
|
||||
struct bpf_local_storage_elem *selem;
|
||||
struct bpf_local_storage *local_storage;
|
||||
bool free_task_storage = false;
|
||||
struct bpf_storage_blob *bsb;
|
||||
struct hlist_node *n;
|
||||
|
||||
bsb = bpf_task(task);
|
||||
if (!bsb)
|
||||
return;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
local_storage = rcu_dereference(bsb->storage);
|
||||
if (!local_storage) {
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Neither the bpf_prog nor the bpf-map's syscall
|
||||
* could be modifying the local_storage->list now.
|
||||
* Thus, no elem can be added-to or deleted-from the
|
||||
* local_storage->list by the bpf_prog or by the bpf-map's syscall.
|
||||
*
|
||||
* It is racing with bpf_local_storage_map_free() alone
|
||||
* when unlinking elem from the local_storage->list and
|
||||
* the map's bucket->list.
|
||||
*/
|
||||
raw_spin_lock_bh(&local_storage->lock);
|
||||
hlist_for_each_entry_safe(selem, n, &local_storage->list, snode) {
|
||||
/* Always unlink from map before unlinking from
|
||||
* local_storage.
|
||||
*/
|
||||
bpf_selem_unlink_map(selem);
|
||||
free_task_storage = bpf_selem_unlink_storage_nolock(
|
||||
local_storage, selem, false);
|
||||
}
|
||||
raw_spin_unlock_bh(&local_storage->lock);
|
||||
rcu_read_unlock();
|
||||
|
||||
/* free_task_storage should always be true as long as
|
||||
* local_storage->list was non-empty.
|
||||
*/
|
||||
if (free_task_storage)
|
||||
kfree_rcu(local_storage, rcu);
|
||||
}
|
||||
|
||||
static void *bpf_pid_task_storage_lookup_elem(struct bpf_map *map, void *key)
|
||||
{
|
||||
struct bpf_local_storage_data *sdata;
|
||||
struct task_struct *task;
|
||||
unsigned int f_flags;
|
||||
struct pid *pid;
|
||||
int fd, err;
|
||||
|
||||
fd = *(int *)key;
|
||||
pid = pidfd_get_pid(fd, &f_flags);
|
||||
if (IS_ERR(pid))
|
||||
return ERR_CAST(pid);
|
||||
|
||||
/* We should be in an RCU read side critical section, it should be safe
|
||||
* to call pid_task.
|
||||
*/
|
||||
WARN_ON_ONCE(!rcu_read_lock_held());
|
||||
task = pid_task(pid, PIDTYPE_PID);
|
||||
if (!task) {
|
||||
err = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sdata = task_storage_lookup(task, map, true);
|
||||
put_pid(pid);
|
||||
return sdata ? sdata->data : NULL;
|
||||
out:
|
||||
put_pid(pid);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static int bpf_pid_task_storage_update_elem(struct bpf_map *map, void *key,
|
||||
void *value, u64 map_flags)
|
||||
{
|
||||
struct bpf_local_storage_data *sdata;
|
||||
struct task_struct *task;
|
||||
unsigned int f_flags;
|
||||
struct pid *pid;
|
||||
int fd, err;
|
||||
|
||||
fd = *(int *)key;
|
||||
pid = pidfd_get_pid(fd, &f_flags);
|
||||
if (IS_ERR(pid))
|
||||
return PTR_ERR(pid);
|
||||
|
||||
/* We should be in an RCU read side critical section, it should be safe
|
||||
* to call pid_task.
|
||||
*/
|
||||
WARN_ON_ONCE(!rcu_read_lock_held());
|
||||
task = pid_task(pid, PIDTYPE_PID);
|
||||
if (!task || !task_storage_ptr(task)) {
|
||||
err = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sdata = bpf_local_storage_update(
|
||||
task, (struct bpf_local_storage_map *)map, value, map_flags);
|
||||
|
||||
err = PTR_ERR_OR_ZERO(sdata);
|
||||
out:
|
||||
put_pid(pid);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int task_storage_delete(struct task_struct *task, struct bpf_map *map)
|
||||
{
|
||||
struct bpf_local_storage_data *sdata;
|
||||
|
||||
sdata = task_storage_lookup(task, map, false);
|
||||
if (!sdata)
|
||||
return -ENOENT;
|
||||
|
||||
bpf_selem_unlink(SELEM(sdata));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bpf_pid_task_storage_delete_elem(struct bpf_map *map, void *key)
|
||||
{
|
||||
struct task_struct *task;
|
||||
unsigned int f_flags;
|
||||
struct pid *pid;
|
||||
int fd, err;
|
||||
|
||||
fd = *(int *)key;
|
||||
pid = pidfd_get_pid(fd, &f_flags);
|
||||
if (IS_ERR(pid))
|
||||
return PTR_ERR(pid);
|
||||
|
||||
/* We should be in an RCU read side critical section, it should be safe
|
||||
* to call pid_task.
|
||||
*/
|
||||
WARN_ON_ONCE(!rcu_read_lock_held());
|
||||
task = pid_task(pid, PIDTYPE_PID);
|
||||
if (!task) {
|
||||
err = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = task_storage_delete(task, map);
|
||||
out:
|
||||
put_pid(pid);
|
||||
return err;
|
||||
}
|
||||
|
||||
BPF_CALL_4(bpf_task_storage_get, struct bpf_map *, map, struct task_struct *,
|
||||
task, void *, value, u64, flags)
|
||||
{
|
||||
struct bpf_local_storage_data *sdata;
|
||||
|
||||
if (flags & ~(BPF_LOCAL_STORAGE_GET_F_CREATE))
|
||||
return (unsigned long)NULL;
|
||||
|
||||
/* explicitly check that the task_storage_ptr is not
|
||||
* NULL as task_storage_lookup returns NULL in this case and
|
||||
* bpf_local_storage_update expects the owner to have a
|
||||
* valid storage pointer.
|
||||
*/
|
||||
if (!task_storage_ptr(task))
|
||||
return (unsigned long)NULL;
|
||||
|
||||
sdata = task_storage_lookup(task, map, true);
|
||||
if (sdata)
|
||||
return (unsigned long)sdata->data;
|
||||
|
||||
/* This helper must only be called from places where the lifetime of the task
|
||||
* is guaranteed. Either by being refcounted or by being protected
|
||||
* by an RCU read-side critical section.
|
||||
*/
|
||||
if (flags & BPF_LOCAL_STORAGE_GET_F_CREATE) {
|
||||
sdata = bpf_local_storage_update(
|
||||
task, (struct bpf_local_storage_map *)map, value,
|
||||
BPF_NOEXIST);
|
||||
return IS_ERR(sdata) ? (unsigned long)NULL :
|
||||
(unsigned long)sdata->data;
|
||||
}
|
||||
|
||||
return (unsigned long)NULL;
|
||||
}
|
||||
|
||||
BPF_CALL_2(bpf_task_storage_delete, struct bpf_map *, map, struct task_struct *,
|
||||
task)
|
||||
{
|
||||
/* This helper must only be called from places where the lifetime of the task
|
||||
* is guaranteed. Either by being refcounted or by being protected
|
||||
* by an RCU read-side critical section.
|
||||
*/
|
||||
return task_storage_delete(task, map);
|
||||
}
|
||||
|
||||
static int notsupp_get_next_key(struct bpf_map *map, void *key, void *next_key)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static struct bpf_map *task_storage_map_alloc(union bpf_attr *attr)
|
||||
{
|
||||
struct bpf_local_storage_map *smap;
|
||||
|
||||
smap = bpf_local_storage_map_alloc(attr);
|
||||
if (IS_ERR(smap))
|
||||
return ERR_CAST(smap);
|
||||
|
||||
smap->cache_idx = bpf_local_storage_cache_idx_get(&task_cache);
|
||||
return &smap->map;
|
||||
}
|
||||
|
||||
static void task_storage_map_free(struct bpf_map *map)
|
||||
{
|
||||
struct bpf_local_storage_map *smap;
|
||||
|
||||
smap = (struct bpf_local_storage_map *)map;
|
||||
bpf_local_storage_cache_idx_free(&task_cache, smap->cache_idx);
|
||||
bpf_local_storage_map_free(smap);
|
||||
}
|
||||
|
||||
static int task_storage_map_btf_id;
|
||||
const struct bpf_map_ops task_storage_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = bpf_local_storage_map_alloc_check,
|
||||
.map_alloc = task_storage_map_alloc,
|
||||
.map_free = task_storage_map_free,
|
||||
.map_get_next_key = notsupp_get_next_key,
|
||||
.map_lookup_elem = bpf_pid_task_storage_lookup_elem,
|
||||
.map_update_elem = bpf_pid_task_storage_update_elem,
|
||||
.map_delete_elem = bpf_pid_task_storage_delete_elem,
|
||||
.map_check_btf = bpf_local_storage_map_check_btf,
|
||||
.map_btf_name = "bpf_local_storage_map",
|
||||
.map_btf_id = &task_storage_map_btf_id,
|
||||
.map_owner_storage_ptr = task_storage_ptr,
|
||||
};
|
||||
|
||||
BTF_ID_LIST_SINGLE(bpf_task_storage_btf_ids, struct, task_struct)
|
||||
|
||||
const struct bpf_func_proto bpf_task_storage_get_proto = {
|
||||
.func = bpf_task_storage_get,
|
||||
.gpl_only = false,
|
||||
.ret_type = RET_PTR_TO_MAP_VALUE_OR_NULL,
|
||||
.arg1_type = ARG_CONST_MAP_PTR,
|
||||
.arg2_type = ARG_PTR_TO_BTF_ID,
|
||||
.arg2_btf_id = &bpf_task_storage_btf_ids[0],
|
||||
.arg3_type = ARG_PTR_TO_MAP_VALUE_OR_NULL,
|
||||
.arg4_type = ARG_ANYTHING,
|
||||
};
|
||||
|
||||
const struct bpf_func_proto bpf_task_storage_delete_proto = {
|
||||
.func = bpf_task_storage_delete,
|
||||
.gpl_only = false,
|
||||
.ret_type = RET_INTEGER,
|
||||
.arg1_type = ARG_CONST_MAP_PTR,
|
||||
.arg2_type = ARG_PTR_TO_BTF_ID,
|
||||
.arg2_btf_id = &bpf_task_storage_btf_ids[0],
|
||||
};
|
||||
+357
-56
@@ -22,7 +22,8 @@
|
||||
#include <linux/skmsg.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/bsearch.h>
|
||||
#include <linux/btf_ids.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
/* BTF (BPF Type Format) is the meta data format which describes
|
||||
@@ -204,12 +205,19 @@ struct btf {
|
||||
const char *strings;
|
||||
void *nohdr_data;
|
||||
struct btf_header hdr;
|
||||
u32 nr_types;
|
||||
u32 nr_types; /* includes VOID for base BTF */
|
||||
u32 types_size;
|
||||
u32 data_size;
|
||||
refcount_t refcnt;
|
||||
u32 id;
|
||||
struct rcu_head rcu;
|
||||
|
||||
/* split BTF support */
|
||||
struct btf *base_btf;
|
||||
u32 start_id; /* first type ID in this BTF (0 for base BTF) */
|
||||
u32 start_str_off; /* first string offset (0 for base BTF) */
|
||||
char name[MODULE_NAME_LEN];
|
||||
bool kernel_btf;
|
||||
};
|
||||
|
||||
enum verifier_phase {
|
||||
@@ -450,14 +458,27 @@ static bool btf_type_is_datasec(const struct btf_type *t)
|
||||
return BTF_INFO_KIND(t->info) == BTF_KIND_DATASEC;
|
||||
}
|
||||
|
||||
static u32 btf_nr_types_total(const struct btf *btf)
|
||||
{
|
||||
u32 total = 0;
|
||||
|
||||
while (btf) {
|
||||
total += btf->nr_types;
|
||||
btf = btf->base_btf;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind)
|
||||
{
|
||||
const struct btf_type *t;
|
||||
const char *tname;
|
||||
u32 i;
|
||||
u32 i, total;
|
||||
|
||||
for (i = 1; i <= btf->nr_types; i++) {
|
||||
t = btf->types[i];
|
||||
total = btf_nr_types_total(btf);
|
||||
for (i = 1; i < total; i++) {
|
||||
t = btf_type_by_id(btf, i);
|
||||
if (BTF_INFO_KIND(t->info) != kind)
|
||||
continue;
|
||||
|
||||
@@ -600,8 +621,14 @@ static const struct btf_kind_operations *btf_type_ops(const struct btf_type *t)
|
||||
|
||||
static bool btf_name_offset_valid(const struct btf *btf, u32 offset)
|
||||
{
|
||||
return BTF_STR_OFFSET_VALID(offset) &&
|
||||
offset < btf->hdr.str_len;
|
||||
if (!BTF_STR_OFFSET_VALID(offset))
|
||||
return false;
|
||||
|
||||
while (offset < btf->start_str_off)
|
||||
btf = btf->base_btf;
|
||||
|
||||
offset -= btf->start_str_off;
|
||||
return offset < btf->hdr.str_len;
|
||||
}
|
||||
|
||||
static bool __btf_name_char_ok(char c, bool first, bool dot_ok)
|
||||
@@ -615,10 +642,22 @@ static bool __btf_name_char_ok(char c, bool first, bool dot_ok)
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char *btf_str_by_offset(const struct btf *btf, u32 offset)
|
||||
{
|
||||
while (offset < btf->start_str_off)
|
||||
btf = btf->base_btf;
|
||||
|
||||
offset -= btf->start_str_off;
|
||||
if (offset < btf->hdr.str_len)
|
||||
return &btf->strings[offset];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool __btf_name_valid(const struct btf *btf, u32 offset, bool dot_ok)
|
||||
{
|
||||
/* offset must be valid */
|
||||
const char *src = &btf->strings[offset];
|
||||
const char *src = btf_str_by_offset(btf, offset);
|
||||
const char *src_limit;
|
||||
|
||||
if (!__btf_name_char_ok(*src, true, dot_ok))
|
||||
@@ -651,27 +690,28 @@ static bool btf_name_valid_section(const struct btf *btf, u32 offset)
|
||||
|
||||
static const char *__btf_name_by_offset(const struct btf *btf, u32 offset)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
if (!offset)
|
||||
return "(anon)";
|
||||
else if (offset < btf->hdr.str_len)
|
||||
return &btf->strings[offset];
|
||||
else
|
||||
return "(invalid-name-offset)";
|
||||
|
||||
name = btf_str_by_offset(btf, offset);
|
||||
return name ?: "(invalid-name-offset)";
|
||||
}
|
||||
|
||||
const char *btf_name_by_offset(const struct btf *btf, u32 offset)
|
||||
{
|
||||
if (offset < btf->hdr.str_len)
|
||||
return &btf->strings[offset];
|
||||
|
||||
return NULL;
|
||||
return btf_str_by_offset(btf, offset);
|
||||
}
|
||||
|
||||
const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id)
|
||||
{
|
||||
if (type_id > btf->nr_types)
|
||||
return NULL;
|
||||
while (type_id < btf->start_id)
|
||||
btf = btf->base_btf;
|
||||
|
||||
type_id -= btf->start_id;
|
||||
if (type_id >= btf->nr_types)
|
||||
return NULL;
|
||||
return btf->types[type_id];
|
||||
}
|
||||
|
||||
@@ -1391,17 +1431,13 @@ static int btf_add_type(struct btf_verifier_env *env, struct btf_type *t)
|
||||
{
|
||||
struct btf *btf = env->btf;
|
||||
|
||||
/* < 2 because +1 for btf_void which is always in btf->types[0].
|
||||
* btf_void is not accounted in btf->nr_types because btf_void
|
||||
* does not come from the BTF file.
|
||||
*/
|
||||
if (btf->types_size - btf->nr_types < 2) {
|
||||
if (btf->types_size == btf->nr_types) {
|
||||
/* Expand 'types' array */
|
||||
|
||||
struct btf_type **new_types;
|
||||
u32 expand_by, new_size;
|
||||
|
||||
if (btf->types_size == BTF_MAX_TYPE) {
|
||||
if (btf->start_id + btf->types_size == BTF_MAX_TYPE) {
|
||||
btf_verifier_log(env, "Exceeded max num of types");
|
||||
return -E2BIG;
|
||||
}
|
||||
@@ -1415,18 +1451,23 @@ static int btf_add_type(struct btf_verifier_env *env, struct btf_type *t)
|
||||
if (!new_types)
|
||||
return -ENOMEM;
|
||||
|
||||
if (btf->nr_types == 0)
|
||||
new_types[0] = &btf_void;
|
||||
else
|
||||
if (btf->nr_types == 0) {
|
||||
if (!btf->base_btf) {
|
||||
/* lazily init VOID type */
|
||||
new_types[0] = &btf_void;
|
||||
btf->nr_types++;
|
||||
}
|
||||
} else {
|
||||
memcpy(new_types, btf->types,
|
||||
sizeof(*btf->types) * (btf->nr_types + 1));
|
||||
sizeof(*btf->types) * btf->nr_types);
|
||||
}
|
||||
|
||||
kvfree(btf->types);
|
||||
btf->types = new_types;
|
||||
btf->types_size = new_size;
|
||||
}
|
||||
|
||||
btf->types[++(btf->nr_types)] = t;
|
||||
btf->types[btf->nr_types++] = t;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1499,18 +1540,17 @@ static int env_resolve_init(struct btf_verifier_env *env)
|
||||
u32 *resolved_ids = NULL;
|
||||
u8 *visit_states = NULL;
|
||||
|
||||
/* +1 for btf_void */
|
||||
resolved_sizes = kvcalloc(nr_types + 1, sizeof(*resolved_sizes),
|
||||
resolved_sizes = kvcalloc(nr_types, sizeof(*resolved_sizes),
|
||||
GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!resolved_sizes)
|
||||
goto nomem;
|
||||
|
||||
resolved_ids = kvcalloc(nr_types + 1, sizeof(*resolved_ids),
|
||||
resolved_ids = kvcalloc(nr_types, sizeof(*resolved_ids),
|
||||
GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!resolved_ids)
|
||||
goto nomem;
|
||||
|
||||
visit_states = kvcalloc(nr_types + 1, sizeof(*visit_states),
|
||||
visit_states = kvcalloc(nr_types, sizeof(*visit_states),
|
||||
GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!visit_states)
|
||||
goto nomem;
|
||||
@@ -1562,21 +1602,27 @@ static bool env_type_is_resolve_sink(const struct btf_verifier_env *env,
|
||||
static bool env_type_is_resolved(const struct btf_verifier_env *env,
|
||||
u32 type_id)
|
||||
{
|
||||
return env->visit_states[type_id] == RESOLVED;
|
||||
/* base BTF types should be resolved by now */
|
||||
if (type_id < env->btf->start_id)
|
||||
return true;
|
||||
|
||||
return env->visit_states[type_id - env->btf->start_id] == RESOLVED;
|
||||
}
|
||||
|
||||
static int env_stack_push(struct btf_verifier_env *env,
|
||||
const struct btf_type *t, u32 type_id)
|
||||
{
|
||||
const struct btf *btf = env->btf;
|
||||
struct resolve_vertex *v;
|
||||
|
||||
if (env->top_stack == MAX_RESOLVE_DEPTH)
|
||||
return -E2BIG;
|
||||
|
||||
if (env->visit_states[type_id] != NOT_VISITED)
|
||||
if (type_id < btf->start_id
|
||||
|| env->visit_states[type_id - btf->start_id] != NOT_VISITED)
|
||||
return -EEXIST;
|
||||
|
||||
env->visit_states[type_id] = VISITED;
|
||||
env->visit_states[type_id - btf->start_id] = VISITED;
|
||||
|
||||
v = &env->stack[env->top_stack++];
|
||||
v->t = t;
|
||||
@@ -1606,6 +1652,7 @@ static void env_stack_pop_resolved(struct btf_verifier_env *env,
|
||||
u32 type_id = env->stack[--(env->top_stack)].type_id;
|
||||
struct btf *btf = env->btf;
|
||||
|
||||
type_id -= btf->start_id; /* adjust to local type id */
|
||||
btf->resolved_sizes[type_id] = resolved_size;
|
||||
btf->resolved_ids[type_id] = resolved_type_id;
|
||||
env->visit_states[type_id] = RESOLVED;
|
||||
@@ -1710,14 +1757,30 @@ btf_resolve_size(const struct btf *btf, const struct btf_type *type,
|
||||
return __btf_resolve_size(btf, type, type_size, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static u32 btf_resolved_type_id(const struct btf *btf, u32 type_id)
|
||||
{
|
||||
while (type_id < btf->start_id)
|
||||
btf = btf->base_btf;
|
||||
|
||||
return btf->resolved_ids[type_id - btf->start_id];
|
||||
}
|
||||
|
||||
/* The input param "type_id" must point to a needs_resolve type */
|
||||
static const struct btf_type *btf_type_id_resolve(const struct btf *btf,
|
||||
u32 *type_id)
|
||||
{
|
||||
*type_id = btf->resolved_ids[*type_id];
|
||||
*type_id = btf_resolved_type_id(btf, *type_id);
|
||||
return btf_type_by_id(btf, *type_id);
|
||||
}
|
||||
|
||||
static u32 btf_resolved_type_size(const struct btf *btf, u32 type_id)
|
||||
{
|
||||
while (type_id < btf->start_id)
|
||||
btf = btf->base_btf;
|
||||
|
||||
return btf->resolved_sizes[type_id - btf->start_id];
|
||||
}
|
||||
|
||||
const struct btf_type *btf_type_id_size(const struct btf *btf,
|
||||
u32 *type_id, u32 *ret_size)
|
||||
{
|
||||
@@ -1732,7 +1795,7 @@ const struct btf_type *btf_type_id_size(const struct btf *btf,
|
||||
if (btf_type_has_size(size_type)) {
|
||||
size = size_type->size;
|
||||
} else if (btf_type_is_array(size_type)) {
|
||||
size = btf->resolved_sizes[size_type_id];
|
||||
size = btf_resolved_type_size(btf, size_type_id);
|
||||
} else if (btf_type_is_ptr(size_type)) {
|
||||
size = sizeof(void *);
|
||||
} else {
|
||||
@@ -1740,14 +1803,14 @@ const struct btf_type *btf_type_id_size(const struct btf *btf,
|
||||
!btf_type_is_var(size_type)))
|
||||
return NULL;
|
||||
|
||||
size_type_id = btf->resolved_ids[size_type_id];
|
||||
size_type_id = btf_resolved_type_id(btf, size_type_id);
|
||||
size_type = btf_type_by_id(btf, size_type_id);
|
||||
if (btf_type_nosize_or_null(size_type))
|
||||
return NULL;
|
||||
else if (btf_type_has_size(size_type))
|
||||
size = size_type->size;
|
||||
else if (btf_type_is_array(size_type))
|
||||
size = btf->resolved_sizes[size_type_id];
|
||||
size = btf_resolved_type_size(btf, size_type_id);
|
||||
else if (btf_type_is_ptr(size_type))
|
||||
size = sizeof(void *);
|
||||
else
|
||||
@@ -3799,7 +3862,7 @@ static int btf_check_all_metas(struct btf_verifier_env *env)
|
||||
cur = btf->nohdr_data + hdr->type_off;
|
||||
end = cur + hdr->type_len;
|
||||
|
||||
env->log_type_id = 1;
|
||||
env->log_type_id = btf->base_btf ? btf->start_id : 1;
|
||||
while (cur < end) {
|
||||
struct btf_type *t = cur;
|
||||
s32 meta_size;
|
||||
@@ -3826,8 +3889,8 @@ static bool btf_resolve_valid(struct btf_verifier_env *env,
|
||||
return false;
|
||||
|
||||
if (btf_type_is_struct(t) || btf_type_is_datasec(t))
|
||||
return !btf->resolved_ids[type_id] &&
|
||||
!btf->resolved_sizes[type_id];
|
||||
return !btf_resolved_type_id(btf, type_id) &&
|
||||
!btf_resolved_type_size(btf, type_id);
|
||||
|
||||
if (btf_type_is_modifier(t) || btf_type_is_ptr(t) ||
|
||||
btf_type_is_var(t)) {
|
||||
@@ -3847,7 +3910,7 @@ static bool btf_resolve_valid(struct btf_verifier_env *env,
|
||||
elem_type = btf_type_id_size(btf, &elem_type_id, &elem_size);
|
||||
return elem_type && !btf_type_is_modifier(elem_type) &&
|
||||
(array->nelems * elem_size ==
|
||||
btf->resolved_sizes[type_id]);
|
||||
btf_resolved_type_size(btf, type_id));
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -3889,7 +3952,8 @@ static int btf_resolve(struct btf_verifier_env *env,
|
||||
static int btf_check_all_types(struct btf_verifier_env *env)
|
||||
{
|
||||
struct btf *btf = env->btf;
|
||||
u32 type_id;
|
||||
const struct btf_type *t;
|
||||
u32 type_id, i;
|
||||
int err;
|
||||
|
||||
err = env_resolve_init(env);
|
||||
@@ -3897,8 +3961,9 @@ static int btf_check_all_types(struct btf_verifier_env *env)
|
||||
return err;
|
||||
|
||||
env->phase++;
|
||||
for (type_id = 1; type_id <= btf->nr_types; type_id++) {
|
||||
const struct btf_type *t = btf_type_by_id(btf, type_id);
|
||||
for (i = btf->base_btf ? 0 : 1; i < btf->nr_types; i++) {
|
||||
type_id = btf->start_id + i;
|
||||
t = btf_type_by_id(btf, type_id);
|
||||
|
||||
env->log_type_id = type_id;
|
||||
if (btf_type_needs_resolve(t) &&
|
||||
@@ -3935,7 +4000,7 @@ static int btf_parse_type_sec(struct btf_verifier_env *env)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!hdr->type_len) {
|
||||
if (!env->btf->base_btf && !hdr->type_len) {
|
||||
btf_verifier_log(env, "No type found");
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -3962,13 +4027,18 @@ static int btf_parse_str_sec(struct btf_verifier_env *env)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!hdr->str_len || hdr->str_len - 1 > BTF_MAX_NAME_OFFSET ||
|
||||
start[0] || end[-1]) {
|
||||
btf->strings = start;
|
||||
|
||||
if (btf->base_btf && !hdr->str_len)
|
||||
return 0;
|
||||
if (!hdr->str_len || hdr->str_len - 1 > BTF_MAX_NAME_OFFSET || end[-1]) {
|
||||
btf_verifier_log(env, "Invalid string section");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!btf->base_btf && start[0]) {
|
||||
btf_verifier_log(env, "Invalid string section");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
btf->strings = start;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -4363,6 +4433,8 @@ struct btf *btf_parse_vmlinux(void)
|
||||
|
||||
btf->data = __start_BTF;
|
||||
btf->data_size = __stop_BTF - __start_BTF;
|
||||
btf->kernel_btf = true;
|
||||
snprintf(btf->name, sizeof(btf->name), "vmlinux");
|
||||
|
||||
err = btf_parse_hdr(env);
|
||||
if (err)
|
||||
@@ -4388,8 +4460,13 @@ struct btf *btf_parse_vmlinux(void)
|
||||
|
||||
bpf_struct_ops_init(btf, log);
|
||||
|
||||
btf_verifier_env_free(env);
|
||||
refcount_set(&btf->refcnt, 1);
|
||||
|
||||
err = btf_alloc_id(btf);
|
||||
if (err)
|
||||
goto errout;
|
||||
|
||||
btf_verifier_env_free(env);
|
||||
return btf;
|
||||
|
||||
errout:
|
||||
@@ -4401,6 +4478,79 @@ errout:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
|
||||
|
||||
static struct btf *btf_parse_module(const char *module_name, const void *data, unsigned int data_size)
|
||||
{
|
||||
struct btf_verifier_env *env = NULL;
|
||||
struct bpf_verifier_log *log;
|
||||
struct btf *btf = NULL, *base_btf;
|
||||
int err;
|
||||
|
||||
base_btf = bpf_get_btf_vmlinux();
|
||||
if (IS_ERR(base_btf))
|
||||
return base_btf;
|
||||
if (!base_btf)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
env = kzalloc(sizeof(*env), GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!env)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
log = &env->log;
|
||||
log->level = BPF_LOG_KERNEL;
|
||||
|
||||
btf = kzalloc(sizeof(*btf), GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!btf) {
|
||||
err = -ENOMEM;
|
||||
goto errout;
|
||||
}
|
||||
env->btf = btf;
|
||||
|
||||
btf->base_btf = base_btf;
|
||||
btf->start_id = base_btf->nr_types;
|
||||
btf->start_str_off = base_btf->hdr.str_len;
|
||||
btf->kernel_btf = true;
|
||||
snprintf(btf->name, sizeof(btf->name), "%s", module_name);
|
||||
|
||||
btf->data = kvmalloc(data_size, GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!btf->data) {
|
||||
err = -ENOMEM;
|
||||
goto errout;
|
||||
}
|
||||
memcpy(btf->data, data, data_size);
|
||||
btf->data_size = data_size;
|
||||
|
||||
err = btf_parse_hdr(env);
|
||||
if (err)
|
||||
goto errout;
|
||||
|
||||
btf->nohdr_data = btf->data + btf->hdr.hdr_len;
|
||||
|
||||
err = btf_parse_str_sec(env);
|
||||
if (err)
|
||||
goto errout;
|
||||
|
||||
err = btf_check_all_metas(env);
|
||||
if (err)
|
||||
goto errout;
|
||||
|
||||
btf_verifier_env_free(env);
|
||||
refcount_set(&btf->refcnt, 1);
|
||||
return btf;
|
||||
|
||||
errout:
|
||||
btf_verifier_env_free(env);
|
||||
if (btf) {
|
||||
kvfree(btf->data);
|
||||
kvfree(btf->types);
|
||||
kfree(btf);
|
||||
}
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_DEBUG_INFO_BTF_MODULES */
|
||||
|
||||
struct btf *bpf_prog_get_target_btf(const struct bpf_prog *prog)
|
||||
{
|
||||
struct bpf_prog *tgt_prog = prog->aux->dst_prog;
|
||||
@@ -4909,7 +5059,7 @@ static int __get_type_size(struct btf *btf, u32 btf_id,
|
||||
while (t && btf_type_is_modifier(t))
|
||||
t = btf_type_by_id(btf, t->type);
|
||||
if (!t) {
|
||||
*bad_type = btf->types[0];
|
||||
*bad_type = btf_type_by_id(btf, 0);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (btf_type_is_ptr(t))
|
||||
@@ -5487,7 +5637,9 @@ int btf_get_info_by_fd(const struct btf *btf,
|
||||
struct bpf_btf_info info;
|
||||
u32 info_copy, btf_copy;
|
||||
void __user *ubtf;
|
||||
u32 uinfo_len;
|
||||
char __user *uname;
|
||||
u32 uinfo_len, uname_len, name_len;
|
||||
int ret = 0;
|
||||
|
||||
uinfo = u64_to_user_ptr(attr->info.info);
|
||||
uinfo_len = attr->info.info_len;
|
||||
@@ -5504,11 +5656,37 @@ int btf_get_info_by_fd(const struct btf *btf,
|
||||
return -EFAULT;
|
||||
info.btf_size = btf->data_size;
|
||||
|
||||
info.kernel_btf = btf->kernel_btf;
|
||||
|
||||
uname = u64_to_user_ptr(info.name);
|
||||
uname_len = info.name_len;
|
||||
if (!uname ^ !uname_len)
|
||||
return -EINVAL;
|
||||
|
||||
name_len = strlen(btf->name);
|
||||
info.name_len = name_len;
|
||||
|
||||
if (uname) {
|
||||
if (uname_len >= name_len + 1) {
|
||||
if (copy_to_user(uname, btf->name, name_len + 1))
|
||||
return -EFAULT;
|
||||
} else {
|
||||
char zero = '\0';
|
||||
|
||||
if (copy_to_user(uname, btf->name, uname_len - 1))
|
||||
return -EFAULT;
|
||||
if (put_user(zero, uname + uname_len - 1))
|
||||
return -EFAULT;
|
||||
/* let user-space know about too short buffer */
|
||||
ret = -ENOSPC;
|
||||
}
|
||||
}
|
||||
|
||||
if (copy_to_user(uinfo, &info, info_copy) ||
|
||||
put_user(info_copy, &uattr->info.info_len))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btf_get_fd_by_id(u32 id)
|
||||
@@ -5548,3 +5726,126 @@ bool btf_id_set_contains(const struct btf_id_set *set, u32 id)
|
||||
{
|
||||
return bsearch(&id, set->ids, set->cnt, sizeof(u32), btf_id_cmp_func) != NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
|
||||
struct btf_module {
|
||||
struct list_head list;
|
||||
struct module *module;
|
||||
struct btf *btf;
|
||||
struct bin_attribute *sysfs_attr;
|
||||
};
|
||||
|
||||
static LIST_HEAD(btf_modules);
|
||||
static DEFINE_MUTEX(btf_module_mutex);
|
||||
|
||||
static ssize_t
|
||||
btf_module_read(struct file *file, struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t off, size_t len)
|
||||
{
|
||||
const struct btf *btf = bin_attr->private;
|
||||
|
||||
memcpy(buf, btf->data + off, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
static int btf_module_notify(struct notifier_block *nb, unsigned long op,
|
||||
void *module)
|
||||
{
|
||||
struct btf_module *btf_mod, *tmp;
|
||||
struct module *mod = module;
|
||||
struct btf *btf;
|
||||
int err = 0;
|
||||
|
||||
if (mod->btf_data_size == 0 ||
|
||||
(op != MODULE_STATE_COMING && op != MODULE_STATE_GOING))
|
||||
goto out;
|
||||
|
||||
switch (op) {
|
||||
case MODULE_STATE_COMING:
|
||||
btf_mod = kzalloc(sizeof(*btf_mod), GFP_KERNEL);
|
||||
if (!btf_mod) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
btf = btf_parse_module(mod->name, mod->btf_data, mod->btf_data_size);
|
||||
if (IS_ERR(btf)) {
|
||||
pr_warn("failed to validate module [%s] BTF: %ld\n",
|
||||
mod->name, PTR_ERR(btf));
|
||||
kfree(btf_mod);
|
||||
err = PTR_ERR(btf);
|
||||
goto out;
|
||||
}
|
||||
err = btf_alloc_id(btf);
|
||||
if (err) {
|
||||
btf_free(btf);
|
||||
kfree(btf_mod);
|
||||
goto out;
|
||||
}
|
||||
|
||||
mutex_lock(&btf_module_mutex);
|
||||
btf_mod->module = module;
|
||||
btf_mod->btf = btf;
|
||||
list_add(&btf_mod->list, &btf_modules);
|
||||
mutex_unlock(&btf_module_mutex);
|
||||
|
||||
if (IS_ENABLED(CONFIG_SYSFS)) {
|
||||
struct bin_attribute *attr;
|
||||
|
||||
attr = kzalloc(sizeof(*attr), GFP_KERNEL);
|
||||
if (!attr)
|
||||
goto out;
|
||||
|
||||
sysfs_bin_attr_init(attr);
|
||||
attr->attr.name = btf->name;
|
||||
attr->attr.mode = 0444;
|
||||
attr->size = btf->data_size;
|
||||
attr->private = btf;
|
||||
attr->read = btf_module_read;
|
||||
|
||||
err = sysfs_create_bin_file(btf_kobj, attr);
|
||||
if (err) {
|
||||
pr_warn("failed to register module [%s] BTF in sysfs: %d\n",
|
||||
mod->name, err);
|
||||
kfree(attr);
|
||||
err = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
btf_mod->sysfs_attr = attr;
|
||||
}
|
||||
|
||||
break;
|
||||
case MODULE_STATE_GOING:
|
||||
mutex_lock(&btf_module_mutex);
|
||||
list_for_each_entry_safe(btf_mod, tmp, &btf_modules, list) {
|
||||
if (btf_mod->module != module)
|
||||
continue;
|
||||
|
||||
list_del(&btf_mod->list);
|
||||
if (btf_mod->sysfs_attr)
|
||||
sysfs_remove_bin_file(btf_kobj, btf_mod->sysfs_attr);
|
||||
btf_put(btf_mod->btf);
|
||||
kfree(btf_mod->sysfs_attr);
|
||||
kfree(btf_mod);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&btf_module_mutex);
|
||||
break;
|
||||
}
|
||||
out:
|
||||
return notifier_from_errno(err);
|
||||
}
|
||||
|
||||
static struct notifier_block btf_module_nb = {
|
||||
.notifier_call = btf_module_notify,
|
||||
};
|
||||
|
||||
static int __init btf_module_init(void)
|
||||
{
|
||||
register_module_notifier(&btf_module_nb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fs_initcall(btf_module_init);
|
||||
#endif /* CONFIG_DEBUG_INFO_BTF_MODULES */
|
||||
|
||||
+99
-45
@@ -86,6 +86,9 @@ struct bucket {
|
||||
};
|
||||
};
|
||||
|
||||
#define HASHTAB_MAP_LOCK_COUNT 8
|
||||
#define HASHTAB_MAP_LOCK_MASK (HASHTAB_MAP_LOCK_COUNT - 1)
|
||||
|
||||
struct bpf_htab {
|
||||
struct bpf_map map;
|
||||
struct bucket *buckets;
|
||||
@@ -99,6 +102,8 @@ struct bpf_htab {
|
||||
u32 n_buckets; /* number of hash buckets */
|
||||
u32 elem_size; /* size of each element in bytes */
|
||||
u32 hashrnd;
|
||||
struct lock_class_key lockdep_key;
|
||||
int __percpu *map_locked[HASHTAB_MAP_LOCK_COUNT];
|
||||
};
|
||||
|
||||
/* each htab element is struct htab_elem + key + value */
|
||||
@@ -138,33 +143,53 @@ static void htab_init_buckets(struct bpf_htab *htab)
|
||||
|
||||
for (i = 0; i < htab->n_buckets; i++) {
|
||||
INIT_HLIST_NULLS_HEAD(&htab->buckets[i].head, i);
|
||||
if (htab_use_raw_lock(htab))
|
||||
if (htab_use_raw_lock(htab)) {
|
||||
raw_spin_lock_init(&htab->buckets[i].raw_lock);
|
||||
else
|
||||
lockdep_set_class(&htab->buckets[i].raw_lock,
|
||||
&htab->lockdep_key);
|
||||
} else {
|
||||
spin_lock_init(&htab->buckets[i].lock);
|
||||
lockdep_set_class(&htab->buckets[i].lock,
|
||||
&htab->lockdep_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline unsigned long htab_lock_bucket(const struct bpf_htab *htab,
|
||||
struct bucket *b)
|
||||
static inline int htab_lock_bucket(const struct bpf_htab *htab,
|
||||
struct bucket *b, u32 hash,
|
||||
unsigned long *pflags)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
hash = hash & HASHTAB_MAP_LOCK_MASK;
|
||||
|
||||
migrate_disable();
|
||||
if (unlikely(__this_cpu_inc_return(*(htab->map_locked[hash])) != 1)) {
|
||||
__this_cpu_dec(*(htab->map_locked[hash]));
|
||||
migrate_enable();
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (htab_use_raw_lock(htab))
|
||||
raw_spin_lock_irqsave(&b->raw_lock, flags);
|
||||
else
|
||||
spin_lock_irqsave(&b->lock, flags);
|
||||
return flags;
|
||||
*pflags = flags;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void htab_unlock_bucket(const struct bpf_htab *htab,
|
||||
struct bucket *b,
|
||||
struct bucket *b, u32 hash,
|
||||
unsigned long flags)
|
||||
{
|
||||
hash = hash & HASHTAB_MAP_LOCK_MASK;
|
||||
if (htab_use_raw_lock(htab))
|
||||
raw_spin_unlock_irqrestore(&b->raw_lock, flags);
|
||||
else
|
||||
spin_unlock_irqrestore(&b->lock, flags);
|
||||
__this_cpu_dec(*(htab->map_locked[hash]));
|
||||
migrate_enable();
|
||||
}
|
||||
|
||||
static bool htab_lru_map_delete_node(void *arg, struct bpf_lru_node *node);
|
||||
@@ -390,17 +415,11 @@ static int htab_map_alloc_check(union bpf_attr *attr)
|
||||
attr->value_size == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (attr->key_size > MAX_BPF_STACK)
|
||||
/* eBPF programs initialize keys on stack, so they cannot be
|
||||
* larger than max stack size
|
||||
*/
|
||||
return -E2BIG;
|
||||
|
||||
if (attr->value_size >= KMALLOC_MAX_SIZE -
|
||||
MAX_BPF_STACK - sizeof(struct htab_elem))
|
||||
/* if value_size is bigger, the user space won't be able to
|
||||
* access the elements via bpf syscall. This check also makes
|
||||
* sure that the elem_size doesn't overflow and it's
|
||||
if ((u64)attr->key_size + attr->value_size >= KMALLOC_MAX_SIZE -
|
||||
sizeof(struct htab_elem))
|
||||
/* if key_size + value_size is bigger, the user space won't be
|
||||
* able to access the elements via bpf syscall. This check
|
||||
* also makes sure that the elem_size doesn't overflow and it's
|
||||
* kmalloc-able later in htab_map_update_elem()
|
||||
*/
|
||||
return -E2BIG;
|
||||
@@ -422,13 +441,15 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr)
|
||||
bool percpu_lru = (attr->map_flags & BPF_F_NO_COMMON_LRU);
|
||||
bool prealloc = !(attr->map_flags & BPF_F_NO_PREALLOC);
|
||||
struct bpf_htab *htab;
|
||||
int err, i;
|
||||
u64 cost;
|
||||
int err;
|
||||
|
||||
htab = kzalloc(sizeof(*htab), GFP_USER);
|
||||
if (!htab)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
lockdep_register_key(&htab->lockdep_key);
|
||||
|
||||
bpf_map_init_from_attr(&htab->map, attr);
|
||||
|
||||
if (percpu_lru) {
|
||||
@@ -480,6 +501,13 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr)
|
||||
if (!htab->buckets)
|
||||
goto free_charge;
|
||||
|
||||
for (i = 0; i < HASHTAB_MAP_LOCK_COUNT; i++) {
|
||||
htab->map_locked[i] = __alloc_percpu_gfp(sizeof(int),
|
||||
sizeof(int), GFP_USER);
|
||||
if (!htab->map_locked[i])
|
||||
goto free_map_locked;
|
||||
}
|
||||
|
||||
if (htab->map.map_flags & BPF_F_ZERO_SEED)
|
||||
htab->hashrnd = 0;
|
||||
else
|
||||
@@ -490,7 +518,7 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr)
|
||||
if (prealloc) {
|
||||
err = prealloc_init(htab);
|
||||
if (err)
|
||||
goto free_buckets;
|
||||
goto free_map_locked;
|
||||
|
||||
if (!percpu && !lru) {
|
||||
/* lru itself can remove the least used element, so
|
||||
@@ -506,11 +534,14 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr)
|
||||
|
||||
free_prealloc:
|
||||
prealloc_destroy(htab);
|
||||
free_buckets:
|
||||
free_map_locked:
|
||||
for (i = 0; i < HASHTAB_MAP_LOCK_COUNT; i++)
|
||||
free_percpu(htab->map_locked[i]);
|
||||
bpf_map_area_free(htab->buckets);
|
||||
free_charge:
|
||||
bpf_map_charge_finish(&htab->map.memory);
|
||||
free_htab:
|
||||
lockdep_unregister_key(&htab->lockdep_key);
|
||||
kfree(htab);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
@@ -687,12 +718,15 @@ static bool htab_lru_map_delete_node(void *arg, struct bpf_lru_node *node)
|
||||
struct hlist_nulls_node *n;
|
||||
unsigned long flags;
|
||||
struct bucket *b;
|
||||
int ret;
|
||||
|
||||
tgt_l = container_of(node, struct htab_elem, lru_node);
|
||||
b = __select_bucket(htab, tgt_l->hash);
|
||||
head = &b->head;
|
||||
|
||||
flags = htab_lock_bucket(htab, b);
|
||||
ret = htab_lock_bucket(htab, b, tgt_l->hash, &flags);
|
||||
if (ret)
|
||||
return false;
|
||||
|
||||
hlist_nulls_for_each_entry_rcu(l, n, head, hash_node)
|
||||
if (l == tgt_l) {
|
||||
@@ -700,7 +734,7 @@ static bool htab_lru_map_delete_node(void *arg, struct bpf_lru_node *node)
|
||||
break;
|
||||
}
|
||||
|
||||
htab_unlock_bucket(htab, b, flags);
|
||||
htab_unlock_bucket(htab, b, tgt_l->hash, flags);
|
||||
|
||||
return l == tgt_l;
|
||||
}
|
||||
@@ -998,7 +1032,9 @@ static int htab_map_update_elem(struct bpf_map *map, void *key, void *value,
|
||||
*/
|
||||
}
|
||||
|
||||
flags = htab_lock_bucket(htab, b);
|
||||
ret = htab_lock_bucket(htab, b, hash, &flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
l_old = lookup_elem_raw(head, hash, key, key_size);
|
||||
|
||||
@@ -1039,7 +1075,7 @@ static int htab_map_update_elem(struct bpf_map *map, void *key, void *value,
|
||||
}
|
||||
ret = 0;
|
||||
err:
|
||||
htab_unlock_bucket(htab, b, flags);
|
||||
htab_unlock_bucket(htab, b, hash, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1077,7 +1113,9 @@ static int htab_lru_map_update_elem(struct bpf_map *map, void *key, void *value,
|
||||
return -ENOMEM;
|
||||
memcpy(l_new->key + round_up(map->key_size, 8), value, map->value_size);
|
||||
|
||||
flags = htab_lock_bucket(htab, b);
|
||||
ret = htab_lock_bucket(htab, b, hash, &flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
l_old = lookup_elem_raw(head, hash, key, key_size);
|
||||
|
||||
@@ -1096,7 +1134,7 @@ static int htab_lru_map_update_elem(struct bpf_map *map, void *key, void *value,
|
||||
ret = 0;
|
||||
|
||||
err:
|
||||
htab_unlock_bucket(htab, b, flags);
|
||||
htab_unlock_bucket(htab, b, hash, flags);
|
||||
|
||||
if (ret)
|
||||
bpf_lru_push_free(&htab->lru, &l_new->lru_node);
|
||||
@@ -1131,7 +1169,9 @@ static int __htab_percpu_map_update_elem(struct bpf_map *map, void *key,
|
||||
b = __select_bucket(htab, hash);
|
||||
head = &b->head;
|
||||
|
||||
flags = htab_lock_bucket(htab, b);
|
||||
ret = htab_lock_bucket(htab, b, hash, &flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
l_old = lookup_elem_raw(head, hash, key, key_size);
|
||||
|
||||
@@ -1154,7 +1194,7 @@ static int __htab_percpu_map_update_elem(struct bpf_map *map, void *key,
|
||||
}
|
||||
ret = 0;
|
||||
err:
|
||||
htab_unlock_bucket(htab, b, flags);
|
||||
htab_unlock_bucket(htab, b, hash, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1194,7 +1234,9 @@ static int __htab_lru_percpu_map_update_elem(struct bpf_map *map, void *key,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
flags = htab_lock_bucket(htab, b);
|
||||
ret = htab_lock_bucket(htab, b, hash, &flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
l_old = lookup_elem_raw(head, hash, key, key_size);
|
||||
|
||||
@@ -1216,7 +1258,7 @@ static int __htab_lru_percpu_map_update_elem(struct bpf_map *map, void *key,
|
||||
}
|
||||
ret = 0;
|
||||
err:
|
||||
htab_unlock_bucket(htab, b, flags);
|
||||
htab_unlock_bucket(htab, b, hash, flags);
|
||||
if (l_new)
|
||||
bpf_lru_push_free(&htab->lru, &l_new->lru_node);
|
||||
return ret;
|
||||
@@ -1244,7 +1286,7 @@ static int htab_map_delete_elem(struct bpf_map *map, void *key)
|
||||
struct htab_elem *l;
|
||||
unsigned long flags;
|
||||
u32 hash, key_size;
|
||||
int ret = -ENOENT;
|
||||
int ret;
|
||||
|
||||
WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held());
|
||||
|
||||
@@ -1254,17 +1296,20 @@ static int htab_map_delete_elem(struct bpf_map *map, void *key)
|
||||
b = __select_bucket(htab, hash);
|
||||
head = &b->head;
|
||||
|
||||
flags = htab_lock_bucket(htab, b);
|
||||
ret = htab_lock_bucket(htab, b, hash, &flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
l = lookup_elem_raw(head, hash, key, key_size);
|
||||
|
||||
if (l) {
|
||||
hlist_nulls_del_rcu(&l->hash_node);
|
||||
free_htab_elem(htab, l);
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = -ENOENT;
|
||||
}
|
||||
|
||||
htab_unlock_bucket(htab, b, flags);
|
||||
htab_unlock_bucket(htab, b, hash, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1276,7 +1321,7 @@ static int htab_lru_map_delete_elem(struct bpf_map *map, void *key)
|
||||
struct htab_elem *l;
|
||||
unsigned long flags;
|
||||
u32 hash, key_size;
|
||||
int ret = -ENOENT;
|
||||
int ret;
|
||||
|
||||
WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held());
|
||||
|
||||
@@ -1286,16 +1331,18 @@ static int htab_lru_map_delete_elem(struct bpf_map *map, void *key)
|
||||
b = __select_bucket(htab, hash);
|
||||
head = &b->head;
|
||||
|
||||
flags = htab_lock_bucket(htab, b);
|
||||
ret = htab_lock_bucket(htab, b, hash, &flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
l = lookup_elem_raw(head, hash, key, key_size);
|
||||
|
||||
if (l) {
|
||||
if (l)
|
||||
hlist_nulls_del_rcu(&l->hash_node);
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
ret = -ENOENT;
|
||||
|
||||
htab_unlock_bucket(htab, b, flags);
|
||||
htab_unlock_bucket(htab, b, hash, flags);
|
||||
if (l)
|
||||
bpf_lru_push_free(&htab->lru, &l->lru_node);
|
||||
return ret;
|
||||
@@ -1321,6 +1368,7 @@ static void delete_all_elements(struct bpf_htab *htab)
|
||||
static void htab_map_free(struct bpf_map *map)
|
||||
{
|
||||
struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
|
||||
int i;
|
||||
|
||||
/* bpf_free_used_maps() or close(map_fd) will trigger this map_free callback.
|
||||
* bpf_free_used_maps() is called after bpf prog is no longer executing.
|
||||
@@ -1338,6 +1386,9 @@ static void htab_map_free(struct bpf_map *map)
|
||||
|
||||
free_percpu(htab->extra_elems);
|
||||
bpf_map_area_free(htab->buckets);
|
||||
for (i = 0; i < HASHTAB_MAP_LOCK_COUNT; i++)
|
||||
free_percpu(htab->map_locked[i]);
|
||||
lockdep_unregister_key(&htab->lockdep_key);
|
||||
kfree(htab);
|
||||
}
|
||||
|
||||
@@ -1441,8 +1492,11 @@ again_nocopy:
|
||||
b = &htab->buckets[batch];
|
||||
head = &b->head;
|
||||
/* do not grab the lock unless need it (bucket_cnt > 0). */
|
||||
if (locked)
|
||||
flags = htab_lock_bucket(htab, b);
|
||||
if (locked) {
|
||||
ret = htab_lock_bucket(htab, b, batch, &flags);
|
||||
if (ret)
|
||||
goto next_batch;
|
||||
}
|
||||
|
||||
bucket_cnt = 0;
|
||||
hlist_nulls_for_each_entry_rcu(l, n, head, hash_node)
|
||||
@@ -1459,7 +1513,7 @@ again_nocopy:
|
||||
/* Note that since bucket_cnt > 0 here, it is implicit
|
||||
* that the locked was grabbed, so release it.
|
||||
*/
|
||||
htab_unlock_bucket(htab, b, flags);
|
||||
htab_unlock_bucket(htab, b, batch, flags);
|
||||
rcu_read_unlock();
|
||||
bpf_enable_instrumentation();
|
||||
goto after_loop;
|
||||
@@ -1470,7 +1524,7 @@ again_nocopy:
|
||||
/* Note that since bucket_cnt > 0 here, it is implicit
|
||||
* that the locked was grabbed, so release it.
|
||||
*/
|
||||
htab_unlock_bucket(htab, b, flags);
|
||||
htab_unlock_bucket(htab, b, batch, flags);
|
||||
rcu_read_unlock();
|
||||
bpf_enable_instrumentation();
|
||||
kvfree(keys);
|
||||
@@ -1523,7 +1577,7 @@ again_nocopy:
|
||||
dst_val += value_size;
|
||||
}
|
||||
|
||||
htab_unlock_bucket(htab, b, flags);
|
||||
htab_unlock_bucket(htab, b, batch, flags);
|
||||
locked = false;
|
||||
|
||||
while (node_to_free) {
|
||||
|
||||
@@ -773,7 +773,8 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf,
|
||||
map->map_type != BPF_MAP_TYPE_ARRAY &&
|
||||
map->map_type != BPF_MAP_TYPE_CGROUP_STORAGE &&
|
||||
map->map_type != BPF_MAP_TYPE_SK_STORAGE &&
|
||||
map->map_type != BPF_MAP_TYPE_INODE_STORAGE)
|
||||
map->map_type != BPF_MAP_TYPE_INODE_STORAGE &&
|
||||
map->map_type != BPF_MAP_TYPE_TASK_STORAGE)
|
||||
return -ENOTSUPP;
|
||||
if (map->spin_lock_off + sizeof(struct bpf_spin_lock) >
|
||||
map->value_size) {
|
||||
|
||||
@@ -26,7 +26,7 @@ static struct bin_attribute bin_attr_btf_vmlinux __ro_after_init = {
|
||||
.read = btf_vmlinux_read,
|
||||
};
|
||||
|
||||
static struct kobject *btf_kobj;
|
||||
struct kobject *btf_kobj;
|
||||
|
||||
static int __init btf_vmlinux_init(void)
|
||||
{
|
||||
|
||||
@@ -337,6 +337,7 @@ static const struct bpf_iter_seq_info task_seq_info = {
|
||||
|
||||
static struct bpf_iter_reg task_reg_info = {
|
||||
.target = "task",
|
||||
.feature = BPF_ITER_RESCHED,
|
||||
.ctx_arg_info_size = 1,
|
||||
.ctx_arg_info = {
|
||||
{ offsetof(struct bpf_iter__task, task),
|
||||
@@ -354,6 +355,7 @@ static const struct bpf_iter_seq_info task_file_seq_info = {
|
||||
|
||||
static struct bpf_iter_reg task_file_reg_info = {
|
||||
.target = "task_file",
|
||||
.feature = BPF_ITER_RESCHED,
|
||||
.ctx_arg_info_size = 2,
|
||||
.ctx_arg_info = {
|
||||
{ offsetof(struct bpf_iter__task_file, task),
|
||||
|
||||
+138
-44
@@ -2739,7 +2739,9 @@ static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off,
|
||||
regno);
|
||||
return -EACCES;
|
||||
}
|
||||
err = __check_mem_access(env, regno, off, size, reg->range,
|
||||
|
||||
err = reg->range < 0 ? -EINVAL :
|
||||
__check_mem_access(env, regno, off, size, reg->range,
|
||||
zero_size_allowed);
|
||||
if (err) {
|
||||
verbose(env, "R%d offset is outside of the packet\n", regno);
|
||||
@@ -4469,6 +4471,11 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env,
|
||||
func_id != BPF_FUNC_inode_storage_delete)
|
||||
goto error;
|
||||
break;
|
||||
case BPF_MAP_TYPE_TASK_STORAGE:
|
||||
if (func_id != BPF_FUNC_task_storage_get &&
|
||||
func_id != BPF_FUNC_task_storage_delete)
|
||||
goto error;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -4547,6 +4554,11 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env,
|
||||
if (map->map_type != BPF_MAP_TYPE_INODE_STORAGE)
|
||||
goto error;
|
||||
break;
|
||||
case BPF_FUNC_task_storage_get:
|
||||
case BPF_FUNC_task_storage_delete:
|
||||
if (map->map_type != BPF_MAP_TYPE_TASK_STORAGE)
|
||||
goto error;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -4687,6 +4699,32 @@ static void clear_all_pkt_pointers(struct bpf_verifier_env *env)
|
||||
__clear_all_pkt_pointers(env, vstate->frame[i]);
|
||||
}
|
||||
|
||||
enum {
|
||||
AT_PKT_END = -1,
|
||||
BEYOND_PKT_END = -2,
|
||||
};
|
||||
|
||||
static void mark_pkt_end(struct bpf_verifier_state *vstate, int regn, bool range_open)
|
||||
{
|
||||
struct bpf_func_state *state = vstate->frame[vstate->curframe];
|
||||
struct bpf_reg_state *reg = &state->regs[regn];
|
||||
|
||||
if (reg->type != PTR_TO_PACKET)
|
||||
/* PTR_TO_PACKET_META is not supported yet */
|
||||
return;
|
||||
|
||||
/* The 'reg' is pkt > pkt_end or pkt >= pkt_end.
|
||||
* How far beyond pkt_end it goes is unknown.
|
||||
* if (!range_open) it's the case of pkt >= pkt_end
|
||||
* if (range_open) it's the case of pkt > pkt_end
|
||||
* hence this pointer is at least 1 byte bigger than pkt_end
|
||||
*/
|
||||
if (range_open)
|
||||
reg->range = BEYOND_PKT_END;
|
||||
else
|
||||
reg->range = AT_PKT_END;
|
||||
}
|
||||
|
||||
static void release_reg_references(struct bpf_verifier_env *env,
|
||||
struct bpf_func_state *state,
|
||||
int ref_obj_id)
|
||||
@@ -5176,11 +5214,14 @@ static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn
|
||||
PTR_TO_BTF_ID : PTR_TO_BTF_ID_OR_NULL;
|
||||
regs[BPF_REG_0].btf_id = meta.ret_btf_id;
|
||||
}
|
||||
} else if (fn->ret_type == RET_PTR_TO_BTF_ID_OR_NULL) {
|
||||
} else if (fn->ret_type == RET_PTR_TO_BTF_ID_OR_NULL ||
|
||||
fn->ret_type == RET_PTR_TO_BTF_ID) {
|
||||
int ret_btf_id;
|
||||
|
||||
mark_reg_known_zero(env, regs, BPF_REG_0);
|
||||
regs[BPF_REG_0].type = PTR_TO_BTF_ID_OR_NULL;
|
||||
regs[BPF_REG_0].type = fn->ret_type == RET_PTR_TO_BTF_ID ?
|
||||
PTR_TO_BTF_ID :
|
||||
PTR_TO_BTF_ID_OR_NULL;
|
||||
ret_btf_id = *fn->ret_btf_id;
|
||||
if (ret_btf_id == 0) {
|
||||
verbose(env, "invalid return type %d of func %s#%d\n",
|
||||
@@ -6695,7 +6736,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
|
||||
|
||||
static void __find_good_pkt_pointers(struct bpf_func_state *state,
|
||||
struct bpf_reg_state *dst_reg,
|
||||
enum bpf_reg_type type, u16 new_range)
|
||||
enum bpf_reg_type type, int new_range)
|
||||
{
|
||||
struct bpf_reg_state *reg;
|
||||
int i;
|
||||
@@ -6720,8 +6761,7 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *vstate,
|
||||
enum bpf_reg_type type,
|
||||
bool range_right_open)
|
||||
{
|
||||
u16 new_range;
|
||||
int i;
|
||||
int new_range, i;
|
||||
|
||||
if (dst_reg->off < 0 ||
|
||||
(dst_reg->off == 0 && range_right_open))
|
||||
@@ -6972,6 +7012,67 @@ static int is_branch_taken(struct bpf_reg_state *reg, u64 val, u8 opcode,
|
||||
return is_branch64_taken(reg, val, opcode);
|
||||
}
|
||||
|
||||
static int flip_opcode(u32 opcode)
|
||||
{
|
||||
/* How can we transform "a <op> b" into "b <op> a"? */
|
||||
static const u8 opcode_flip[16] = {
|
||||
/* these stay the same */
|
||||
[BPF_JEQ >> 4] = BPF_JEQ,
|
||||
[BPF_JNE >> 4] = BPF_JNE,
|
||||
[BPF_JSET >> 4] = BPF_JSET,
|
||||
/* these swap "lesser" and "greater" (L and G in the opcodes) */
|
||||
[BPF_JGE >> 4] = BPF_JLE,
|
||||
[BPF_JGT >> 4] = BPF_JLT,
|
||||
[BPF_JLE >> 4] = BPF_JGE,
|
||||
[BPF_JLT >> 4] = BPF_JGT,
|
||||
[BPF_JSGE >> 4] = BPF_JSLE,
|
||||
[BPF_JSGT >> 4] = BPF_JSLT,
|
||||
[BPF_JSLE >> 4] = BPF_JSGE,
|
||||
[BPF_JSLT >> 4] = BPF_JSGT
|
||||
};
|
||||
return opcode_flip[opcode >> 4];
|
||||
}
|
||||
|
||||
static int is_pkt_ptr_branch_taken(struct bpf_reg_state *dst_reg,
|
||||
struct bpf_reg_state *src_reg,
|
||||
u8 opcode)
|
||||
{
|
||||
struct bpf_reg_state *pkt;
|
||||
|
||||
if (src_reg->type == PTR_TO_PACKET_END) {
|
||||
pkt = dst_reg;
|
||||
} else if (dst_reg->type == PTR_TO_PACKET_END) {
|
||||
pkt = src_reg;
|
||||
opcode = flip_opcode(opcode);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pkt->range >= 0)
|
||||
return -1;
|
||||
|
||||
switch (opcode) {
|
||||
case BPF_JLE:
|
||||
/* pkt <= pkt_end */
|
||||
fallthrough;
|
||||
case BPF_JGT:
|
||||
/* pkt > pkt_end */
|
||||
if (pkt->range == BEYOND_PKT_END)
|
||||
/* pkt has at last one extra byte beyond pkt_end */
|
||||
return opcode == BPF_JGT;
|
||||
break;
|
||||
case BPF_JLT:
|
||||
/* pkt < pkt_end */
|
||||
fallthrough;
|
||||
case BPF_JGE:
|
||||
/* pkt >= pkt_end */
|
||||
if (pkt->range == BEYOND_PKT_END || pkt->range == AT_PKT_END)
|
||||
return opcode == BPF_JGE;
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Adjusts the register min/max values in the case that the dst_reg is the
|
||||
* variable register that we are working on, and src_reg is a constant or we're
|
||||
* simply doing a BPF_K check.
|
||||
@@ -7135,23 +7236,7 @@ static void reg_set_min_max_inv(struct bpf_reg_state *true_reg,
|
||||
u64 val, u32 val32,
|
||||
u8 opcode, bool is_jmp32)
|
||||
{
|
||||
/* How can we transform "a <op> b" into "b <op> a"? */
|
||||
static const u8 opcode_flip[16] = {
|
||||
/* these stay the same */
|
||||
[BPF_JEQ >> 4] = BPF_JEQ,
|
||||
[BPF_JNE >> 4] = BPF_JNE,
|
||||
[BPF_JSET >> 4] = BPF_JSET,
|
||||
/* these swap "lesser" and "greater" (L and G in the opcodes) */
|
||||
[BPF_JGE >> 4] = BPF_JLE,
|
||||
[BPF_JGT >> 4] = BPF_JLT,
|
||||
[BPF_JLE >> 4] = BPF_JGE,
|
||||
[BPF_JLT >> 4] = BPF_JGT,
|
||||
[BPF_JSGE >> 4] = BPF_JSLE,
|
||||
[BPF_JSGT >> 4] = BPF_JSLT,
|
||||
[BPF_JSLE >> 4] = BPF_JSGE,
|
||||
[BPF_JSLT >> 4] = BPF_JSGT
|
||||
};
|
||||
opcode = opcode_flip[opcode >> 4];
|
||||
opcode = flip_opcode(opcode);
|
||||
/* This uses zero as "not present in table"; luckily the zero opcode,
|
||||
* BPF_JA, can't get here.
|
||||
*/
|
||||
@@ -7333,6 +7418,7 @@ static bool try_match_pkt_pointers(const struct bpf_insn *insn,
|
||||
/* pkt_data' > pkt_end, pkt_meta' > pkt_data */
|
||||
find_good_pkt_pointers(this_branch, dst_reg,
|
||||
dst_reg->type, false);
|
||||
mark_pkt_end(other_branch, insn->dst_reg, true);
|
||||
} else if ((dst_reg->type == PTR_TO_PACKET_END &&
|
||||
src_reg->type == PTR_TO_PACKET) ||
|
||||
(reg_is_init_pkt_pointer(dst_reg, PTR_TO_PACKET) &&
|
||||
@@ -7340,6 +7426,7 @@ static bool try_match_pkt_pointers(const struct bpf_insn *insn,
|
||||
/* pkt_end > pkt_data', pkt_data > pkt_meta' */
|
||||
find_good_pkt_pointers(other_branch, src_reg,
|
||||
src_reg->type, true);
|
||||
mark_pkt_end(this_branch, insn->src_reg, false);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -7352,6 +7439,7 @@ static bool try_match_pkt_pointers(const struct bpf_insn *insn,
|
||||
/* pkt_data' < pkt_end, pkt_meta' < pkt_data */
|
||||
find_good_pkt_pointers(other_branch, dst_reg,
|
||||
dst_reg->type, true);
|
||||
mark_pkt_end(this_branch, insn->dst_reg, false);
|
||||
} else if ((dst_reg->type == PTR_TO_PACKET_END &&
|
||||
src_reg->type == PTR_TO_PACKET) ||
|
||||
(reg_is_init_pkt_pointer(dst_reg, PTR_TO_PACKET) &&
|
||||
@@ -7359,6 +7447,7 @@ static bool try_match_pkt_pointers(const struct bpf_insn *insn,
|
||||
/* pkt_end < pkt_data', pkt_data > pkt_meta' */
|
||||
find_good_pkt_pointers(this_branch, src_reg,
|
||||
src_reg->type, false);
|
||||
mark_pkt_end(other_branch, insn->src_reg, true);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -7371,6 +7460,7 @@ static bool try_match_pkt_pointers(const struct bpf_insn *insn,
|
||||
/* pkt_data' >= pkt_end, pkt_meta' >= pkt_data */
|
||||
find_good_pkt_pointers(this_branch, dst_reg,
|
||||
dst_reg->type, true);
|
||||
mark_pkt_end(other_branch, insn->dst_reg, false);
|
||||
} else if ((dst_reg->type == PTR_TO_PACKET_END &&
|
||||
src_reg->type == PTR_TO_PACKET) ||
|
||||
(reg_is_init_pkt_pointer(dst_reg, PTR_TO_PACKET) &&
|
||||
@@ -7378,6 +7468,7 @@ static bool try_match_pkt_pointers(const struct bpf_insn *insn,
|
||||
/* pkt_end >= pkt_data', pkt_data >= pkt_meta' */
|
||||
find_good_pkt_pointers(other_branch, src_reg,
|
||||
src_reg->type, false);
|
||||
mark_pkt_end(this_branch, insn->src_reg, true);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -7390,6 +7481,7 @@ static bool try_match_pkt_pointers(const struct bpf_insn *insn,
|
||||
/* pkt_data' <= pkt_end, pkt_meta' <= pkt_data */
|
||||
find_good_pkt_pointers(other_branch, dst_reg,
|
||||
dst_reg->type, false);
|
||||
mark_pkt_end(this_branch, insn->dst_reg, true);
|
||||
} else if ((dst_reg->type == PTR_TO_PACKET_END &&
|
||||
src_reg->type == PTR_TO_PACKET) ||
|
||||
(reg_is_init_pkt_pointer(dst_reg, PTR_TO_PACKET) &&
|
||||
@@ -7397,6 +7489,7 @@ static bool try_match_pkt_pointers(const struct bpf_insn *insn,
|
||||
/* pkt_end <= pkt_data', pkt_data <= pkt_meta' */
|
||||
find_good_pkt_pointers(this_branch, src_reg,
|
||||
src_reg->type, true);
|
||||
mark_pkt_end(other_branch, insn->src_reg, false);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -7496,6 +7589,10 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
|
||||
src_reg->var_off.value,
|
||||
opcode,
|
||||
is_jmp32);
|
||||
} else if (reg_is_pkt_pointer_any(dst_reg) &&
|
||||
reg_is_pkt_pointer_any(src_reg) &&
|
||||
!is_jmp32) {
|
||||
pred = is_pkt_ptr_branch_taken(dst_reg, src_reg, opcode);
|
||||
}
|
||||
|
||||
if (pred >= 0) {
|
||||
@@ -7504,7 +7601,8 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
|
||||
*/
|
||||
if (!__is_pointer_value(false, dst_reg))
|
||||
err = mark_chain_precision(env, insn->dst_reg);
|
||||
if (BPF_SRC(insn->code) == BPF_X && !err)
|
||||
if (BPF_SRC(insn->code) == BPF_X && !err &&
|
||||
!__is_pointer_value(false, src_reg))
|
||||
err = mark_chain_precision(env, insn->src_reg);
|
||||
if (err)
|
||||
return err;
|
||||
@@ -9719,11 +9817,21 @@ static int check_map_prog_compatibility(struct bpf_verifier_env *env,
|
||||
verbose(env, "trace type programs with run-time allocated hash maps are unsafe. Switch to preallocated hash maps.\n");
|
||||
}
|
||||
|
||||
if ((is_tracing_prog_type(prog_type) ||
|
||||
prog_type == BPF_PROG_TYPE_SOCKET_FILTER) &&
|
||||
map_value_has_spin_lock(map)) {
|
||||
verbose(env, "tracing progs cannot use bpf_spin_lock yet\n");
|
||||
return -EINVAL;
|
||||
if (map_value_has_spin_lock(map)) {
|
||||
if (prog_type == BPF_PROG_TYPE_SOCKET_FILTER) {
|
||||
verbose(env, "socket filter progs cannot use bpf_spin_lock yet\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (is_tracing_prog_type(prog_type)) {
|
||||
verbose(env, "tracing progs cannot use bpf_spin_lock yet\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (prog->aux->sleepable) {
|
||||
verbose(env, "sleepable progs cannot use bpf_spin_lock yet\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if ((bpf_prog_is_dev_bound(prog->aux) || bpf_map_is_dev_bound(map)) &&
|
||||
@@ -11454,20 +11562,6 @@ static int check_attach_modify_return(unsigned long addr, const char *func_name)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* non exhaustive list of sleepable bpf_lsm_*() functions */
|
||||
BTF_SET_START(btf_sleepable_lsm_hooks)
|
||||
#ifdef CONFIG_BPF_LSM
|
||||
BTF_ID(func, bpf_lsm_bprm_committed_creds)
|
||||
#else
|
||||
BTF_ID_UNUSED
|
||||
#endif
|
||||
BTF_SET_END(btf_sleepable_lsm_hooks)
|
||||
|
||||
static int check_sleepable_lsm_hook(u32 btf_id)
|
||||
{
|
||||
return btf_id_set_contains(&btf_sleepable_lsm_hooks, btf_id);
|
||||
}
|
||||
|
||||
/* list of non-sleepable functions that are otherwise on
|
||||
* ALLOW_ERROR_INJECTION list
|
||||
*/
|
||||
@@ -11689,7 +11783,7 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
|
||||
/* LSM progs check that they are attached to bpf_lsm_*() funcs.
|
||||
* Only some of them are sleepable.
|
||||
*/
|
||||
if (check_sleepable_lsm_hook(btf_id))
|
||||
if (bpf_lsm_is_sleepable_hook(btf_id))
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -380,6 +380,35 @@ static void *section_objs(const struct load_info *info,
|
||||
return (void *)info->sechdrs[sec].sh_addr;
|
||||
}
|
||||
|
||||
/* Find a module section: 0 means not found. Ignores SHF_ALLOC flag. */
|
||||
static unsigned int find_any_sec(const struct load_info *info, const char *name)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 1; i < info->hdr->e_shnum; i++) {
|
||||
Elf_Shdr *shdr = &info->sechdrs[i];
|
||||
if (strcmp(info->secstrings + shdr->sh_name, name) == 0)
|
||||
return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a module section, or NULL. Fill in number of "objects" in section.
|
||||
* Ignores SHF_ALLOC flag.
|
||||
*/
|
||||
static __maybe_unused void *any_section_objs(const struct load_info *info,
|
||||
const char *name,
|
||||
size_t object_size,
|
||||
unsigned int *num)
|
||||
{
|
||||
unsigned int sec = find_any_sec(info, name);
|
||||
|
||||
/* Section 0 has sh_addr 0 and sh_size 0. */
|
||||
*num = info->sechdrs[sec].sh_size / object_size;
|
||||
return (void *)info->sechdrs[sec].sh_addr;
|
||||
}
|
||||
|
||||
/* Provided by the linker */
|
||||
extern const struct kernel_symbol __start___ksymtab[];
|
||||
extern const struct kernel_symbol __stop___ksymtab[];
|
||||
@@ -3250,6 +3279,9 @@ static int find_module_sections(struct module *mod, struct load_info *info)
|
||||
sizeof(*mod->bpf_raw_events),
|
||||
&mod->num_bpf_raw_events);
|
||||
#endif
|
||||
#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
|
||||
mod->btf_data = any_section_objs(info, ".BTF", 1, &mod->btf_data_size);
|
||||
#endif
|
||||
#ifdef CONFIG_JUMP_LABEL
|
||||
mod->jump_entries = section_objs(info, "__jump_table",
|
||||
sizeof(*mod->jump_entries),
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/error-injection.h>
|
||||
#include <linux/btf_ids.h>
|
||||
#include <linux/bpf_lsm.h>
|
||||
|
||||
#include <net/bpf_sk_storage.h>
|
||||
|
||||
#include <uapi/linux/bpf.h>
|
||||
#include <uapi/linux/btf.h>
|
||||
@@ -1022,6 +1025,20 @@ const struct bpf_func_proto bpf_get_current_task_proto = {
|
||||
.ret_type = RET_INTEGER,
|
||||
};
|
||||
|
||||
BPF_CALL_0(bpf_get_current_task_btf)
|
||||
{
|
||||
return (unsigned long) current;
|
||||
}
|
||||
|
||||
BTF_ID_LIST_SINGLE(bpf_get_current_btf_ids, struct, task_struct)
|
||||
|
||||
static const struct bpf_func_proto bpf_get_current_task_btf_proto = {
|
||||
.func = bpf_get_current_task_btf,
|
||||
.gpl_only = true,
|
||||
.ret_type = RET_PTR_TO_BTF_ID,
|
||||
.ret_btf_id = &bpf_get_current_btf_ids[0],
|
||||
};
|
||||
|
||||
BPF_CALL_2(bpf_current_task_under_cgroup, struct bpf_map *, map, u32, idx)
|
||||
{
|
||||
struct bpf_array *array = container_of(map, struct bpf_array, map);
|
||||
@@ -1164,7 +1181,11 @@ BTF_SET_END(btf_allowlist_d_path)
|
||||
|
||||
static bool bpf_d_path_allowed(const struct bpf_prog *prog)
|
||||
{
|
||||
return btf_id_set_contains(&btf_allowlist_d_path, prog->aux->attach_btf_id);
|
||||
if (prog->type == BPF_PROG_TYPE_LSM)
|
||||
return bpf_lsm_is_sleepable_hook(prog->aux->attach_btf_id);
|
||||
|
||||
return btf_id_set_contains(&btf_allowlist_d_path,
|
||||
prog->aux->attach_btf_id);
|
||||
}
|
||||
|
||||
BTF_ID_LIST_SINGLE(bpf_d_path_btf_ids, struct, path)
|
||||
@@ -1265,6 +1286,8 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
||||
return &bpf_get_current_pid_tgid_proto;
|
||||
case BPF_FUNC_get_current_task:
|
||||
return &bpf_get_current_task_proto;
|
||||
case BPF_FUNC_get_current_task_btf:
|
||||
return &bpf_get_current_task_btf_proto;
|
||||
case BPF_FUNC_get_current_uid_gid:
|
||||
return &bpf_get_current_uid_gid_proto;
|
||||
case BPF_FUNC_get_current_comm:
|
||||
@@ -1719,6 +1742,10 @@ tracing_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
||||
return &bpf_skc_to_tcp_request_sock_proto;
|
||||
case BPF_FUNC_skc_to_udp6_sock:
|
||||
return &bpf_skc_to_udp6_sock_proto;
|
||||
case BPF_FUNC_sk_storage_get:
|
||||
return &bpf_sk_storage_get_tracing_proto;
|
||||
case BPF_FUNC_sk_storage_delete:
|
||||
return &bpf_sk_storage_delete_tracing_proto;
|
||||
#endif
|
||||
case BPF_FUNC_seq_printf:
|
||||
return prog->expected_attach_type == BPF_TRACE_ITER ?
|
||||
|
||||
Reference in New Issue
Block a user