Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller: 1) New ipset extensions for matching on destination MAC addresses, from Stefano Brivio. 2) Add ipv4 ttl and tos, plus ipv6 flow label and hop limit offloads to nfp driver. From Stefano Brivio. 3) Implement GRO for plain UDP sockets, from Paolo Abeni. 4) Lots of work from Michał Mirosław to eliminate the VLAN_TAG_PRESENT bit so that we could support the entire vlan_tci value. 5) Rework the IPSEC policy lookups to better optimize more usecases, from Florian Westphal. 6) Infrastructure changes eliminating direct manipulation of SKB lists wherever possible, and to always use the appropriate SKB list helpers. This work is still ongoing... 7) Lots of PHY driver and state machine improvements and simplifications, from Heiner Kallweit. 8) Various TSO deferral refinements, from Eric Dumazet. 9) Add ntuple filter support to aquantia driver, from Dmitry Bogdanov. 10) Batch dropping of XDP packets in tuntap, from Jason Wang. 11) Lots of cleanups and improvements to the r8169 driver from Heiner Kallweit, including support for ->xmit_more. This driver has been getting some much needed love since he started working on it. 12) Lots of new forwarding selftests from Petr Machata. 13) Enable VXLAN learning in mlxsw driver, from Ido Schimmel. 14) Packed ring support for virtio, from Tiwei Bie. 15) Add new Aquantia AQtion USB driver, from Dmitry Bezrukov. 16) Add XDP support to dpaa2-eth driver, from Ioana Ciocoi Radulescu. 17) Implement coalescing on TCP backlog queue, from Eric Dumazet. 18) Implement carrier change in tun driver, from Nicolas Dichtel. 19) Support msg_zerocopy in UDP, from Willem de Bruijn. 20) Significantly improve garbage collection of neighbor objects when the table has many PERMANENT entries, from David Ahern. 21) Remove egdev usage from nfp and mlx5, and remove the facility completely from the tree as it no longer has any users. From Oz Shlomo and others. 22) Add a NETDEV_PRE_CHANGEADDR so that drivers can veto the change and therefore abort the operation before the commit phase (which is the NETDEV_CHANGEADDR event). From Petr Machata. 23) Add indirect call wrappers to avoid retpoline overhead, and use them in the GRO code paths. From Paolo Abeni. 24) Add support for netlink FDB get operations, from Roopa Prabhu. 25) Support bloom filter in mlxsw driver, from Nir Dotan. 26) Add SKB extension infrastructure. This consolidates the handling of the auxiliary SKB data used by IPSEC and bridge netfilter, and is designed to support the needs to MPTCP which could be integrated in the future. 27) Lots of XDP TX optimizations in mlx5 from Tariq Toukan. * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1845 commits) net: dccp: fix kernel crash on module load drivers/net: appletalk/cops: remove redundant if statement and mask bnx2x: Fix NULL pointer dereference in bnx2x_del_all_vlans() on some hw net/net_namespace: Check the return value of register_pernet_subsys() net/netlink_compat: Fix a missing check of nla_parse_nested ieee802154: lowpan_header_create check must check daddr net/mlx4_core: drop useless LIST_HEAD mlxsw: spectrum: drop useless LIST_HEAD net/mlx5e: drop useless LIST_HEAD iptunnel: Set tun_flags in the iptunnel_metadata_reply from src net/mlx5e: fix semicolon.cocci warnings staging: octeon: fix build failure with XFRM enabled net: Revert recent Spectre-v1 patches. can: af_can: Fix Spectre v1 vulnerability packet: validate address length if non-zero nfc: af_nfc: Fix Spectre v1 vulnerability phonet: af_phonet: Fix Spectre v1 vulnerability net: core: Fix Spectre v1 vulnerability net: minor cleanup in skb_ext_add() net: drop the unused helper skb_ext_get() ...
This commit is contained in:
@@ -382,6 +382,7 @@ static void percpu_array_map_seq_show_elem(struct bpf_map *map, void *key,
|
||||
}
|
||||
|
||||
static int array_map_check_btf(const struct bpf_map *map,
|
||||
const struct btf *btf,
|
||||
const struct btf_type *key_type,
|
||||
const struct btf_type *value_type)
|
||||
{
|
||||
|
778
kernel/bpf/btf.c
778
kernel/bpf/btf.c
File diff suppressed because it is too large
Load Diff
@@ -21,12 +21,14 @@
|
||||
* Kris Katterjohn - Added many additional checks in bpf_check_classic()
|
||||
*/
|
||||
|
||||
#include <uapi/linux/btf.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/moduleloader.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/btf.h>
|
||||
#include <linux/frame.h>
|
||||
#include <linux/rbtree_latch.h>
|
||||
#include <linux/kallsyms.h>
|
||||
@@ -103,6 +105,91 @@ struct bpf_prog *bpf_prog_alloc(unsigned int size, gfp_t gfp_extra_flags)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bpf_prog_alloc);
|
||||
|
||||
int bpf_prog_alloc_jited_linfo(struct bpf_prog *prog)
|
||||
{
|
||||
if (!prog->aux->nr_linfo || !prog->jit_requested)
|
||||
return 0;
|
||||
|
||||
prog->aux->jited_linfo = kcalloc(prog->aux->nr_linfo,
|
||||
sizeof(*prog->aux->jited_linfo),
|
||||
GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!prog->aux->jited_linfo)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bpf_prog_free_jited_linfo(struct bpf_prog *prog)
|
||||
{
|
||||
kfree(prog->aux->jited_linfo);
|
||||
prog->aux->jited_linfo = NULL;
|
||||
}
|
||||
|
||||
void bpf_prog_free_unused_jited_linfo(struct bpf_prog *prog)
|
||||
{
|
||||
if (prog->aux->jited_linfo && !prog->aux->jited_linfo[0])
|
||||
bpf_prog_free_jited_linfo(prog);
|
||||
}
|
||||
|
||||
/* The jit engine is responsible to provide an array
|
||||
* for insn_off to the jited_off mapping (insn_to_jit_off).
|
||||
*
|
||||
* The idx to this array is the insn_off. Hence, the insn_off
|
||||
* here is relative to the prog itself instead of the main prog.
|
||||
* This array has one entry for each xlated bpf insn.
|
||||
*
|
||||
* jited_off is the byte off to the last byte of the jited insn.
|
||||
*
|
||||
* Hence, with
|
||||
* insn_start:
|
||||
* The first bpf insn off of the prog. The insn off
|
||||
* here is relative to the main prog.
|
||||
* e.g. if prog is a subprog, insn_start > 0
|
||||
* linfo_idx:
|
||||
* The prog's idx to prog->aux->linfo and jited_linfo
|
||||
*
|
||||
* jited_linfo[linfo_idx] = prog->bpf_func
|
||||
*
|
||||
* For i > linfo_idx,
|
||||
*
|
||||
* jited_linfo[i] = prog->bpf_func +
|
||||
* insn_to_jit_off[linfo[i].insn_off - insn_start - 1]
|
||||
*/
|
||||
void bpf_prog_fill_jited_linfo(struct bpf_prog *prog,
|
||||
const u32 *insn_to_jit_off)
|
||||
{
|
||||
u32 linfo_idx, insn_start, insn_end, nr_linfo, i;
|
||||
const struct bpf_line_info *linfo;
|
||||
void **jited_linfo;
|
||||
|
||||
if (!prog->aux->jited_linfo)
|
||||
/* Userspace did not provide linfo */
|
||||
return;
|
||||
|
||||
linfo_idx = prog->aux->linfo_idx;
|
||||
linfo = &prog->aux->linfo[linfo_idx];
|
||||
insn_start = linfo[0].insn_off;
|
||||
insn_end = insn_start + prog->len;
|
||||
|
||||
jited_linfo = &prog->aux->jited_linfo[linfo_idx];
|
||||
jited_linfo[0] = prog->bpf_func;
|
||||
|
||||
nr_linfo = prog->aux->nr_linfo - linfo_idx;
|
||||
|
||||
for (i = 1; i < nr_linfo && linfo[i].insn_off < insn_end; i++)
|
||||
/* The verifier ensures that linfo[i].insn_off is
|
||||
* strictly increasing
|
||||
*/
|
||||
jited_linfo[i] = prog->bpf_func +
|
||||
insn_to_jit_off[linfo[i].insn_off - insn_start - 1];
|
||||
}
|
||||
|
||||
void bpf_prog_free_linfo(struct bpf_prog *prog)
|
||||
{
|
||||
bpf_prog_free_jited_linfo(prog);
|
||||
kvfree(prog->aux->linfo);
|
||||
}
|
||||
|
||||
struct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size,
|
||||
gfp_t gfp_extra_flags)
|
||||
{
|
||||
@@ -292,6 +379,26 @@ static int bpf_adj_branches(struct bpf_prog *prog, u32 pos, u32 delta,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bpf_adj_linfo(struct bpf_prog *prog, u32 off, u32 delta)
|
||||
{
|
||||
struct bpf_line_info *linfo;
|
||||
u32 i, nr_linfo;
|
||||
|
||||
nr_linfo = prog->aux->nr_linfo;
|
||||
if (!nr_linfo || !delta)
|
||||
return;
|
||||
|
||||
linfo = prog->aux->linfo;
|
||||
|
||||
for (i = 0; i < nr_linfo; i++)
|
||||
if (off < linfo[i].insn_off)
|
||||
break;
|
||||
|
||||
/* Push all off < linfo[i].insn_off by delta */
|
||||
for (; i < nr_linfo; i++)
|
||||
linfo[i].insn_off += delta;
|
||||
}
|
||||
|
||||
struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
|
||||
const struct bpf_insn *patch, u32 len)
|
||||
{
|
||||
@@ -347,6 +454,8 @@ struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
|
||||
*/
|
||||
BUG_ON(bpf_adj_branches(prog_adj, off, insn_delta, false));
|
||||
|
||||
bpf_adj_linfo(prog_adj, off, insn_delta);
|
||||
|
||||
return prog_adj;
|
||||
}
|
||||
|
||||
@@ -388,6 +497,8 @@ bpf_get_prog_addr_region(const struct bpf_prog *prog,
|
||||
static void bpf_get_prog_name(const struct bpf_prog *prog, char *sym)
|
||||
{
|
||||
const char *end = sym + KSYM_NAME_LEN;
|
||||
const struct btf_type *type;
|
||||
const char *func_name;
|
||||
|
||||
BUILD_BUG_ON(sizeof("bpf_prog_") +
|
||||
sizeof(prog->tag) * 2 +
|
||||
@@ -402,6 +513,16 @@ static void bpf_get_prog_name(const struct bpf_prog *prog, char *sym)
|
||||
|
||||
sym += snprintf(sym, KSYM_NAME_LEN, "bpf_prog_");
|
||||
sym = bin2hex(sym, prog->tag, sizeof(prog->tag));
|
||||
|
||||
/* prog->aux->name will be ignored if full btf name is available */
|
||||
if (prog->aux->func_info_cnt) {
|
||||
type = btf_type_by_id(prog->aux->btf,
|
||||
prog->aux->func_info[prog->aux->func_idx].type_id);
|
||||
func_name = btf_name_by_offset(prog->aux->btf, type->name_off);
|
||||
snprintf(sym, (size_t)(end - sym), "_%s", func_name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (prog->aux->name[0])
|
||||
snprintf(sym, (size_t)(end - sym), "_%s", prog->aux->name);
|
||||
else
|
||||
@@ -618,6 +739,16 @@ static void bpf_jit_uncharge_modmem(u32 pages)
|
||||
atomic_long_sub(pages, &bpf_jit_current);
|
||||
}
|
||||
|
||||
void *__weak bpf_jit_alloc_exec(unsigned long size)
|
||||
{
|
||||
return module_alloc(size);
|
||||
}
|
||||
|
||||
void __weak bpf_jit_free_exec(void *addr)
|
||||
{
|
||||
module_memfree(addr);
|
||||
}
|
||||
|
||||
struct bpf_binary_header *
|
||||
bpf_jit_binary_alloc(unsigned int proglen, u8 **image_ptr,
|
||||
unsigned int alignment,
|
||||
@@ -635,7 +766,7 @@ bpf_jit_binary_alloc(unsigned int proglen, u8 **image_ptr,
|
||||
|
||||
if (bpf_jit_charge_modmem(pages))
|
||||
return NULL;
|
||||
hdr = module_alloc(size);
|
||||
hdr = bpf_jit_alloc_exec(size);
|
||||
if (!hdr) {
|
||||
bpf_jit_uncharge_modmem(pages);
|
||||
return NULL;
|
||||
@@ -659,7 +790,7 @@ void bpf_jit_binary_free(struct bpf_binary_header *hdr)
|
||||
{
|
||||
u32 pages = hdr->pages;
|
||||
|
||||
module_memfree(hdr);
|
||||
bpf_jit_free_exec(hdr);
|
||||
bpf_jit_uncharge_modmem(pages);
|
||||
}
|
||||
|
||||
@@ -918,32 +1049,34 @@ EXPORT_SYMBOL_GPL(__bpf_call_base);
|
||||
#define BPF_INSN_MAP(INSN_2, INSN_3) \
|
||||
/* 32 bit ALU operations. */ \
|
||||
/* Register based. */ \
|
||||
INSN_3(ALU, ADD, X), \
|
||||
INSN_3(ALU, SUB, X), \
|
||||
INSN_3(ALU, AND, X), \
|
||||
INSN_3(ALU, OR, X), \
|
||||
INSN_3(ALU, LSH, X), \
|
||||
INSN_3(ALU, RSH, X), \
|
||||
INSN_3(ALU, XOR, X), \
|
||||
INSN_3(ALU, MUL, X), \
|
||||
INSN_3(ALU, MOV, X), \
|
||||
INSN_3(ALU, DIV, X), \
|
||||
INSN_3(ALU, MOD, X), \
|
||||
INSN_3(ALU, ADD, X), \
|
||||
INSN_3(ALU, SUB, X), \
|
||||
INSN_3(ALU, AND, X), \
|
||||
INSN_3(ALU, OR, X), \
|
||||
INSN_3(ALU, LSH, X), \
|
||||
INSN_3(ALU, RSH, X), \
|
||||
INSN_3(ALU, XOR, X), \
|
||||
INSN_3(ALU, MUL, X), \
|
||||
INSN_3(ALU, MOV, X), \
|
||||
INSN_3(ALU, ARSH, X), \
|
||||
INSN_3(ALU, DIV, X), \
|
||||
INSN_3(ALU, MOD, X), \
|
||||
INSN_2(ALU, NEG), \
|
||||
INSN_3(ALU, END, TO_BE), \
|
||||
INSN_3(ALU, END, TO_LE), \
|
||||
/* Immediate based. */ \
|
||||
INSN_3(ALU, ADD, K), \
|
||||
INSN_3(ALU, SUB, K), \
|
||||
INSN_3(ALU, AND, K), \
|
||||
INSN_3(ALU, OR, K), \
|
||||
INSN_3(ALU, LSH, K), \
|
||||
INSN_3(ALU, RSH, K), \
|
||||
INSN_3(ALU, XOR, K), \
|
||||
INSN_3(ALU, MUL, K), \
|
||||
INSN_3(ALU, MOV, K), \
|
||||
INSN_3(ALU, DIV, K), \
|
||||
INSN_3(ALU, MOD, K), \
|
||||
INSN_3(ALU, ADD, K), \
|
||||
INSN_3(ALU, SUB, K), \
|
||||
INSN_3(ALU, AND, K), \
|
||||
INSN_3(ALU, OR, K), \
|
||||
INSN_3(ALU, LSH, K), \
|
||||
INSN_3(ALU, RSH, K), \
|
||||
INSN_3(ALU, XOR, K), \
|
||||
INSN_3(ALU, MUL, K), \
|
||||
INSN_3(ALU, MOV, K), \
|
||||
INSN_3(ALU, ARSH, K), \
|
||||
INSN_3(ALU, DIV, K), \
|
||||
INSN_3(ALU, MOD, K), \
|
||||
/* 64 bit ALU operations. */ \
|
||||
/* Register based. */ \
|
||||
INSN_3(ALU64, ADD, X), \
|
||||
@@ -1122,6 +1255,12 @@ select_insn:
|
||||
DST = (u64) (u32) insn[0].imm | ((u64) (u32) insn[1].imm) << 32;
|
||||
insn++;
|
||||
CONT;
|
||||
ALU_ARSH_X:
|
||||
DST = (u64) (u32) ((*(s32 *) &DST) >> SRC);
|
||||
CONT;
|
||||
ALU_ARSH_K:
|
||||
DST = (u64) (u32) ((*(s32 *) &DST) >> IMM);
|
||||
CONT;
|
||||
ALU64_ARSH_X:
|
||||
(*(s64 *) &DST) >>= SRC;
|
||||
CONT;
|
||||
@@ -1568,13 +1707,20 @@ struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err)
|
||||
* be JITed, but falls back to the interpreter.
|
||||
*/
|
||||
if (!bpf_prog_is_dev_bound(fp->aux)) {
|
||||
*err = bpf_prog_alloc_jited_linfo(fp);
|
||||
if (*err)
|
||||
return fp;
|
||||
|
||||
fp = bpf_int_jit_compile(fp);
|
||||
#ifdef CONFIG_BPF_JIT_ALWAYS_ON
|
||||
if (!fp->jited) {
|
||||
bpf_prog_free_jited_linfo(fp);
|
||||
#ifdef CONFIG_BPF_JIT_ALWAYS_ON
|
||||
*err = -ENOTSUPP;
|
||||
return fp;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
bpf_prog_free_unused_jited_linfo(fp);
|
||||
}
|
||||
} else {
|
||||
*err = bpf_prog_offload_compile(fp);
|
||||
if (*err)
|
||||
|
@@ -183,7 +183,7 @@ static struct sk_buff *cpu_map_build_skb(struct bpf_cpu_map_entry *rcpu,
|
||||
* is not at a fixed memory location, with mixed length
|
||||
* packets, which is bad for cache-line hotness.
|
||||
*/
|
||||
frame_size = SKB_DATA_ALIGN(xdpf->len) + xdpf->headroom +
|
||||
frame_size = SKB_DATA_ALIGN(xdpf->len + xdpf->headroom) +
|
||||
SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
|
||||
|
||||
pkt_data_start = xdpf->data - xdpf->headroom;
|
||||
|
@@ -23,7 +23,7 @@
|
||||
|
||||
#define HTAB_CREATE_FLAG_MASK \
|
||||
(BPF_F_NO_PREALLOC | BPF_F_NO_COMMON_LRU | BPF_F_NUMA_NODE | \
|
||||
BPF_F_RDONLY | BPF_F_WRONLY)
|
||||
BPF_F_RDONLY | BPF_F_WRONLY | BPF_F_ZERO_SEED)
|
||||
|
||||
struct bucket {
|
||||
struct hlist_nulls_head head;
|
||||
@@ -244,6 +244,7 @@ static int htab_map_alloc_check(union bpf_attr *attr)
|
||||
*/
|
||||
bool percpu_lru = (attr->map_flags & BPF_F_NO_COMMON_LRU);
|
||||
bool prealloc = !(attr->map_flags & BPF_F_NO_PREALLOC);
|
||||
bool zero_seed = (attr->map_flags & BPF_F_ZERO_SEED);
|
||||
int numa_node = bpf_map_attr_numa_node(attr);
|
||||
|
||||
BUILD_BUG_ON(offsetof(struct htab_elem, htab) !=
|
||||
@@ -257,6 +258,10 @@ static int htab_map_alloc_check(union bpf_attr *attr)
|
||||
*/
|
||||
return -EPERM;
|
||||
|
||||
if (zero_seed && !capable(CAP_SYS_ADMIN))
|
||||
/* Guard against local DoS, and discourage production use. */
|
||||
return -EPERM;
|
||||
|
||||
if (attr->map_flags & ~HTAB_CREATE_FLAG_MASK)
|
||||
/* reserved bits should not be used */
|
||||
return -EINVAL;
|
||||
@@ -373,7 +378,11 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr)
|
||||
if (!htab->buckets)
|
||||
goto free_htab;
|
||||
|
||||
htab->hashrnd = get_random_int();
|
||||
if (htab->map.map_flags & BPF_F_ZERO_SEED)
|
||||
htab->hashrnd = 0;
|
||||
else
|
||||
htab->hashrnd = get_random_int();
|
||||
|
||||
for (i = 0; i < htab->n_buckets; i++) {
|
||||
INIT_HLIST_NULLS_HEAD(&htab->buckets[i].head, i);
|
||||
raw_spin_lock_init(&htab->buckets[i].lock);
|
||||
|
@@ -1,14 +1,15 @@
|
||||
//SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/bpf-cgroup.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/btf.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/slab.h>
|
||||
#include <uapi/linux/btf.h>
|
||||
|
||||
DEFINE_PER_CPU(struct bpf_cgroup_storage*,
|
||||
bpf_cgroup_storage[MAX_BPF_CGROUP_STORAGE_TYPE]);
|
||||
DEFINE_PER_CPU(struct bpf_cgroup_storage*, bpf_cgroup_storage[MAX_BPF_CGROUP_STORAGE_TYPE]);
|
||||
|
||||
#ifdef CONFIG_CGROUP_BPF
|
||||
|
||||
@@ -309,6 +310,85 @@ static int cgroup_storage_delete_elem(struct bpf_map *map, void *key)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int cgroup_storage_check_btf(const struct bpf_map *map,
|
||||
const struct btf *btf,
|
||||
const struct btf_type *key_type,
|
||||
const struct btf_type *value_type)
|
||||
{
|
||||
struct btf_member *m;
|
||||
u32 offset, size;
|
||||
|
||||
/* Key is expected to be of struct bpf_cgroup_storage_key type,
|
||||
* which is:
|
||||
* struct bpf_cgroup_storage_key {
|
||||
* __u64 cgroup_inode_id;
|
||||
* __u32 attach_type;
|
||||
* };
|
||||
*/
|
||||
|
||||
/*
|
||||
* Key_type must be a structure with two fields.
|
||||
*/
|
||||
if (BTF_INFO_KIND(key_type->info) != BTF_KIND_STRUCT ||
|
||||
BTF_INFO_VLEN(key_type->info) != 2)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* The first field must be a 64 bit integer at 0 offset.
|
||||
*/
|
||||
m = (struct btf_member *)(key_type + 1);
|
||||
size = FIELD_SIZEOF(struct bpf_cgroup_storage_key, cgroup_inode_id);
|
||||
if (!btf_member_is_reg_int(btf, key_type, m, 0, size))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* The second field must be a 32 bit integer at 64 bit offset.
|
||||
*/
|
||||
m++;
|
||||
offset = offsetof(struct bpf_cgroup_storage_key, attach_type);
|
||||
size = FIELD_SIZEOF(struct bpf_cgroup_storage_key, attach_type);
|
||||
if (!btf_member_is_reg_int(btf, key_type, m, offset, size))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cgroup_storage_seq_show_elem(struct bpf_map *map, void *_key,
|
||||
struct seq_file *m)
|
||||
{
|
||||
enum bpf_cgroup_storage_type stype = cgroup_storage_type(map);
|
||||
struct bpf_cgroup_storage_key *key = _key;
|
||||
struct bpf_cgroup_storage *storage;
|
||||
int cpu;
|
||||
|
||||
rcu_read_lock();
|
||||
storage = cgroup_storage_lookup(map_to_storage(map), key, false);
|
||||
if (!storage) {
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
btf_type_seq_show(map->btf, map->btf_key_type_id, key, m);
|
||||
stype = cgroup_storage_type(map);
|
||||
if (stype == BPF_CGROUP_STORAGE_SHARED) {
|
||||
seq_puts(m, ": ");
|
||||
btf_type_seq_show(map->btf, map->btf_value_type_id,
|
||||
&READ_ONCE(storage->buf)->data[0], m);
|
||||
seq_puts(m, "\n");
|
||||
} else {
|
||||
seq_puts(m, ": {\n");
|
||||
for_each_possible_cpu(cpu) {
|
||||
seq_printf(m, "\tcpu%d: ", cpu);
|
||||
btf_type_seq_show(map->btf, map->btf_value_type_id,
|
||||
per_cpu_ptr(storage->percpu_buf, cpu),
|
||||
m);
|
||||
seq_puts(m, "\n");
|
||||
}
|
||||
seq_puts(m, "}\n");
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
const struct bpf_map_ops cgroup_storage_map_ops = {
|
||||
.map_alloc = cgroup_storage_map_alloc,
|
||||
.map_free = cgroup_storage_map_free,
|
||||
@@ -316,7 +396,8 @@ const struct bpf_map_ops cgroup_storage_map_ops = {
|
||||
.map_lookup_elem = cgroup_storage_lookup_elem,
|
||||
.map_update_elem = cgroup_storage_update_elem,
|
||||
.map_delete_elem = cgroup_storage_delete_elem,
|
||||
.map_check_btf = map_check_no_btf,
|
||||
.map_check_btf = cgroup_storage_check_btf,
|
||||
.map_seq_show_elem = cgroup_storage_seq_show_elem,
|
||||
};
|
||||
|
||||
int bpf_cgroup_storage_assign(struct bpf_prog *prog, struct bpf_map *_map)
|
||||
|
@@ -168,20 +168,59 @@ static size_t longest_prefix_match(const struct lpm_trie *trie,
|
||||
const struct lpm_trie_node *node,
|
||||
const struct bpf_lpm_trie_key *key)
|
||||
{
|
||||
size_t prefixlen = 0;
|
||||
size_t i;
|
||||
u32 limit = min(node->prefixlen, key->prefixlen);
|
||||
u32 prefixlen = 0, i = 0;
|
||||
|
||||
for (i = 0; i < trie->data_size; i++) {
|
||||
size_t b;
|
||||
BUILD_BUG_ON(offsetof(struct lpm_trie_node, data) % sizeof(u32));
|
||||
BUILD_BUG_ON(offsetof(struct bpf_lpm_trie_key, data) % sizeof(u32));
|
||||
|
||||
b = 8 - fls(node->data[i] ^ key->data[i]);
|
||||
prefixlen += b;
|
||||
#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && defined(CONFIG_64BIT)
|
||||
|
||||
if (prefixlen >= node->prefixlen || prefixlen >= key->prefixlen)
|
||||
return min(node->prefixlen, key->prefixlen);
|
||||
/* data_size >= 16 has very small probability.
|
||||
* We do not use a loop for optimal code generation.
|
||||
*/
|
||||
if (trie->data_size >= 8) {
|
||||
u64 diff = be64_to_cpu(*(__be64 *)node->data ^
|
||||
*(__be64 *)key->data);
|
||||
|
||||
if (b < 8)
|
||||
break;
|
||||
prefixlen = 64 - fls64(diff);
|
||||
if (prefixlen >= limit)
|
||||
return limit;
|
||||
if (diff)
|
||||
return prefixlen;
|
||||
i = 8;
|
||||
}
|
||||
#endif
|
||||
|
||||
while (trie->data_size >= i + 4) {
|
||||
u32 diff = be32_to_cpu(*(__be32 *)&node->data[i] ^
|
||||
*(__be32 *)&key->data[i]);
|
||||
|
||||
prefixlen += 32 - fls(diff);
|
||||
if (prefixlen >= limit)
|
||||
return limit;
|
||||
if (diff)
|
||||
return prefixlen;
|
||||
i += 4;
|
||||
}
|
||||
|
||||
if (trie->data_size >= i + 2) {
|
||||
u16 diff = be16_to_cpu(*(__be16 *)&node->data[i] ^
|
||||
*(__be16 *)&key->data[i]);
|
||||
|
||||
prefixlen += 16 - fls(diff);
|
||||
if (prefixlen >= limit)
|
||||
return limit;
|
||||
if (diff)
|
||||
return prefixlen;
|
||||
i += 2;
|
||||
}
|
||||
|
||||
if (trie->data_size >= i + 1) {
|
||||
prefixlen += 8 - fls(node->data[i] ^ key->data[i]);
|
||||
|
||||
if (prefixlen >= limit)
|
||||
return limit;
|
||||
}
|
||||
|
||||
return prefixlen;
|
||||
@@ -689,6 +728,7 @@ free_stack:
|
||||
}
|
||||
|
||||
static int trie_check_btf(const struct bpf_map *map,
|
||||
const struct btf *btf,
|
||||
const struct btf_type *key_type,
|
||||
const struct btf_type *value_type)
|
||||
{
|
||||
|
@@ -33,6 +33,7 @@
|
||||
static DECLARE_RWSEM(bpf_devs_lock);
|
||||
|
||||
struct bpf_offload_dev {
|
||||
const struct bpf_prog_offload_ops *ops;
|
||||
struct list_head netdevs;
|
||||
};
|
||||
|
||||
@@ -106,6 +107,7 @@ int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr)
|
||||
err = -EINVAL;
|
||||
goto err_unlock;
|
||||
}
|
||||
offload->offdev = ondev->offdev;
|
||||
prog->aux->offload = offload;
|
||||
list_add_tail(&offload->offloads, &ondev->progs);
|
||||
dev_put(offload->netdev);
|
||||
@@ -121,40 +123,20 @@ err_maybe_put:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __bpf_offload_ndo(struct bpf_prog *prog, enum bpf_netdev_command cmd,
|
||||
struct netdev_bpf *data)
|
||||
int bpf_prog_offload_verifier_prep(struct bpf_prog *prog)
|
||||
{
|
||||
struct bpf_prog_offload *offload = prog->aux->offload;
|
||||
struct net_device *netdev;
|
||||
struct bpf_prog_offload *offload;
|
||||
int ret = -ENODEV;
|
||||
|
||||
ASSERT_RTNL();
|
||||
down_read(&bpf_devs_lock);
|
||||
offload = prog->aux->offload;
|
||||
if (offload) {
|
||||
ret = offload->offdev->ops->prepare(prog);
|
||||
offload->dev_state = !ret;
|
||||
}
|
||||
up_read(&bpf_devs_lock);
|
||||
|
||||
if (!offload)
|
||||
return -ENODEV;
|
||||
netdev = offload->netdev;
|
||||
|
||||
data->command = cmd;
|
||||
|
||||
return netdev->netdev_ops->ndo_bpf(netdev, data);
|
||||
}
|
||||
|
||||
int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env)
|
||||
{
|
||||
struct netdev_bpf data = {};
|
||||
int err;
|
||||
|
||||
data.verifier.prog = env->prog;
|
||||
|
||||
rtnl_lock();
|
||||
err = __bpf_offload_ndo(env->prog, BPF_OFFLOAD_VERIFIER_PREP, &data);
|
||||
if (err)
|
||||
goto exit_unlock;
|
||||
|
||||
env->prog->aux->offload->dev_ops = data.verifier.ops;
|
||||
env->prog->aux->offload->dev_state = true;
|
||||
exit_unlock:
|
||||
rtnl_unlock();
|
||||
return err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bpf_prog_offload_verify_insn(struct bpf_verifier_env *env,
|
||||
@@ -166,7 +148,8 @@ int bpf_prog_offload_verify_insn(struct bpf_verifier_env *env,
|
||||
down_read(&bpf_devs_lock);
|
||||
offload = env->prog->aux->offload;
|
||||
if (offload)
|
||||
ret = offload->dev_ops->insn_hook(env, insn_idx, prev_insn_idx);
|
||||
ret = offload->offdev->ops->insn_hook(env, insn_idx,
|
||||
prev_insn_idx);
|
||||
up_read(&bpf_devs_lock);
|
||||
|
||||
return ret;
|
||||
@@ -180,8 +163,8 @@ int bpf_prog_offload_finalize(struct bpf_verifier_env *env)
|
||||
down_read(&bpf_devs_lock);
|
||||
offload = env->prog->aux->offload;
|
||||
if (offload) {
|
||||
if (offload->dev_ops->finalize)
|
||||
ret = offload->dev_ops->finalize(env);
|
||||
if (offload->offdev->ops->finalize)
|
||||
ret = offload->offdev->ops->finalize(env);
|
||||
else
|
||||
ret = 0;
|
||||
}
|
||||
@@ -193,12 +176,9 @@ int bpf_prog_offload_finalize(struct bpf_verifier_env *env)
|
||||
static void __bpf_prog_offload_destroy(struct bpf_prog *prog)
|
||||
{
|
||||
struct bpf_prog_offload *offload = prog->aux->offload;
|
||||
struct netdev_bpf data = {};
|
||||
|
||||
data.offload.prog = prog;
|
||||
|
||||
if (offload->dev_state)
|
||||
WARN_ON(__bpf_offload_ndo(prog, BPF_OFFLOAD_DESTROY, &data));
|
||||
offload->offdev->ops->destroy(prog);
|
||||
|
||||
/* Make sure BPF_PROG_GET_NEXT_ID can't find this dead program */
|
||||
bpf_prog_free_id(prog, true);
|
||||
@@ -210,24 +190,22 @@ static void __bpf_prog_offload_destroy(struct bpf_prog *prog)
|
||||
|
||||
void bpf_prog_offload_destroy(struct bpf_prog *prog)
|
||||
{
|
||||
rtnl_lock();
|
||||
down_write(&bpf_devs_lock);
|
||||
if (prog->aux->offload)
|
||||
__bpf_prog_offload_destroy(prog);
|
||||
up_write(&bpf_devs_lock);
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
static int bpf_prog_offload_translate(struct bpf_prog *prog)
|
||||
{
|
||||
struct netdev_bpf data = {};
|
||||
int ret;
|
||||
struct bpf_prog_offload *offload;
|
||||
int ret = -ENODEV;
|
||||
|
||||
data.offload.prog = prog;
|
||||
|
||||
rtnl_lock();
|
||||
ret = __bpf_offload_ndo(prog, BPF_OFFLOAD_TRANSLATE, &data);
|
||||
rtnl_unlock();
|
||||
down_read(&bpf_devs_lock);
|
||||
offload = prog->aux->offload;
|
||||
if (offload)
|
||||
ret = offload->offdev->ops->translate(prog);
|
||||
up_read(&bpf_devs_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -655,7 +633,8 @@ unlock:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bpf_offload_dev_netdev_unregister);
|
||||
|
||||
struct bpf_offload_dev *bpf_offload_dev_create(void)
|
||||
struct bpf_offload_dev *
|
||||
bpf_offload_dev_create(const struct bpf_prog_offload_ops *ops)
|
||||
{
|
||||
struct bpf_offload_dev *offdev;
|
||||
int err;
|
||||
@@ -673,6 +652,7 @@ struct bpf_offload_dev *bpf_offload_dev_create(void)
|
||||
if (!offdev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
offdev->ops = ops;
|
||||
INIT_LIST_HEAD(&offdev->netdevs);
|
||||
|
||||
return offdev;
|
||||
|
@@ -456,6 +456,7 @@ static int bpf_obj_name_cpy(char *dst, const char *src)
|
||||
}
|
||||
|
||||
int map_check_no_btf(const struct bpf_map *map,
|
||||
const struct btf *btf,
|
||||
const struct btf_type *key_type,
|
||||
const struct btf_type *value_type)
|
||||
{
|
||||
@@ -478,7 +479,7 @@ static int map_check_btf(const struct bpf_map *map, const struct btf *btf,
|
||||
return -EINVAL;
|
||||
|
||||
if (map->ops->map_check_btf)
|
||||
ret = map->ops->map_check_btf(map, key_type, value_type);
|
||||
ret = map->ops->map_check_btf(map, btf, key_type, value_type);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1213,6 +1214,9 @@ static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock)
|
||||
/* bpf_prog_free_id() must be called first */
|
||||
bpf_prog_free_id(prog, do_idr_lock);
|
||||
bpf_prog_kallsyms_del_all(prog);
|
||||
btf_put(prog->aux->btf);
|
||||
kvfree(prog->aux->func_info);
|
||||
bpf_prog_free_linfo(prog);
|
||||
|
||||
call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu);
|
||||
}
|
||||
@@ -1437,9 +1441,9 @@ bpf_prog_load_check_attach_type(enum bpf_prog_type prog_type,
|
||||
}
|
||||
|
||||
/* last field in 'union bpf_attr' used by this command */
|
||||
#define BPF_PROG_LOAD_LAST_FIELD expected_attach_type
|
||||
#define BPF_PROG_LOAD_LAST_FIELD line_info_cnt
|
||||
|
||||
static int bpf_prog_load(union bpf_attr *attr)
|
||||
static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr)
|
||||
{
|
||||
enum bpf_prog_type type = attr->prog_type;
|
||||
struct bpf_prog *prog;
|
||||
@@ -1450,9 +1454,14 @@ static int bpf_prog_load(union bpf_attr *attr)
|
||||
if (CHECK_ATTR(BPF_PROG_LOAD))
|
||||
return -EINVAL;
|
||||
|
||||
if (attr->prog_flags & ~BPF_F_STRICT_ALIGNMENT)
|
||||
if (attr->prog_flags & ~(BPF_F_STRICT_ALIGNMENT | BPF_F_ANY_ALIGNMENT))
|
||||
return -EINVAL;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) &&
|
||||
(attr->prog_flags & BPF_F_ANY_ALIGNMENT) &&
|
||||
!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
/* copy eBPF program license from user space */
|
||||
if (strncpy_from_user(license, u64_to_user_ptr(attr->license),
|
||||
sizeof(license) - 1) < 0)
|
||||
@@ -1464,11 +1473,6 @@ static int bpf_prog_load(union bpf_attr *attr)
|
||||
|
||||
if (attr->insn_cnt == 0 || attr->insn_cnt > BPF_MAXINSNS)
|
||||
return -E2BIG;
|
||||
|
||||
if (type == BPF_PROG_TYPE_KPROBE &&
|
||||
attr->kern_version != LINUX_VERSION_CODE)
|
||||
return -EINVAL;
|
||||
|
||||
if (type != BPF_PROG_TYPE_SOCKET_FILTER &&
|
||||
type != BPF_PROG_TYPE_CGROUP_SKB &&
|
||||
!capable(CAP_SYS_ADMIN))
|
||||
@@ -1525,7 +1529,7 @@ static int bpf_prog_load(union bpf_attr *attr)
|
||||
goto free_prog;
|
||||
|
||||
/* run eBPF verifier */
|
||||
err = bpf_check(&prog, attr);
|
||||
err = bpf_check(&prog, attr, uattr);
|
||||
if (err < 0)
|
||||
goto free_used_maps;
|
||||
|
||||
@@ -1553,6 +1557,9 @@ static int bpf_prog_load(union bpf_attr *attr)
|
||||
return err;
|
||||
|
||||
free_used_maps:
|
||||
bpf_prog_free_linfo(prog);
|
||||
kvfree(prog->aux->func_info);
|
||||
btf_put(prog->aux->btf);
|
||||
bpf_prog_kallsyms_del_subprogs(prog);
|
||||
free_used_maps(prog->aux);
|
||||
free_prog:
|
||||
@@ -1597,6 +1604,7 @@ static int bpf_raw_tracepoint_release(struct inode *inode, struct file *filp)
|
||||
bpf_probe_unregister(raw_tp->btp, raw_tp->prog);
|
||||
bpf_prog_put(raw_tp->prog);
|
||||
}
|
||||
bpf_put_raw_tracepoint(raw_tp->btp);
|
||||
kfree(raw_tp);
|
||||
return 0;
|
||||
}
|
||||
@@ -1622,13 +1630,15 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
|
||||
return -EFAULT;
|
||||
tp_name[sizeof(tp_name) - 1] = 0;
|
||||
|
||||
btp = bpf_find_raw_tracepoint(tp_name);
|
||||
btp = bpf_get_raw_tracepoint(tp_name);
|
||||
if (!btp)
|
||||
return -ENOENT;
|
||||
|
||||
raw_tp = kzalloc(sizeof(*raw_tp), GFP_USER);
|
||||
if (!raw_tp)
|
||||
return -ENOMEM;
|
||||
if (!raw_tp) {
|
||||
err = -ENOMEM;
|
||||
goto out_put_btp;
|
||||
}
|
||||
raw_tp->btp = btp;
|
||||
|
||||
prog = bpf_prog_get_type(attr->raw_tracepoint.prog_fd,
|
||||
@@ -1656,6 +1666,8 @@ out_put_prog:
|
||||
bpf_prog_put(prog);
|
||||
out_free_tp:
|
||||
kfree(raw_tp);
|
||||
out_put_btp:
|
||||
bpf_put_raw_tracepoint(btp);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -2020,18 +2032,42 @@ static struct bpf_insn *bpf_insn_prepare_dump(const struct bpf_prog *prog)
|
||||
insns[i + 1].imm = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!bpf_dump_raw_ok() &&
|
||||
imm == (unsigned long)prog->aux) {
|
||||
insns[i].imm = 0;
|
||||
insns[i + 1].imm = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return insns;
|
||||
}
|
||||
|
||||
static int set_info_rec_size(struct bpf_prog_info *info)
|
||||
{
|
||||
/*
|
||||
* Ensure info.*_rec_size is the same as kernel expected size
|
||||
*
|
||||
* or
|
||||
*
|
||||
* Only allow zero *_rec_size if both _rec_size and _cnt are
|
||||
* zero. In this case, the kernel will set the expected
|
||||
* _rec_size back to the info.
|
||||
*/
|
||||
|
||||
if ((info->nr_func_info || info->func_info_rec_size) &&
|
||||
info->func_info_rec_size != sizeof(struct bpf_func_info))
|
||||
return -EINVAL;
|
||||
|
||||
if ((info->nr_line_info || info->line_info_rec_size) &&
|
||||
info->line_info_rec_size != sizeof(struct bpf_line_info))
|
||||
return -EINVAL;
|
||||
|
||||
if ((info->nr_jited_line_info || info->jited_line_info_rec_size) &&
|
||||
info->jited_line_info_rec_size != sizeof(__u64))
|
||||
return -EINVAL;
|
||||
|
||||
info->func_info_rec_size = sizeof(struct bpf_func_info);
|
||||
info->line_info_rec_size = sizeof(struct bpf_line_info);
|
||||
info->jited_line_info_rec_size = sizeof(__u64);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,
|
||||
const union bpf_attr *attr,
|
||||
union bpf_attr __user *uattr)
|
||||
@@ -2074,11 +2110,18 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
err = set_info_rec_size(&info);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN)) {
|
||||
info.jited_prog_len = 0;
|
||||
info.xlated_prog_len = 0;
|
||||
info.nr_jited_ksyms = 0;
|
||||
info.nr_jited_func_lens = 0;
|
||||
info.nr_func_info = 0;
|
||||
info.nr_line_info = 0;
|
||||
info.nr_jited_line_info = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
@@ -2160,7 +2203,7 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,
|
||||
|
||||
ulen = info.nr_jited_ksyms;
|
||||
info.nr_jited_ksyms = prog->aux->func_cnt ? : 1;
|
||||
if (info.nr_jited_ksyms && ulen) {
|
||||
if (ulen) {
|
||||
if (bpf_dump_raw_ok()) {
|
||||
unsigned long ksym_addr;
|
||||
u64 __user *user_ksyms;
|
||||
@@ -2191,7 +2234,7 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,
|
||||
|
||||
ulen = info.nr_jited_func_lens;
|
||||
info.nr_jited_func_lens = prog->aux->func_cnt ? : 1;
|
||||
if (info.nr_jited_func_lens && ulen) {
|
||||
if (ulen) {
|
||||
if (bpf_dump_raw_ok()) {
|
||||
u32 __user *user_lens;
|
||||
u32 func_len, i;
|
||||
@@ -2216,6 +2259,77 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,
|
||||
}
|
||||
}
|
||||
|
||||
if (prog->aux->btf)
|
||||
info.btf_id = btf_id(prog->aux->btf);
|
||||
|
||||
ulen = info.nr_func_info;
|
||||
info.nr_func_info = prog->aux->func_info_cnt;
|
||||
if (info.nr_func_info && ulen) {
|
||||
char __user *user_finfo;
|
||||
|
||||
user_finfo = u64_to_user_ptr(info.func_info);
|
||||
ulen = min_t(u32, info.nr_func_info, ulen);
|
||||
if (copy_to_user(user_finfo, prog->aux->func_info,
|
||||
info.func_info_rec_size * ulen))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
ulen = info.nr_line_info;
|
||||
info.nr_line_info = prog->aux->nr_linfo;
|
||||
if (info.nr_line_info && ulen) {
|
||||
__u8 __user *user_linfo;
|
||||
|
||||
user_linfo = u64_to_user_ptr(info.line_info);
|
||||
ulen = min_t(u32, info.nr_line_info, ulen);
|
||||
if (copy_to_user(user_linfo, prog->aux->linfo,
|
||||
info.line_info_rec_size * ulen))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
ulen = info.nr_jited_line_info;
|
||||
if (prog->aux->jited_linfo)
|
||||
info.nr_jited_line_info = prog->aux->nr_linfo;
|
||||
else
|
||||
info.nr_jited_line_info = 0;
|
||||
if (info.nr_jited_line_info && ulen) {
|
||||
if (bpf_dump_raw_ok()) {
|
||||
__u64 __user *user_linfo;
|
||||
u32 i;
|
||||
|
||||
user_linfo = u64_to_user_ptr(info.jited_line_info);
|
||||
ulen = min_t(u32, info.nr_jited_line_info, ulen);
|
||||
for (i = 0; i < ulen; i++) {
|
||||
if (put_user((__u64)(long)prog->aux->jited_linfo[i],
|
||||
&user_linfo[i]))
|
||||
return -EFAULT;
|
||||
}
|
||||
} else {
|
||||
info.jited_line_info = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ulen = info.nr_prog_tags;
|
||||
info.nr_prog_tags = prog->aux->func_cnt ? : 1;
|
||||
if (ulen) {
|
||||
__u8 __user (*user_prog_tags)[BPF_TAG_SIZE];
|
||||
u32 i;
|
||||
|
||||
user_prog_tags = u64_to_user_ptr(info.prog_tags);
|
||||
ulen = min_t(u32, info.nr_prog_tags, ulen);
|
||||
if (prog->aux->func_cnt) {
|
||||
for (i = 0; i < ulen; i++) {
|
||||
if (copy_to_user(user_prog_tags[i],
|
||||
prog->aux->func[i]->tag,
|
||||
BPF_TAG_SIZE))
|
||||
return -EFAULT;
|
||||
}
|
||||
} else {
|
||||
if (copy_to_user(user_prog_tags[0],
|
||||
prog->tag, BPF_TAG_SIZE))
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
if (copy_to_user(uinfo, &info, info_len) ||
|
||||
put_user(info_len, &uattr->info.info_len))
|
||||
@@ -2501,7 +2615,7 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
|
||||
err = map_get_next_key(&attr);
|
||||
break;
|
||||
case BPF_PROG_LOAD:
|
||||
err = bpf_prog_load(&attr);
|
||||
err = bpf_prog_load(&attr, uattr);
|
||||
break;
|
||||
case BPF_OBJ_PIN:
|
||||
err = bpf_obj_pin(&attr);
|
||||
|
@@ -11,10 +11,12 @@
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*/
|
||||
#include <uapi/linux/btf.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/btf.h>
|
||||
#include <linux/bpf_verifier.h>
|
||||
#include <linux/filter.h>
|
||||
#include <net/netlink.h>
|
||||
@@ -24,6 +26,7 @@
|
||||
#include <linux/bsearch.h>
|
||||
#include <linux/sort.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/ctype.h>
|
||||
|
||||
#include "disasm.h"
|
||||
|
||||
@@ -214,6 +217,27 @@ struct bpf_call_arg_meta {
|
||||
|
||||
static DEFINE_MUTEX(bpf_verifier_lock);
|
||||
|
||||
static const struct bpf_line_info *
|
||||
find_linfo(const struct bpf_verifier_env *env, u32 insn_off)
|
||||
{
|
||||
const struct bpf_line_info *linfo;
|
||||
const struct bpf_prog *prog;
|
||||
u32 i, nr_linfo;
|
||||
|
||||
prog = env->prog;
|
||||
nr_linfo = prog->aux->nr_linfo;
|
||||
|
||||
if (!nr_linfo || insn_off >= prog->len)
|
||||
return NULL;
|
||||
|
||||
linfo = prog->aux->linfo;
|
||||
for (i = 1; i < nr_linfo; i++)
|
||||
if (insn_off < linfo[i].insn_off)
|
||||
break;
|
||||
|
||||
return &linfo[i - 1];
|
||||
}
|
||||
|
||||
void bpf_verifier_vlog(struct bpf_verifier_log *log, const char *fmt,
|
||||
va_list args)
|
||||
{
|
||||
@@ -264,6 +288,42 @@ __printf(2, 3) static void verbose(void *private_data, const char *fmt, ...)
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static const char *ltrim(const char *s)
|
||||
{
|
||||
while (isspace(*s))
|
||||
s++;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
__printf(3, 4) static void verbose_linfo(struct bpf_verifier_env *env,
|
||||
u32 insn_off,
|
||||
const char *prefix_fmt, ...)
|
||||
{
|
||||
const struct bpf_line_info *linfo;
|
||||
|
||||
if (!bpf_verifier_log_needed(&env->log))
|
||||
return;
|
||||
|
||||
linfo = find_linfo(env, insn_off);
|
||||
if (!linfo || linfo == env->prev_linfo)
|
||||
return;
|
||||
|
||||
if (prefix_fmt) {
|
||||
va_list args;
|
||||
|
||||
va_start(args, prefix_fmt);
|
||||
bpf_verifier_vlog(&env->log, prefix_fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
verbose(env, "%s\n",
|
||||
ltrim(btf_name_by_offset(env->prog->aux->btf,
|
||||
linfo->line_off)));
|
||||
|
||||
env->prev_linfo = linfo;
|
||||
}
|
||||
|
||||
static bool type_is_pkt_pointer(enum bpf_reg_type type)
|
||||
{
|
||||
return type == PTR_TO_PACKET ||
|
||||
@@ -337,12 +397,14 @@ static char slot_type_char[] = {
|
||||
static void print_liveness(struct bpf_verifier_env *env,
|
||||
enum bpf_reg_liveness live)
|
||||
{
|
||||
if (live & (REG_LIVE_READ | REG_LIVE_WRITTEN))
|
||||
if (live & (REG_LIVE_READ | REG_LIVE_WRITTEN | REG_LIVE_DONE))
|
||||
verbose(env, "_");
|
||||
if (live & REG_LIVE_READ)
|
||||
verbose(env, "r");
|
||||
if (live & REG_LIVE_WRITTEN)
|
||||
verbose(env, "w");
|
||||
if (live & REG_LIVE_DONE)
|
||||
verbose(env, "D");
|
||||
}
|
||||
|
||||
static struct bpf_func_state *func(struct bpf_verifier_env *env,
|
||||
@@ -1072,6 +1134,12 @@ static int mark_reg_read(struct bpf_verifier_env *env,
|
||||
/* if read wasn't screened by an earlier write ... */
|
||||
if (writes && state->live & REG_LIVE_WRITTEN)
|
||||
break;
|
||||
if (parent->live & REG_LIVE_DONE) {
|
||||
verbose(env, "verifier BUG type %s var_off %lld off %d\n",
|
||||
reg_type_str[parent->type],
|
||||
parent->var_off.value, parent->off);
|
||||
return -EFAULT;
|
||||
}
|
||||
/* ... then we depend on parent's value */
|
||||
parent->live |= REG_LIVE_READ;
|
||||
state = parent;
|
||||
@@ -1218,6 +1286,10 @@ static int check_stack_write(struct bpf_verifier_env *env,
|
||||
|
||||
/* regular write of data into stack destroys any spilled ptr */
|
||||
state->stack[spi].spilled_ptr.type = NOT_INIT;
|
||||
/* Mark slots as STACK_MISC if they belonged to spilled ptr. */
|
||||
if (state->stack[spi].slot_type[0] == STACK_SPILL)
|
||||
for (i = 0; i < BPF_REG_SIZE; i++)
|
||||
state->stack[spi].slot_type[i] = STACK_MISC;
|
||||
|
||||
/* only mark the slot as written if all 8 bytes were written
|
||||
* otherwise read propagation may incorrectly stop too soon
|
||||
@@ -1235,6 +1307,7 @@ static int check_stack_write(struct bpf_verifier_env *env,
|
||||
register_is_null(&cur->regs[value_regno]))
|
||||
type = STACK_ZERO;
|
||||
|
||||
/* Mark slots affected by this stack write. */
|
||||
for (i = 0; i < size; i++)
|
||||
state->stack[spi].slot_type[(slot - i) % BPF_REG_SIZE] =
|
||||
type;
|
||||
@@ -1456,6 +1529,17 @@ static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off,
|
||||
verbose(env, "R%d offset is outside of the packet\n", regno);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* __check_packet_access has made sure "off + size - 1" is within u16.
|
||||
* reg->umax_value can't be bigger than MAX_PACKET_OFF which is 0xffff,
|
||||
* otherwise find_good_pkt_pointers would have refused to set range info
|
||||
* that __check_packet_access would have rejected this pkt access.
|
||||
* Therefore, "off + reg->umax_value + size - 1" won't overflow u32.
|
||||
*/
|
||||
env->prog->aux->max_pkt_offset =
|
||||
max_t(u32, env->prog->aux->max_pkt_offset,
|
||||
off + reg->umax_value + size - 1);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -3571,12 +3655,15 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
|
||||
return err;
|
||||
|
||||
if (BPF_SRC(insn->code) == BPF_X) {
|
||||
struct bpf_reg_state *src_reg = regs + insn->src_reg;
|
||||
struct bpf_reg_state *dst_reg = regs + insn->dst_reg;
|
||||
|
||||
if (BPF_CLASS(insn->code) == BPF_ALU64) {
|
||||
/* case: R1 = R2
|
||||
* copy register state to dest reg
|
||||
*/
|
||||
regs[insn->dst_reg] = regs[insn->src_reg];
|
||||
regs[insn->dst_reg].live |= REG_LIVE_WRITTEN;
|
||||
*dst_reg = *src_reg;
|
||||
dst_reg->live |= REG_LIVE_WRITTEN;
|
||||
} else {
|
||||
/* R1 = (u32) R2 */
|
||||
if (is_pointer_value(env, insn->src_reg)) {
|
||||
@@ -3584,9 +3671,14 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
|
||||
"R%d partial copy of pointer\n",
|
||||
insn->src_reg);
|
||||
return -EACCES;
|
||||
} else if (src_reg->type == SCALAR_VALUE) {
|
||||
*dst_reg = *src_reg;
|
||||
dst_reg->live |= REG_LIVE_WRITTEN;
|
||||
} else {
|
||||
mark_reg_unknown(env, regs,
|
||||
insn->dst_reg);
|
||||
}
|
||||
mark_reg_unknown(env, regs, insn->dst_reg);
|
||||
coerce_reg_to_size(®s[insn->dst_reg], 4);
|
||||
coerce_reg_to_size(dst_reg, 4);
|
||||
}
|
||||
} else {
|
||||
/* case: R = imm
|
||||
@@ -3637,11 +3729,6 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (opcode == BPF_ARSH && BPF_CLASS(insn->code) != BPF_ALU64) {
|
||||
verbose(env, "BPF_ARSH not supported for 32 bit ALU\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((opcode == BPF_LSH || opcode == BPF_RSH ||
|
||||
opcode == BPF_ARSH) && BPF_SRC(insn->code) == BPF_K) {
|
||||
int size = BPF_CLASS(insn->code) == BPF_ALU64 ? 64 : 32;
|
||||
@@ -3772,6 +3859,12 @@ static int is_branch_taken(struct bpf_reg_state *reg, u64 val, u8 opcode)
|
||||
if (tnum_is_const(reg->var_off))
|
||||
return !tnum_equals_const(reg->var_off, val);
|
||||
break;
|
||||
case BPF_JSET:
|
||||
if ((~reg->var_off.mask & reg->var_off.value) & val)
|
||||
return 1;
|
||||
if (!((reg->var_off.mask | reg->var_off.value) & val))
|
||||
return 0;
|
||||
break;
|
||||
case BPF_JGT:
|
||||
if (reg->umin_value > val)
|
||||
return 1;
|
||||
@@ -3856,6 +3949,13 @@ static void reg_set_min_max(struct bpf_reg_state *true_reg,
|
||||
*/
|
||||
__mark_reg_known(false_reg, val);
|
||||
break;
|
||||
case BPF_JSET:
|
||||
false_reg->var_off = tnum_and(false_reg->var_off,
|
||||
tnum_const(~val));
|
||||
if (is_power_of_2(val))
|
||||
true_reg->var_off = tnum_or(true_reg->var_off,
|
||||
tnum_const(val));
|
||||
break;
|
||||
case BPF_JGT:
|
||||
false_reg->umax_value = min(false_reg->umax_value, val);
|
||||
true_reg->umin_value = max(true_reg->umin_value, val + 1);
|
||||
@@ -3928,6 +4028,13 @@ static void reg_set_min_max_inv(struct bpf_reg_state *true_reg,
|
||||
*/
|
||||
__mark_reg_known(false_reg, val);
|
||||
break;
|
||||
case BPF_JSET:
|
||||
false_reg->var_off = tnum_and(false_reg->var_off,
|
||||
tnum_const(~val));
|
||||
if (is_power_of_2(val))
|
||||
true_reg->var_off = tnum_or(true_reg->var_off,
|
||||
tnum_const(val));
|
||||
break;
|
||||
case BPF_JGT:
|
||||
true_reg->umax_value = min(true_reg->umax_value, val - 1);
|
||||
false_reg->umin_value = max(false_reg->umin_value, val);
|
||||
@@ -4545,6 +4652,7 @@ static int push_insn(int t, int w, int e, struct bpf_verifier_env *env)
|
||||
return 0;
|
||||
|
||||
if (w < 0 || w >= env->prog->len) {
|
||||
verbose_linfo(env, t, "%d: ", t);
|
||||
verbose(env, "jump out of range from insn %d to %d\n", t, w);
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -4562,6 +4670,8 @@ static int push_insn(int t, int w, int e, struct bpf_verifier_env *env)
|
||||
insn_stack[cur_stack++] = w;
|
||||
return 1;
|
||||
} else if ((insn_state[w] & 0xF0) == DISCOVERED) {
|
||||
verbose_linfo(env, t, "%d: ", t);
|
||||
verbose_linfo(env, w, "%d: ", w);
|
||||
verbose(env, "back-edge from insn %d to %d\n", t, w);
|
||||
return -EINVAL;
|
||||
} else if (insn_state[w] == EXPLORED) {
|
||||
@@ -4584,10 +4694,6 @@ static int check_cfg(struct bpf_verifier_env *env)
|
||||
int ret = 0;
|
||||
int i, t;
|
||||
|
||||
ret = check_subprogs(env);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
insn_state = kcalloc(insn_cnt, sizeof(int), GFP_KERNEL);
|
||||
if (!insn_state)
|
||||
return -ENOMEM;
|
||||
@@ -4696,6 +4802,277 @@ err_free:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The minimum supported BTF func info size */
|
||||
#define MIN_BPF_FUNCINFO_SIZE 8
|
||||
#define MAX_FUNCINFO_REC_SIZE 252
|
||||
|
||||
static int check_btf_func(struct bpf_verifier_env *env,
|
||||
const union bpf_attr *attr,
|
||||
union bpf_attr __user *uattr)
|
||||
{
|
||||
u32 i, nfuncs, urec_size, min_size, prev_offset;
|
||||
u32 krec_size = sizeof(struct bpf_func_info);
|
||||
struct bpf_func_info *krecord;
|
||||
const struct btf_type *type;
|
||||
struct bpf_prog *prog;
|
||||
const struct btf *btf;
|
||||
void __user *urecord;
|
||||
int ret = 0;
|
||||
|
||||
nfuncs = attr->func_info_cnt;
|
||||
if (!nfuncs)
|
||||
return 0;
|
||||
|
||||
if (nfuncs != env->subprog_cnt) {
|
||||
verbose(env, "number of funcs in func_info doesn't match number of subprogs\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
urec_size = attr->func_info_rec_size;
|
||||
if (urec_size < MIN_BPF_FUNCINFO_SIZE ||
|
||||
urec_size > MAX_FUNCINFO_REC_SIZE ||
|
||||
urec_size % sizeof(u32)) {
|
||||
verbose(env, "invalid func info rec size %u\n", urec_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
prog = env->prog;
|
||||
btf = prog->aux->btf;
|
||||
|
||||
urecord = u64_to_user_ptr(attr->func_info);
|
||||
min_size = min_t(u32, krec_size, urec_size);
|
||||
|
||||
krecord = kvcalloc(nfuncs, krec_size, GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!krecord)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < nfuncs; i++) {
|
||||
ret = bpf_check_uarg_tail_zero(urecord, krec_size, urec_size);
|
||||
if (ret) {
|
||||
if (ret == -E2BIG) {
|
||||
verbose(env, "nonzero tailing record in func info");
|
||||
/* set the size kernel expects so loader can zero
|
||||
* out the rest of the record.
|
||||
*/
|
||||
if (put_user(min_size, &uattr->func_info_rec_size))
|
||||
ret = -EFAULT;
|
||||
}
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (copy_from_user(&krecord[i], urecord, min_size)) {
|
||||
ret = -EFAULT;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
/* check insn_off */
|
||||
if (i == 0) {
|
||||
if (krecord[i].insn_off) {
|
||||
verbose(env,
|
||||
"nonzero insn_off %u for the first func info record",
|
||||
krecord[i].insn_off);
|
||||
ret = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
} else if (krecord[i].insn_off <= prev_offset) {
|
||||
verbose(env,
|
||||
"same or smaller insn offset (%u) than previous func info record (%u)",
|
||||
krecord[i].insn_off, prev_offset);
|
||||
ret = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (env->subprog_info[i].start != krecord[i].insn_off) {
|
||||
verbose(env, "func_info BTF section doesn't match subprog layout in BPF program\n");
|
||||
ret = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
/* check type_id */
|
||||
type = btf_type_by_id(btf, krecord[i].type_id);
|
||||
if (!type || BTF_INFO_KIND(type->info) != BTF_KIND_FUNC) {
|
||||
verbose(env, "invalid type id %d in func info",
|
||||
krecord[i].type_id);
|
||||
ret = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
prev_offset = krecord[i].insn_off;
|
||||
urecord += urec_size;
|
||||
}
|
||||
|
||||
prog->aux->func_info = krecord;
|
||||
prog->aux->func_info_cnt = nfuncs;
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
kvfree(krecord);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void adjust_btf_func(struct bpf_verifier_env *env)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!env->prog->aux->func_info)
|
||||
return;
|
||||
|
||||
for (i = 0; i < env->subprog_cnt; i++)
|
||||
env->prog->aux->func_info[i].insn_off = env->subprog_info[i].start;
|
||||
}
|
||||
|
||||
#define MIN_BPF_LINEINFO_SIZE (offsetof(struct bpf_line_info, line_col) + \
|
||||
sizeof(((struct bpf_line_info *)(0))->line_col))
|
||||
#define MAX_LINEINFO_REC_SIZE MAX_FUNCINFO_REC_SIZE
|
||||
|
||||
static int check_btf_line(struct bpf_verifier_env *env,
|
||||
const union bpf_attr *attr,
|
||||
union bpf_attr __user *uattr)
|
||||
{
|
||||
u32 i, s, nr_linfo, ncopy, expected_size, rec_size, prev_offset = 0;
|
||||
struct bpf_subprog_info *sub;
|
||||
struct bpf_line_info *linfo;
|
||||
struct bpf_prog *prog;
|
||||
const struct btf *btf;
|
||||
void __user *ulinfo;
|
||||
int err;
|
||||
|
||||
nr_linfo = attr->line_info_cnt;
|
||||
if (!nr_linfo)
|
||||
return 0;
|
||||
|
||||
rec_size = attr->line_info_rec_size;
|
||||
if (rec_size < MIN_BPF_LINEINFO_SIZE ||
|
||||
rec_size > MAX_LINEINFO_REC_SIZE ||
|
||||
rec_size & (sizeof(u32) - 1))
|
||||
return -EINVAL;
|
||||
|
||||
/* Need to zero it in case the userspace may
|
||||
* pass in a smaller bpf_line_info object.
|
||||
*/
|
||||
linfo = kvcalloc(nr_linfo, sizeof(struct bpf_line_info),
|
||||
GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!linfo)
|
||||
return -ENOMEM;
|
||||
|
||||
prog = env->prog;
|
||||
btf = prog->aux->btf;
|
||||
|
||||
s = 0;
|
||||
sub = env->subprog_info;
|
||||
ulinfo = u64_to_user_ptr(attr->line_info);
|
||||
expected_size = sizeof(struct bpf_line_info);
|
||||
ncopy = min_t(u32, expected_size, rec_size);
|
||||
for (i = 0; i < nr_linfo; i++) {
|
||||
err = bpf_check_uarg_tail_zero(ulinfo, expected_size, rec_size);
|
||||
if (err) {
|
||||
if (err == -E2BIG) {
|
||||
verbose(env, "nonzero tailing record in line_info");
|
||||
if (put_user(expected_size,
|
||||
&uattr->line_info_rec_size))
|
||||
err = -EFAULT;
|
||||
}
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (copy_from_user(&linfo[i], ulinfo, ncopy)) {
|
||||
err = -EFAULT;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check insn_off to ensure
|
||||
* 1) strictly increasing AND
|
||||
* 2) bounded by prog->len
|
||||
*
|
||||
* The linfo[0].insn_off == 0 check logically falls into
|
||||
* the later "missing bpf_line_info for func..." case
|
||||
* because the first linfo[0].insn_off must be the
|
||||
* first sub also and the first sub must have
|
||||
* subprog_info[0].start == 0.
|
||||
*/
|
||||
if ((i && linfo[i].insn_off <= prev_offset) ||
|
||||
linfo[i].insn_off >= prog->len) {
|
||||
verbose(env, "Invalid line_info[%u].insn_off:%u (prev_offset:%u prog->len:%u)\n",
|
||||
i, linfo[i].insn_off, prev_offset,
|
||||
prog->len);
|
||||
err = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (!prog->insnsi[linfo[i].insn_off].code) {
|
||||
verbose(env,
|
||||
"Invalid insn code at line_info[%u].insn_off\n",
|
||||
i);
|
||||
err = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (!btf_name_by_offset(btf, linfo[i].line_off) ||
|
||||
!btf_name_by_offset(btf, linfo[i].file_name_off)) {
|
||||
verbose(env, "Invalid line_info[%u].line_off or .file_name_off\n", i);
|
||||
err = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (s != env->subprog_cnt) {
|
||||
if (linfo[i].insn_off == sub[s].start) {
|
||||
sub[s].linfo_idx = i;
|
||||
s++;
|
||||
} else if (sub[s].start < linfo[i].insn_off) {
|
||||
verbose(env, "missing bpf_line_info for func#%u\n", s);
|
||||
err = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
}
|
||||
|
||||
prev_offset = linfo[i].insn_off;
|
||||
ulinfo += rec_size;
|
||||
}
|
||||
|
||||
if (s != env->subprog_cnt) {
|
||||
verbose(env, "missing bpf_line_info for %u funcs starting from func#%u\n",
|
||||
env->subprog_cnt - s, s);
|
||||
err = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
prog->aux->linfo = linfo;
|
||||
prog->aux->nr_linfo = nr_linfo;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
kvfree(linfo);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int check_btf_info(struct bpf_verifier_env *env,
|
||||
const union bpf_attr *attr,
|
||||
union bpf_attr __user *uattr)
|
||||
{
|
||||
struct btf *btf;
|
||||
int err;
|
||||
|
||||
if (!attr->func_info_cnt && !attr->line_info_cnt)
|
||||
return 0;
|
||||
|
||||
btf = btf_get_by_fd(attr->prog_btf_fd);
|
||||
if (IS_ERR(btf))
|
||||
return PTR_ERR(btf);
|
||||
env->prog->aux->btf = btf;
|
||||
|
||||
err = check_btf_func(env, attr, uattr);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = check_btf_line(env, attr, uattr);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check %cur's range satisfies %old's */
|
||||
static bool range_within(struct bpf_reg_state *old,
|
||||
struct bpf_reg_state *cur)
|
||||
@@ -4742,6 +5119,102 @@ static bool check_ids(u32 old_id, u32 cur_id, struct idpair *idmap)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void clean_func_state(struct bpf_verifier_env *env,
|
||||
struct bpf_func_state *st)
|
||||
{
|
||||
enum bpf_reg_liveness live;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < BPF_REG_FP; i++) {
|
||||
live = st->regs[i].live;
|
||||
/* liveness must not touch this register anymore */
|
||||
st->regs[i].live |= REG_LIVE_DONE;
|
||||
if (!(live & REG_LIVE_READ))
|
||||
/* since the register is unused, clear its state
|
||||
* to make further comparison simpler
|
||||
*/
|
||||
__mark_reg_not_init(&st->regs[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < st->allocated_stack / BPF_REG_SIZE; i++) {
|
||||
live = st->stack[i].spilled_ptr.live;
|
||||
/* liveness must not touch this stack slot anymore */
|
||||
st->stack[i].spilled_ptr.live |= REG_LIVE_DONE;
|
||||
if (!(live & REG_LIVE_READ)) {
|
||||
__mark_reg_not_init(&st->stack[i].spilled_ptr);
|
||||
for (j = 0; j < BPF_REG_SIZE; j++)
|
||||
st->stack[i].slot_type[j] = STACK_INVALID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void clean_verifier_state(struct bpf_verifier_env *env,
|
||||
struct bpf_verifier_state *st)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (st->frame[0]->regs[0].live & REG_LIVE_DONE)
|
||||
/* all regs in this state in all frames were already marked */
|
||||
return;
|
||||
|
||||
for (i = 0; i <= st->curframe; i++)
|
||||
clean_func_state(env, st->frame[i]);
|
||||
}
|
||||
|
||||
/* the parentage chains form a tree.
|
||||
* the verifier states are added to state lists at given insn and
|
||||
* pushed into state stack for future exploration.
|
||||
* when the verifier reaches bpf_exit insn some of the verifer states
|
||||
* stored in the state lists have their final liveness state already,
|
||||
* but a lot of states will get revised from liveness point of view when
|
||||
* the verifier explores other branches.
|
||||
* Example:
|
||||
* 1: r0 = 1
|
||||
* 2: if r1 == 100 goto pc+1
|
||||
* 3: r0 = 2
|
||||
* 4: exit
|
||||
* when the verifier reaches exit insn the register r0 in the state list of
|
||||
* insn 2 will be seen as !REG_LIVE_READ. Then the verifier pops the other_branch
|
||||
* of insn 2 and goes exploring further. At the insn 4 it will walk the
|
||||
* parentage chain from insn 4 into insn 2 and will mark r0 as REG_LIVE_READ.
|
||||
*
|
||||
* Since the verifier pushes the branch states as it sees them while exploring
|
||||
* the program the condition of walking the branch instruction for the second
|
||||
* time means that all states below this branch were already explored and
|
||||
* their final liveness markes are already propagated.
|
||||
* Hence when the verifier completes the search of state list in is_state_visited()
|
||||
* we can call this clean_live_states() function to mark all liveness states
|
||||
* as REG_LIVE_DONE to indicate that 'parent' pointers of 'struct bpf_reg_state'
|
||||
* will not be used.
|
||||
* This function also clears the registers and stack for states that !READ
|
||||
* to simplify state merging.
|
||||
*
|
||||
* Important note here that walking the same branch instruction in the callee
|
||||
* doesn't meant that the states are DONE. The verifier has to compare
|
||||
* the callsites
|
||||
*/
|
||||
static void clean_live_states(struct bpf_verifier_env *env, int insn,
|
||||
struct bpf_verifier_state *cur)
|
||||
{
|
||||
struct bpf_verifier_state_list *sl;
|
||||
int i;
|
||||
|
||||
sl = env->explored_states[insn];
|
||||
if (!sl)
|
||||
return;
|
||||
|
||||
while (sl != STATE_LIST_MARK) {
|
||||
if (sl->state.curframe != cur->curframe)
|
||||
goto next;
|
||||
for (i = 0; i <= cur->curframe; i++)
|
||||
if (sl->state.frame[i]->callsite != cur->frame[i]->callsite)
|
||||
goto next;
|
||||
clean_verifier_state(env, &sl->state);
|
||||
next:
|
||||
sl = sl->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns true if (rold safe implies rcur safe) */
|
||||
static bool regsafe(struct bpf_reg_state *rold, struct bpf_reg_state *rcur,
|
||||
struct idpair *idmap)
|
||||
@@ -4855,12 +5328,6 @@ static bool stacksafe(struct bpf_func_state *old,
|
||||
{
|
||||
int i, spi;
|
||||
|
||||
/* if explored stack has more populated slots than current stack
|
||||
* such stacks are not equivalent
|
||||
*/
|
||||
if (old->allocated_stack > cur->allocated_stack)
|
||||
return false;
|
||||
|
||||
/* walk slots of the explored stack and ignore any additional
|
||||
* slots in the current stack, since explored(safe) state
|
||||
* didn't use them
|
||||
@@ -4868,12 +5335,21 @@ static bool stacksafe(struct bpf_func_state *old,
|
||||
for (i = 0; i < old->allocated_stack; i++) {
|
||||
spi = i / BPF_REG_SIZE;
|
||||
|
||||
if (!(old->stack[spi].spilled_ptr.live & REG_LIVE_READ))
|
||||
if (!(old->stack[spi].spilled_ptr.live & REG_LIVE_READ)) {
|
||||
i += BPF_REG_SIZE - 1;
|
||||
/* explored state didn't use this */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (old->stack[spi].slot_type[i % BPF_REG_SIZE] == STACK_INVALID)
|
||||
continue;
|
||||
|
||||
/* explored stack has more populated slots than current stack
|
||||
* and these slots were used
|
||||
*/
|
||||
if (i >= cur->allocated_stack)
|
||||
return false;
|
||||
|
||||
/* if old state was safe with misc data in the stack
|
||||
* it will be safe with zero-initialized stack.
|
||||
* The opposite is not true
|
||||
@@ -5057,6 +5533,8 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
|
||||
*/
|
||||
return 0;
|
||||
|
||||
clean_live_states(env, insn_idx, cur);
|
||||
|
||||
while (sl != STATE_LIST_MARK) {
|
||||
if (states_equal(env, &sl->state, cur)) {
|
||||
/* reached equivalent register/stack state,
|
||||
@@ -5176,6 +5654,8 @@ static int do_check(struct bpf_verifier_env *env)
|
||||
int insn_processed = 0;
|
||||
bool do_print_state = false;
|
||||
|
||||
env->prev_linfo = NULL;
|
||||
|
||||
state = kzalloc(sizeof(struct bpf_verifier_state), GFP_KERNEL);
|
||||
if (!state)
|
||||
return -ENOMEM;
|
||||
@@ -5249,6 +5729,7 @@ static int do_check(struct bpf_verifier_env *env)
|
||||
.private_data = env,
|
||||
};
|
||||
|
||||
verbose_linfo(env, insn_idx, "; ");
|
||||
verbose(env, "%d: ", insn_idx);
|
||||
print_bpf_insn(&cbs, insn, env->allow_ptr_leaks);
|
||||
}
|
||||
@@ -5789,10 +6270,10 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
|
||||
int i, cnt, size, ctx_field_size, delta = 0;
|
||||
const int insn_cnt = env->prog->len;
|
||||
struct bpf_insn insn_buf[16], *insn;
|
||||
u32 target_size, size_default, off;
|
||||
struct bpf_prog *new_prog;
|
||||
enum bpf_access_type type;
|
||||
bool is_narrower_load;
|
||||
u32 target_size;
|
||||
|
||||
if (ops->gen_prologue || env->seen_direct_write) {
|
||||
if (!ops->gen_prologue) {
|
||||
@@ -5885,9 +6366,9 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
|
||||
* we will apply proper mask to the result.
|
||||
*/
|
||||
is_narrower_load = size < ctx_field_size;
|
||||
size_default = bpf_ctx_off_adjust_machine(ctx_field_size);
|
||||
off = insn->off;
|
||||
if (is_narrower_load) {
|
||||
u32 size_default = bpf_ctx_off_adjust_machine(ctx_field_size);
|
||||
u32 off = insn->off;
|
||||
u8 size_code;
|
||||
|
||||
if (type == BPF_WRITE) {
|
||||
@@ -5915,12 +6396,23 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
|
||||
}
|
||||
|
||||
if (is_narrower_load && size < target_size) {
|
||||
if (ctx_field_size <= 4)
|
||||
u8 shift = (off & (size_default - 1)) * 8;
|
||||
|
||||
if (ctx_field_size <= 4) {
|
||||
if (shift)
|
||||
insn_buf[cnt++] = BPF_ALU32_IMM(BPF_RSH,
|
||||
insn->dst_reg,
|
||||
shift);
|
||||
insn_buf[cnt++] = BPF_ALU32_IMM(BPF_AND, insn->dst_reg,
|
||||
(1 << size * 8) - 1);
|
||||
else
|
||||
} else {
|
||||
if (shift)
|
||||
insn_buf[cnt++] = BPF_ALU64_IMM(BPF_RSH,
|
||||
insn->dst_reg,
|
||||
shift);
|
||||
insn_buf[cnt++] = BPF_ALU64_IMM(BPF_AND, insn->dst_reg,
|
||||
(1 << size * 8) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
|
||||
@@ -5943,7 +6435,7 @@ static int jit_subprogs(struct bpf_verifier_env *env)
|
||||
int i, j, subprog_start, subprog_end = 0, len, subprog;
|
||||
struct bpf_insn *insn;
|
||||
void *old_bpf_func;
|
||||
int err = -ENOMEM;
|
||||
int err;
|
||||
|
||||
if (env->subprog_cnt <= 1)
|
||||
return 0;
|
||||
@@ -5974,6 +6466,11 @@ static int jit_subprogs(struct bpf_verifier_env *env)
|
||||
insn->imm = 1;
|
||||
}
|
||||
|
||||
err = bpf_prog_alloc_jited_linfo(prog);
|
||||
if (err)
|
||||
goto out_undo_insn;
|
||||
|
||||
err = -ENOMEM;
|
||||
func = kcalloc(env->subprog_cnt, sizeof(prog), GFP_KERNEL);
|
||||
if (!func)
|
||||
goto out_undo_insn;
|
||||
@@ -5993,12 +6490,21 @@ static int jit_subprogs(struct bpf_verifier_env *env)
|
||||
if (bpf_prog_calc_tag(func[i]))
|
||||
goto out_free;
|
||||
func[i]->is_func = 1;
|
||||
func[i]->aux->func_idx = i;
|
||||
/* the btf and func_info will be freed only at prog->aux */
|
||||
func[i]->aux->btf = prog->aux->btf;
|
||||
func[i]->aux->func_info = prog->aux->func_info;
|
||||
|
||||
/* Use bpf_prog_F_tag to indicate functions in stack traces.
|
||||
* Long term would need debug info to populate names
|
||||
*/
|
||||
func[i]->aux->name[0] = 'F';
|
||||
func[i]->aux->stack_depth = env->subprog_info[i].stack_depth;
|
||||
func[i]->jit_requested = 1;
|
||||
func[i]->aux->linfo = prog->aux->linfo;
|
||||
func[i]->aux->nr_linfo = prog->aux->nr_linfo;
|
||||
func[i]->aux->jited_linfo = prog->aux->jited_linfo;
|
||||
func[i]->aux->linfo_idx = env->subprog_info[i].linfo_idx;
|
||||
func[i] = bpf_int_jit_compile(func[i]);
|
||||
if (!func[i]->jited) {
|
||||
err = -ENOTSUPP;
|
||||
@@ -6072,6 +6578,7 @@ static int jit_subprogs(struct bpf_verifier_env *env)
|
||||
prog->bpf_func = func[0]->bpf_func;
|
||||
prog->aux->func = func;
|
||||
prog->aux->func_cnt = env->subprog_cnt;
|
||||
bpf_prog_free_unused_jited_linfo(prog);
|
||||
return 0;
|
||||
out_free:
|
||||
for (i = 0; i < env->subprog_cnt; i++)
|
||||
@@ -6088,6 +6595,7 @@ out_undo_insn:
|
||||
insn->off = 0;
|
||||
insn->imm = env->insn_aux_data[i].call_imm;
|
||||
}
|
||||
bpf_prog_free_jited_linfo(prog);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -6220,6 +6728,7 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
|
||||
*/
|
||||
prog->cb_access = 1;
|
||||
env->prog->aux->stack_depth = MAX_BPF_STACK;
|
||||
env->prog->aux->max_pkt_offset = MAX_PACKET_OFF;
|
||||
|
||||
/* mark bpf_tail_call as different opcode to avoid
|
||||
* conditional branch in the interpeter for every normal
|
||||
@@ -6384,7 +6893,8 @@ static void free_states(struct bpf_verifier_env *env)
|
||||
kfree(env->explored_states);
|
||||
}
|
||||
|
||||
int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)
|
||||
int bpf_check(struct bpf_prog **prog, union bpf_attr *attr,
|
||||
union bpf_attr __user *uattr)
|
||||
{
|
||||
struct bpf_verifier_env *env;
|
||||
struct bpf_verifier_log *log;
|
||||
@@ -6432,13 +6942,15 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)
|
||||
env->strict_alignment = !!(attr->prog_flags & BPF_F_STRICT_ALIGNMENT);
|
||||
if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS))
|
||||
env->strict_alignment = true;
|
||||
if (attr->prog_flags & BPF_F_ANY_ALIGNMENT)
|
||||
env->strict_alignment = false;
|
||||
|
||||
ret = replace_map_fd_with_map_ptr(env);
|
||||
if (ret < 0)
|
||||
goto skip_full_check;
|
||||
|
||||
if (bpf_prog_is_dev_bound(env->prog->aux)) {
|
||||
ret = bpf_prog_offload_verifier_prep(env);
|
||||
ret = bpf_prog_offload_verifier_prep(env->prog);
|
||||
if (ret)
|
||||
goto skip_full_check;
|
||||
}
|
||||
@@ -6452,6 +6964,14 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)
|
||||
|
||||
env->allow_ptr_leaks = capable(CAP_SYS_ADMIN);
|
||||
|
||||
ret = check_subprogs(env);
|
||||
if (ret < 0)
|
||||
goto skip_full_check;
|
||||
|
||||
ret = check_btf_info(env, attr, uattr);
|
||||
if (ret < 0)
|
||||
goto skip_full_check;
|
||||
|
||||
ret = check_cfg(env);
|
||||
if (ret < 0)
|
||||
goto skip_full_check;
|
||||
@@ -6469,12 +6989,13 @@ skip_full_check:
|
||||
while (!pop_stack(env, NULL, NULL));
|
||||
free_states(env);
|
||||
|
||||
if (ret == 0)
|
||||
sanitize_dead_code(env);
|
||||
|
||||
if (ret == 0)
|
||||
ret = check_max_stack_depth(env);
|
||||
|
||||
/* instruction rewrites happen after this point */
|
||||
if (ret == 0)
|
||||
sanitize_dead_code(env);
|
||||
|
||||
if (ret == 0)
|
||||
/* program is valid, convert *(u32*)(ctx + off) accesses */
|
||||
ret = convert_ctx_accesses(env);
|
||||
@@ -6513,6 +7034,9 @@ skip_full_check:
|
||||
convert_pseudo_ld_imm64(env);
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
adjust_btf_func(env);
|
||||
|
||||
err_release_maps:
|
||||
if (!env->prog->aux->used_maps)
|
||||
/* if we didn't copy map pointers into bpf_prog_info, release
|
||||
|
@@ -3095,6 +3095,11 @@ static int find_module_sections(struct module *mod, struct load_info *info)
|
||||
sizeof(*mod->tracepoints_ptrs),
|
||||
&mod->num_tracepoints);
|
||||
#endif
|
||||
#ifdef CONFIG_BPF_EVENTS
|
||||
mod->bpf_raw_events = section_objs(info, "__bpf_raw_tp_map",
|
||||
sizeof(*mod->bpf_raw_events),
|
||||
&mod->num_bpf_raw_events);
|
||||
#endif
|
||||
#ifdef HAVE_JUMP_LABEL
|
||||
mod->jump_entries = section_objs(info, "__jump_table",
|
||||
sizeof(*mod->jump_entries),
|
||||
|
@@ -17,6 +17,43 @@
|
||||
#include "trace_probe.h"
|
||||
#include "trace.h"
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
struct bpf_trace_module {
|
||||
struct module *module;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static LIST_HEAD(bpf_trace_modules);
|
||||
static DEFINE_MUTEX(bpf_module_mutex);
|
||||
|
||||
static struct bpf_raw_event_map *bpf_get_raw_tracepoint_module(const char *name)
|
||||
{
|
||||
struct bpf_raw_event_map *btp, *ret = NULL;
|
||||
struct bpf_trace_module *btm;
|
||||
unsigned int i;
|
||||
|
||||
mutex_lock(&bpf_module_mutex);
|
||||
list_for_each_entry(btm, &bpf_trace_modules, list) {
|
||||
for (i = 0; i < btm->module->num_bpf_raw_events; ++i) {
|
||||
btp = &btm->module->bpf_raw_events[i];
|
||||
if (!strcmp(btp->tp->name, name)) {
|
||||
if (try_module_get(btm->module))
|
||||
ret = btp;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&bpf_module_mutex);
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
static struct bpf_raw_event_map *bpf_get_raw_tracepoint_module(const char *name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif /* CONFIG_MODULES */
|
||||
|
||||
u64 bpf_get_stackid(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
|
||||
u64 bpf_get_stack(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
|
||||
|
||||
@@ -1076,7 +1113,7 @@ int perf_event_query_prog_array(struct perf_event *event, void __user *info)
|
||||
extern struct bpf_raw_event_map __start__bpf_raw_tp[];
|
||||
extern struct bpf_raw_event_map __stop__bpf_raw_tp[];
|
||||
|
||||
struct bpf_raw_event_map *bpf_find_raw_tracepoint(const char *name)
|
||||
struct bpf_raw_event_map *bpf_get_raw_tracepoint(const char *name)
|
||||
{
|
||||
struct bpf_raw_event_map *btp = __start__bpf_raw_tp;
|
||||
|
||||
@@ -1084,7 +1121,16 @@ struct bpf_raw_event_map *bpf_find_raw_tracepoint(const char *name)
|
||||
if (!strcmp(btp->tp->name, name))
|
||||
return btp;
|
||||
}
|
||||
return NULL;
|
||||
|
||||
return bpf_get_raw_tracepoint_module(name);
|
||||
}
|
||||
|
||||
void bpf_put_raw_tracepoint(struct bpf_raw_event_map *btp)
|
||||
{
|
||||
struct module *mod = __module_address((unsigned long)btp);
|
||||
|
||||
if (mod)
|
||||
module_put(mod);
|
||||
}
|
||||
|
||||
static __always_inline
|
||||
@@ -1222,3 +1268,52 @@ int bpf_get_perf_event_info(const struct perf_event *event, u32 *prog_id,
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
int bpf_event_notify(struct notifier_block *nb, unsigned long op, void *module)
|
||||
{
|
||||
struct bpf_trace_module *btm, *tmp;
|
||||
struct module *mod = module;
|
||||
|
||||
if (mod->num_bpf_raw_events == 0 ||
|
||||
(op != MODULE_STATE_COMING && op != MODULE_STATE_GOING))
|
||||
return 0;
|
||||
|
||||
mutex_lock(&bpf_module_mutex);
|
||||
|
||||
switch (op) {
|
||||
case MODULE_STATE_COMING:
|
||||
btm = kzalloc(sizeof(*btm), GFP_KERNEL);
|
||||
if (btm) {
|
||||
btm->module = module;
|
||||
list_add(&btm->list, &bpf_trace_modules);
|
||||
}
|
||||
break;
|
||||
case MODULE_STATE_GOING:
|
||||
list_for_each_entry_safe(btm, tmp, &bpf_trace_modules, list) {
|
||||
if (btm->module == module) {
|
||||
list_del(&btm->list);
|
||||
kfree(btm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&bpf_module_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct notifier_block bpf_module_nb = {
|
||||
.notifier_call = bpf_event_notify,
|
||||
};
|
||||
|
||||
int __init bpf_event_init(void)
|
||||
{
|
||||
register_module_notifier(&bpf_module_nb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fs_initcall(bpf_event_init);
|
||||
#endif /* CONFIG_MODULES */
|
||||
|
Reference in New Issue
Block a user