mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-25 06:03:36 -04:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
Alexei Starovoitov says: ==================== pull-request: bpf-next 2019-05-31 The following pull-request contains BPF updates for your *net-next* tree. Lots of exciting new features in the first PR of this developement cycle! The main changes are: 1) misc verifier improvements, from Alexei. 2) bpftool can now convert btf to valid C, from Andrii. 3) verifier can insert explicit ZEXT insn when requested by 32-bit JITs. This feature greatly improves BPF speed on 32-bit architectures. From Jiong. 4) cgroups will now auto-detach bpf programs. This fixes issue of thousands bpf programs got stuck in dying cgroups. From Roman. 5) new bpf_send_signal() helper, from Yonghong. 6) cgroup inet skb programs can signal CN to the stack, from Lawrence. 7) miscellaneous cleanups, from many developers. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
0462eaacee
122 changed files with 6430 additions and 1013 deletions
|
|
@ -83,6 +83,7 @@ static struct bpf_map *array_map_alloc(union bpf_attr *attr)
|
|||
u32 elem_size, index_mask, max_entries;
|
||||
bool unpriv = !capable(CAP_SYS_ADMIN);
|
||||
u64 cost, array_size, mask64;
|
||||
struct bpf_map_memory mem;
|
||||
struct bpf_array *array;
|
||||
|
||||
elem_size = round_up(attr->value_size, 8);
|
||||
|
|
@ -116,32 +117,29 @@ static struct bpf_map *array_map_alloc(union bpf_attr *attr)
|
|||
|
||||
/* make sure there is no u32 overflow later in round_up() */
|
||||
cost = array_size;
|
||||
if (cost >= U32_MAX - PAGE_SIZE)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
if (percpu) {
|
||||
if (percpu)
|
||||
cost += (u64)attr->max_entries * elem_size * num_possible_cpus();
|
||||
if (cost >= U32_MAX - PAGE_SIZE)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
cost = round_up(cost, PAGE_SIZE) >> PAGE_SHIFT;
|
||||
|
||||
ret = bpf_map_precharge_memlock(cost);
|
||||
ret = bpf_map_charge_init(&mem, cost);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
/* allocate all map elements and zero-initialize them */
|
||||
array = bpf_map_area_alloc(array_size, numa_node);
|
||||
if (!array)
|
||||
if (!array) {
|
||||
bpf_map_charge_finish(&mem);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
array->index_mask = index_mask;
|
||||
array->map.unpriv_array = unpriv;
|
||||
|
||||
/* copy mandatory map attributes */
|
||||
bpf_map_init_from_attr(&array->map, attr);
|
||||
array->map.pages = cost;
|
||||
bpf_map_charge_move(&array->map.memory, &mem);
|
||||
array->elem_size = elem_size;
|
||||
|
||||
if (percpu && bpf_array_alloc_percpu(array)) {
|
||||
bpf_map_charge_finish(&array->map.memory);
|
||||
bpf_map_area_free(array);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,13 +22,23 @@
|
|||
DEFINE_STATIC_KEY_FALSE(cgroup_bpf_enabled_key);
|
||||
EXPORT_SYMBOL(cgroup_bpf_enabled_key);
|
||||
|
||||
/**
|
||||
* cgroup_bpf_put() - put references of all bpf programs
|
||||
* @cgrp: the cgroup to modify
|
||||
*/
|
||||
void cgroup_bpf_put(struct cgroup *cgrp)
|
||||
void cgroup_bpf_offline(struct cgroup *cgrp)
|
||||
{
|
||||
cgroup_get(cgrp);
|
||||
percpu_ref_kill(&cgrp->bpf.refcnt);
|
||||
}
|
||||
|
||||
/**
|
||||
* cgroup_bpf_release() - put references of all bpf programs and
|
||||
* release all cgroup bpf data
|
||||
* @work: work structure embedded into the cgroup to modify
|
||||
*/
|
||||
static void cgroup_bpf_release(struct work_struct *work)
|
||||
{
|
||||
struct cgroup *cgrp = container_of(work, struct cgroup,
|
||||
bpf.release_work);
|
||||
enum bpf_cgroup_storage_type stype;
|
||||
struct bpf_prog_array *old_array;
|
||||
unsigned int type;
|
||||
|
||||
for (type = 0; type < ARRAY_SIZE(cgrp->bpf.progs); type++) {
|
||||
|
|
@ -45,8 +55,27 @@ void cgroup_bpf_put(struct cgroup *cgrp)
|
|||
kfree(pl);
|
||||
static_branch_dec(&cgroup_bpf_enabled_key);
|
||||
}
|
||||
bpf_prog_array_free(cgrp->bpf.effective[type]);
|
||||
old_array = rcu_dereference_protected(
|
||||
cgrp->bpf.effective[type],
|
||||
percpu_ref_is_dying(&cgrp->bpf.refcnt));
|
||||
bpf_prog_array_free(old_array);
|
||||
}
|
||||
|
||||
percpu_ref_exit(&cgrp->bpf.refcnt);
|
||||
cgroup_put(cgrp);
|
||||
}
|
||||
|
||||
/**
|
||||
* cgroup_bpf_release_fn() - callback used to schedule releasing
|
||||
* of bpf cgroup data
|
||||
* @ref: percpu ref counter structure
|
||||
*/
|
||||
static void cgroup_bpf_release_fn(struct percpu_ref *ref)
|
||||
{
|
||||
struct cgroup *cgrp = container_of(ref, struct cgroup, bpf.refcnt);
|
||||
|
||||
INIT_WORK(&cgrp->bpf.release_work, cgroup_bpf_release);
|
||||
queue_work(system_wq, &cgrp->bpf.release_work);
|
||||
}
|
||||
|
||||
/* count number of elements in the list.
|
||||
|
|
@ -101,7 +130,7 @@ static bool hierarchy_allows_attach(struct cgroup *cgrp,
|
|||
*/
|
||||
static int compute_effective_progs(struct cgroup *cgrp,
|
||||
enum bpf_attach_type type,
|
||||
struct bpf_prog_array __rcu **array)
|
||||
struct bpf_prog_array **array)
|
||||
{
|
||||
enum bpf_cgroup_storage_type stype;
|
||||
struct bpf_prog_array *progs;
|
||||
|
|
@ -139,17 +168,16 @@ static int compute_effective_progs(struct cgroup *cgrp,
|
|||
}
|
||||
} while ((p = cgroup_parent(p)));
|
||||
|
||||
rcu_assign_pointer(*array, progs);
|
||||
*array = progs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void activate_effective_progs(struct cgroup *cgrp,
|
||||
enum bpf_attach_type type,
|
||||
struct bpf_prog_array __rcu *array)
|
||||
struct bpf_prog_array *old_array)
|
||||
{
|
||||
struct bpf_prog_array __rcu *old_array;
|
||||
|
||||
old_array = xchg(&cgrp->bpf.effective[type], array);
|
||||
rcu_swap_protected(cgrp->bpf.effective[type], old_array,
|
||||
lockdep_is_held(&cgroup_mutex));
|
||||
/* free prog array after grace period, since __cgroup_bpf_run_*()
|
||||
* might be still walking the array
|
||||
*/
|
||||
|
|
@ -166,8 +194,13 @@ int cgroup_bpf_inherit(struct cgroup *cgrp)
|
|||
* that array below is variable length
|
||||
*/
|
||||
#define NR ARRAY_SIZE(cgrp->bpf.effective)
|
||||
struct bpf_prog_array __rcu *arrays[NR] = {};
|
||||
int i;
|
||||
struct bpf_prog_array *arrays[NR] = {};
|
||||
int ret, i;
|
||||
|
||||
ret = percpu_ref_init(&cgrp->bpf.refcnt, cgroup_bpf_release_fn, 0,
|
||||
GFP_KERNEL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < NR; i++)
|
||||
INIT_LIST_HEAD(&cgrp->bpf.progs[i]);
|
||||
|
|
@ -183,6 +216,9 @@ int cgroup_bpf_inherit(struct cgroup *cgrp)
|
|||
cleanup:
|
||||
for (i = 0; i < NR; i++)
|
||||
bpf_prog_array_free(arrays[i]);
|
||||
|
||||
percpu_ref_exit(&cgrp->bpf.refcnt);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
|
@ -444,10 +480,14 @@ int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
|
|||
enum bpf_attach_type type = attr->query.attach_type;
|
||||
struct list_head *progs = &cgrp->bpf.progs[type];
|
||||
u32 flags = cgrp->bpf.flags[type];
|
||||
struct bpf_prog_array *effective;
|
||||
int cnt, ret = 0, i;
|
||||
|
||||
effective = rcu_dereference_protected(cgrp->bpf.effective[type],
|
||||
lockdep_is_held(&cgroup_mutex));
|
||||
|
||||
if (attr->query.query_flags & BPF_F_QUERY_EFFECTIVE)
|
||||
cnt = bpf_prog_array_length(cgrp->bpf.effective[type]);
|
||||
cnt = bpf_prog_array_length(effective);
|
||||
else
|
||||
cnt = prog_list_length(progs);
|
||||
|
||||
|
|
@ -464,8 +504,7 @@ int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
|
|||
}
|
||||
|
||||
if (attr->query.query_flags & BPF_F_QUERY_EFFECTIVE) {
|
||||
return bpf_prog_array_copy_to_user(cgrp->bpf.effective[type],
|
||||
prog_ids, cnt);
|
||||
return bpf_prog_array_copy_to_user(effective, prog_ids, cnt);
|
||||
} else {
|
||||
struct bpf_prog_list *pl;
|
||||
u32 id;
|
||||
|
|
@ -548,8 +587,16 @@ int cgroup_bpf_prog_query(const union bpf_attr *attr,
|
|||
* The program type passed in via @type must be suitable for network
|
||||
* filtering. No further check is performed to assert that.
|
||||
*
|
||||
* This function will return %-EPERM if any if an attached program was found
|
||||
* and if it returned != 1 during execution. In all other cases, 0 is returned.
|
||||
* For egress packets, this function can return:
|
||||
* NET_XMIT_SUCCESS (0) - continue with packet output
|
||||
* NET_XMIT_DROP (1) - drop packet and notify TCP to call cwr
|
||||
* NET_XMIT_CN (2) - continue with packet output and notify TCP
|
||||
* to call cwr
|
||||
* -EPERM - drop packet
|
||||
*
|
||||
* For ingress packets, this function will return -EPERM if any
|
||||
* attached program was found and if it returned != 1 during execution.
|
||||
* Otherwise 0 is returned.
|
||||
*/
|
||||
int __cgroup_bpf_run_filter_skb(struct sock *sk,
|
||||
struct sk_buff *skb,
|
||||
|
|
@ -575,12 +622,19 @@ int __cgroup_bpf_run_filter_skb(struct sock *sk,
|
|||
/* compute pointers for the bpf prog */
|
||||
bpf_compute_and_save_data_end(skb, &saved_data_end);
|
||||
|
||||
ret = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], skb,
|
||||
__bpf_prog_run_save_cb);
|
||||
if (type == BPF_CGROUP_INET_EGRESS) {
|
||||
ret = BPF_PROG_CGROUP_INET_EGRESS_RUN_ARRAY(
|
||||
cgrp->bpf.effective[type], skb, __bpf_prog_run_save_cb);
|
||||
} else {
|
||||
ret = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], skb,
|
||||
__bpf_prog_run_save_cb);
|
||||
ret = (ret == 1 ? 0 : -EPERM);
|
||||
}
|
||||
bpf_restore_data_end(skb, saved_data_end);
|
||||
__skb_pull(skb, offset);
|
||||
skb->sk = save_sk;
|
||||
return ret == 1 ? 0 : -EPERM;
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(__cgroup_bpf_run_filter_skb);
|
||||
|
||||
|
|
|
|||
|
|
@ -1795,38 +1795,33 @@ struct bpf_prog_array *bpf_prog_array_alloc(u32 prog_cnt, gfp_t flags)
|
|||
return &empty_prog_array.hdr;
|
||||
}
|
||||
|
||||
void bpf_prog_array_free(struct bpf_prog_array __rcu *progs)
|
||||
void bpf_prog_array_free(struct bpf_prog_array *progs)
|
||||
{
|
||||
if (!progs ||
|
||||
progs == (struct bpf_prog_array __rcu *)&empty_prog_array.hdr)
|
||||
if (!progs || progs == &empty_prog_array.hdr)
|
||||
return;
|
||||
kfree_rcu(progs, rcu);
|
||||
}
|
||||
|
||||
int bpf_prog_array_length(struct bpf_prog_array __rcu *array)
|
||||
int bpf_prog_array_length(struct bpf_prog_array *array)
|
||||
{
|
||||
struct bpf_prog_array_item *item;
|
||||
u32 cnt = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
item = rcu_dereference(array)->items;
|
||||
for (; item->prog; item++)
|
||||
for (item = array->items; item->prog; item++)
|
||||
if (item->prog != &dummy_bpf_prog.prog)
|
||||
cnt++;
|
||||
rcu_read_unlock();
|
||||
return cnt;
|
||||
}
|
||||
|
||||
|
||||
static bool bpf_prog_array_copy_core(struct bpf_prog_array __rcu *array,
|
||||
static bool bpf_prog_array_copy_core(struct bpf_prog_array *array,
|
||||
u32 *prog_ids,
|
||||
u32 request_cnt)
|
||||
{
|
||||
struct bpf_prog_array_item *item;
|
||||
int i = 0;
|
||||
|
||||
item = rcu_dereference_check(array, 1)->items;
|
||||
for (; item->prog; item++) {
|
||||
for (item = array->items; item->prog; item++) {
|
||||
if (item->prog == &dummy_bpf_prog.prog)
|
||||
continue;
|
||||
prog_ids[i] = item->prog->aux->id;
|
||||
|
|
@ -1839,7 +1834,7 @@ static bool bpf_prog_array_copy_core(struct bpf_prog_array __rcu *array,
|
|||
return !!(item->prog);
|
||||
}
|
||||
|
||||
int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *array,
|
||||
int bpf_prog_array_copy_to_user(struct bpf_prog_array *array,
|
||||
__u32 __user *prog_ids, u32 cnt)
|
||||
{
|
||||
unsigned long err = 0;
|
||||
|
|
@ -1850,18 +1845,12 @@ int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *array,
|
|||
* cnt = bpf_prog_array_length();
|
||||
* if (cnt > 0)
|
||||
* bpf_prog_array_copy_to_user(..., cnt);
|
||||
* so below kcalloc doesn't need extra cnt > 0 check, but
|
||||
* bpf_prog_array_length() releases rcu lock and
|
||||
* prog array could have been swapped with empty or larger array,
|
||||
* so always copy 'cnt' prog_ids to the user.
|
||||
* In a rare race the user will see zero prog_ids
|
||||
* so below kcalloc doesn't need extra cnt > 0 check.
|
||||
*/
|
||||
ids = kcalloc(cnt, sizeof(u32), GFP_USER | __GFP_NOWARN);
|
||||
if (!ids)
|
||||
return -ENOMEM;
|
||||
rcu_read_lock();
|
||||
nospc = bpf_prog_array_copy_core(array, ids, cnt);
|
||||
rcu_read_unlock();
|
||||
err = copy_to_user(prog_ids, ids, cnt * sizeof(u32));
|
||||
kfree(ids);
|
||||
if (err)
|
||||
|
|
@ -1871,19 +1860,19 @@ int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *array,
|
|||
return 0;
|
||||
}
|
||||
|
||||
void bpf_prog_array_delete_safe(struct bpf_prog_array __rcu *array,
|
||||
void bpf_prog_array_delete_safe(struct bpf_prog_array *array,
|
||||
struct bpf_prog *old_prog)
|
||||
{
|
||||
struct bpf_prog_array_item *item = array->items;
|
||||
struct bpf_prog_array_item *item;
|
||||
|
||||
for (; item->prog; item++)
|
||||
for (item = array->items; item->prog; item++)
|
||||
if (item->prog == old_prog) {
|
||||
WRITE_ONCE(item->prog, &dummy_bpf_prog.prog);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int bpf_prog_array_copy(struct bpf_prog_array __rcu *old_array,
|
||||
int bpf_prog_array_copy(struct bpf_prog_array *old_array,
|
||||
struct bpf_prog *exclude_prog,
|
||||
struct bpf_prog *include_prog,
|
||||
struct bpf_prog_array **new_array)
|
||||
|
|
@ -1947,7 +1936,7 @@ int bpf_prog_array_copy(struct bpf_prog_array __rcu *old_array,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int bpf_prog_array_copy_info(struct bpf_prog_array __rcu *array,
|
||||
int bpf_prog_array_copy_info(struct bpf_prog_array *array,
|
||||
u32 *prog_ids, u32 request_cnt,
|
||||
u32 *prog_cnt)
|
||||
{
|
||||
|
|
@ -2090,6 +2079,15 @@ bool __weak bpf_helper_changes_pkt_data(void *func)
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Return TRUE if the JIT backend wants verifier to enable sub-register usage
|
||||
* analysis code and wants explicit zero extension inserted by verifier.
|
||||
* Otherwise, return FALSE.
|
||||
*/
|
||||
bool __weak bpf_jit_needs_zext(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* To execute LD_ABS/LD_IND instructions __bpf_prog_run() may call
|
||||
* skb_copy_bits(), so provide a weak definition of it for NET-less config.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -106,12 +106,9 @@ static struct bpf_map *cpu_map_alloc(union bpf_attr *attr)
|
|||
/* make sure page count doesn't overflow */
|
||||
cost = (u64) cmap->map.max_entries * sizeof(struct bpf_cpu_map_entry *);
|
||||
cost += cpu_map_bitmap_size(attr) * num_possible_cpus();
|
||||
if (cost >= U32_MAX - PAGE_SIZE)
|
||||
goto free_cmap;
|
||||
cmap->map.pages = round_up(cost, PAGE_SIZE) >> PAGE_SHIFT;
|
||||
|
||||
/* Notice returns -EPERM on if map size is larger than memlock limit */
|
||||
ret = bpf_map_precharge_memlock(cmap->map.pages);
|
||||
ret = bpf_map_charge_init(&cmap->map.memory, cost);
|
||||
if (ret) {
|
||||
err = ret;
|
||||
goto free_cmap;
|
||||
|
|
@ -121,7 +118,7 @@ static struct bpf_map *cpu_map_alloc(union bpf_attr *attr)
|
|||
cmap->flush_needed = __alloc_percpu(cpu_map_bitmap_size(attr),
|
||||
__alignof__(unsigned long));
|
||||
if (!cmap->flush_needed)
|
||||
goto free_cmap;
|
||||
goto free_charge;
|
||||
|
||||
/* Alloc array for possible remote "destination" CPUs */
|
||||
cmap->cpu_map = bpf_map_area_alloc(cmap->map.max_entries *
|
||||
|
|
@ -133,6 +130,8 @@ static struct bpf_map *cpu_map_alloc(union bpf_attr *attr)
|
|||
return &cmap->map;
|
||||
free_percpu:
|
||||
free_percpu(cmap->flush_needed);
|
||||
free_charge:
|
||||
bpf_map_charge_finish(&cmap->map.memory);
|
||||
free_cmap:
|
||||
kfree(cmap);
|
||||
return ERR_PTR(err);
|
||||
|
|
|
|||
|
|
@ -108,13 +108,9 @@ static struct bpf_map *dev_map_alloc(union bpf_attr *attr)
|
|||
/* make sure page count doesn't overflow */
|
||||
cost = (u64) dtab->map.max_entries * sizeof(struct bpf_dtab_netdev *);
|
||||
cost += dev_map_bitmap_size(attr) * num_possible_cpus();
|
||||
if (cost >= U32_MAX - PAGE_SIZE)
|
||||
goto free_dtab;
|
||||
|
||||
dtab->map.pages = round_up(cost, PAGE_SIZE) >> PAGE_SHIFT;
|
||||
|
||||
/* if map size is larger than memlock limit, reject it early */
|
||||
err = bpf_map_precharge_memlock(dtab->map.pages);
|
||||
/* if map size is larger than memlock limit, reject it */
|
||||
err = bpf_map_charge_init(&dtab->map.memory, cost);
|
||||
if (err)
|
||||
goto free_dtab;
|
||||
|
||||
|
|
@ -125,19 +121,21 @@ static struct bpf_map *dev_map_alloc(union bpf_attr *attr)
|
|||
__alignof__(unsigned long),
|
||||
GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!dtab->flush_needed)
|
||||
goto free_dtab;
|
||||
goto free_charge;
|
||||
|
||||
dtab->netdev_map = bpf_map_area_alloc(dtab->map.max_entries *
|
||||
sizeof(struct bpf_dtab_netdev *),
|
||||
dtab->map.numa_node);
|
||||
if (!dtab->netdev_map)
|
||||
goto free_dtab;
|
||||
goto free_charge;
|
||||
|
||||
spin_lock(&dev_map_lock);
|
||||
list_add_tail_rcu(&dtab->list, &dev_map_list);
|
||||
spin_unlock(&dev_map_lock);
|
||||
|
||||
return &dtab->map;
|
||||
free_charge:
|
||||
bpf_map_charge_finish(&dtab->map.memory);
|
||||
free_dtab:
|
||||
free_percpu(dtab->flush_needed);
|
||||
kfree(dtab);
|
||||
|
|
|
|||
|
|
@ -360,14 +360,8 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr)
|
|||
else
|
||||
cost += (u64) htab->elem_size * num_possible_cpus();
|
||||
|
||||
if (cost >= U32_MAX - PAGE_SIZE)
|
||||
/* make sure page count doesn't overflow */
|
||||
goto free_htab;
|
||||
|
||||
htab->map.pages = round_up(cost, PAGE_SIZE) >> PAGE_SHIFT;
|
||||
|
||||
/* if map size is larger than memlock limit, reject it early */
|
||||
err = bpf_map_precharge_memlock(htab->map.pages);
|
||||
/* if map size is larger than memlock limit, reject it */
|
||||
err = bpf_map_charge_init(&htab->map.memory, cost);
|
||||
if (err)
|
||||
goto free_htab;
|
||||
|
||||
|
|
@ -376,7 +370,7 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr)
|
|||
sizeof(struct bucket),
|
||||
htab->map.numa_node);
|
||||
if (!htab->buckets)
|
||||
goto free_htab;
|
||||
goto free_charge;
|
||||
|
||||
if (htab->map.map_flags & BPF_F_ZERO_SEED)
|
||||
htab->hashrnd = 0;
|
||||
|
|
@ -409,6 +403,8 @@ free_prealloc:
|
|||
prealloc_destroy(htab);
|
||||
free_buckets:
|
||||
bpf_map_area_free(htab->buckets);
|
||||
free_charge:
|
||||
bpf_map_charge_finish(&htab->map.memory);
|
||||
free_htab:
|
||||
kfree(htab);
|
||||
return ERR_PTR(err);
|
||||
|
|
|
|||
|
|
@ -272,6 +272,8 @@ static struct bpf_map *cgroup_storage_map_alloc(union bpf_attr *attr)
|
|||
{
|
||||
int numa_node = bpf_map_attr_numa_node(attr);
|
||||
struct bpf_cgroup_storage_map *map;
|
||||
struct bpf_map_memory mem;
|
||||
int ret;
|
||||
|
||||
if (attr->key_size != sizeof(struct bpf_cgroup_storage_key))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
|
@ -290,13 +292,18 @@ static struct bpf_map *cgroup_storage_map_alloc(union bpf_attr *attr)
|
|||
/* max_entries is not used and enforced to be 0 */
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
ret = bpf_map_charge_init(&mem, sizeof(struct bpf_cgroup_storage_map));
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
map = kmalloc_node(sizeof(struct bpf_cgroup_storage_map),
|
||||
__GFP_ZERO | GFP_USER, numa_node);
|
||||
if (!map)
|
||||
if (!map) {
|
||||
bpf_map_charge_finish(&mem);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
map->map.pages = round_up(sizeof(struct bpf_cgroup_storage_map),
|
||||
PAGE_SIZE) >> PAGE_SHIFT;
|
||||
bpf_map_charge_move(&map->map.memory, &mem);
|
||||
|
||||
/* copy mandatory map attributes */
|
||||
bpf_map_init_from_attr(&map->map, attr);
|
||||
|
|
|
|||
|
|
@ -573,14 +573,8 @@ static struct bpf_map *trie_alloc(union bpf_attr *attr)
|
|||
cost_per_node = sizeof(struct lpm_trie_node) +
|
||||
attr->value_size + trie->data_size;
|
||||
cost += (u64) attr->max_entries * cost_per_node;
|
||||
if (cost >= U32_MAX - PAGE_SIZE) {
|
||||
ret = -E2BIG;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
trie->map.pages = round_up(cost, PAGE_SIZE) >> PAGE_SHIFT;
|
||||
|
||||
ret = bpf_map_precharge_memlock(trie->map.pages);
|
||||
ret = bpf_map_charge_init(&trie->map.memory, cost);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
|
|
|
|||
|
|
@ -67,29 +67,28 @@ static int queue_stack_map_alloc_check(union bpf_attr *attr)
|
|||
static struct bpf_map *queue_stack_map_alloc(union bpf_attr *attr)
|
||||
{
|
||||
int ret, numa_node = bpf_map_attr_numa_node(attr);
|
||||
struct bpf_map_memory mem = {0};
|
||||
struct bpf_queue_stack *qs;
|
||||
u64 size, queue_size, cost;
|
||||
|
||||
size = (u64) attr->max_entries + 1;
|
||||
cost = queue_size = sizeof(*qs) + size * attr->value_size;
|
||||
if (cost >= U32_MAX - PAGE_SIZE)
|
||||
return ERR_PTR(-E2BIG);
|
||||
|
||||
cost = round_up(cost, PAGE_SIZE) >> PAGE_SHIFT;
|
||||
|
||||
ret = bpf_map_precharge_memlock(cost);
|
||||
ret = bpf_map_charge_init(&mem, cost);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
qs = bpf_map_area_alloc(queue_size, numa_node);
|
||||
if (!qs)
|
||||
if (!qs) {
|
||||
bpf_map_charge_finish(&mem);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
memset(qs, 0, sizeof(*qs));
|
||||
|
||||
bpf_map_init_from_attr(&qs->map, attr);
|
||||
|
||||
qs->map.pages = cost;
|
||||
bpf_map_charge_move(&qs->map.memory, &mem);
|
||||
qs->size = size;
|
||||
|
||||
raw_spin_lock_init(&qs->lock);
|
||||
|
|
|
|||
|
|
@ -151,7 +151,8 @@ static struct bpf_map *reuseport_array_alloc(union bpf_attr *attr)
|
|||
{
|
||||
int err, numa_node = bpf_map_attr_numa_node(attr);
|
||||
struct reuseport_array *array;
|
||||
u64 cost, array_size;
|
||||
struct bpf_map_memory mem;
|
||||
u64 array_size;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return ERR_PTR(-EPERM);
|
||||
|
|
@ -159,24 +160,20 @@ static struct bpf_map *reuseport_array_alloc(union bpf_attr *attr)
|
|||
array_size = sizeof(*array);
|
||||
array_size += (u64)attr->max_entries * sizeof(struct sock *);
|
||||
|
||||
/* make sure there is no u32 overflow later in round_up() */
|
||||
cost = array_size;
|
||||
if (cost >= U32_MAX - PAGE_SIZE)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
cost = round_up(cost, PAGE_SIZE) >> PAGE_SHIFT;
|
||||
|
||||
err = bpf_map_precharge_memlock(cost);
|
||||
err = bpf_map_charge_init(&mem, array_size);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
/* allocate all map elements and zero-initialize them */
|
||||
array = bpf_map_area_alloc(array_size, numa_node);
|
||||
if (!array)
|
||||
if (!array) {
|
||||
bpf_map_charge_finish(&mem);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
/* copy mandatory map attributes */
|
||||
bpf_map_init_from_attr(&array->map, attr);
|
||||
array->map.pages = cost;
|
||||
bpf_map_charge_move(&array->map.memory, &mem);
|
||||
|
||||
return &array->map;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ static struct bpf_map *stack_map_alloc(union bpf_attr *attr)
|
|||
{
|
||||
u32 value_size = attr->value_size;
|
||||
struct bpf_stack_map *smap;
|
||||
struct bpf_map_memory mem;
|
||||
u64 cost, n_buckets;
|
||||
int err;
|
||||
|
||||
|
|
@ -116,40 +117,37 @@ static struct bpf_map *stack_map_alloc(union bpf_attr *attr)
|
|||
n_buckets = roundup_pow_of_two(attr->max_entries);
|
||||
|
||||
cost = n_buckets * sizeof(struct stack_map_bucket *) + sizeof(*smap);
|
||||
if (cost >= U32_MAX - PAGE_SIZE)
|
||||
return ERR_PTR(-E2BIG);
|
||||
cost += n_buckets * (value_size + sizeof(struct stack_map_bucket));
|
||||
err = bpf_map_charge_init(&mem, cost);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
smap = bpf_map_area_alloc(cost, bpf_map_attr_numa_node(attr));
|
||||
if (!smap)
|
||||
if (!smap) {
|
||||
bpf_map_charge_finish(&mem);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
err = -E2BIG;
|
||||
cost += n_buckets * (value_size + sizeof(struct stack_map_bucket));
|
||||
if (cost >= U32_MAX - PAGE_SIZE)
|
||||
goto free_smap;
|
||||
}
|
||||
|
||||
bpf_map_init_from_attr(&smap->map, attr);
|
||||
smap->map.value_size = value_size;
|
||||
smap->n_buckets = n_buckets;
|
||||
smap->map.pages = round_up(cost, PAGE_SIZE) >> PAGE_SHIFT;
|
||||
|
||||
err = bpf_map_precharge_memlock(smap->map.pages);
|
||||
if (err)
|
||||
goto free_smap;
|
||||
|
||||
err = get_callchain_buffers(sysctl_perf_event_max_stack);
|
||||
if (err)
|
||||
goto free_smap;
|
||||
goto free_charge;
|
||||
|
||||
err = prealloc_elems_and_freelist(smap);
|
||||
if (err)
|
||||
goto put_buffers;
|
||||
|
||||
bpf_map_charge_move(&smap->map.memory, &mem);
|
||||
|
||||
return &smap->map;
|
||||
|
||||
put_buffers:
|
||||
put_callchain_buffers();
|
||||
free_smap:
|
||||
free_charge:
|
||||
bpf_map_charge_finish(&mem);
|
||||
bpf_map_area_free(smap);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -188,19 +188,6 @@ void bpf_map_init_from_attr(struct bpf_map *map, union bpf_attr *attr)
|
|||
map->numa_node = bpf_map_attr_numa_node(attr);
|
||||
}
|
||||
|
||||
int bpf_map_precharge_memlock(u32 pages)
|
||||
{
|
||||
struct user_struct *user = get_current_user();
|
||||
unsigned long memlock_limit, cur;
|
||||
|
||||
memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
|
||||
cur = atomic_long_read(&user->locked_vm);
|
||||
free_uid(user);
|
||||
if (cur + pages > memlock_limit)
|
||||
return -EPERM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bpf_charge_memlock(struct user_struct *user, u32 pages)
|
||||
{
|
||||
unsigned long memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
|
||||
|
|
@ -214,45 +201,62 @@ static int bpf_charge_memlock(struct user_struct *user, u32 pages)
|
|||
|
||||
static void bpf_uncharge_memlock(struct user_struct *user, u32 pages)
|
||||
{
|
||||
atomic_long_sub(pages, &user->locked_vm);
|
||||
if (user)
|
||||
atomic_long_sub(pages, &user->locked_vm);
|
||||
}
|
||||
|
||||
static int bpf_map_init_memlock(struct bpf_map *map)
|
||||
int bpf_map_charge_init(struct bpf_map_memory *mem, size_t size)
|
||||
{
|
||||
struct user_struct *user = get_current_user();
|
||||
u32 pages = round_up(size, PAGE_SIZE) >> PAGE_SHIFT;
|
||||
struct user_struct *user;
|
||||
int ret;
|
||||
|
||||
ret = bpf_charge_memlock(user, map->pages);
|
||||
if (size >= U32_MAX - PAGE_SIZE)
|
||||
return -E2BIG;
|
||||
|
||||
user = get_current_user();
|
||||
ret = bpf_charge_memlock(user, pages);
|
||||
if (ret) {
|
||||
free_uid(user);
|
||||
return ret;
|
||||
}
|
||||
map->user = user;
|
||||
return ret;
|
||||
|
||||
mem->pages = pages;
|
||||
mem->user = user;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bpf_map_release_memlock(struct bpf_map *map)
|
||||
void bpf_map_charge_finish(struct bpf_map_memory *mem)
|
||||
{
|
||||
struct user_struct *user = map->user;
|
||||
bpf_uncharge_memlock(user, map->pages);
|
||||
free_uid(user);
|
||||
bpf_uncharge_memlock(mem->user, mem->pages);
|
||||
free_uid(mem->user);
|
||||
}
|
||||
|
||||
void bpf_map_charge_move(struct bpf_map_memory *dst,
|
||||
struct bpf_map_memory *src)
|
||||
{
|
||||
*dst = *src;
|
||||
|
||||
/* Make sure src will not be used for the redundant uncharging. */
|
||||
memset(src, 0, sizeof(struct bpf_map_memory));
|
||||
}
|
||||
|
||||
int bpf_map_charge_memlock(struct bpf_map *map, u32 pages)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = bpf_charge_memlock(map->user, pages);
|
||||
ret = bpf_charge_memlock(map->memory.user, pages);
|
||||
if (ret)
|
||||
return ret;
|
||||
map->pages += pages;
|
||||
map->memory.pages += pages;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bpf_map_uncharge_memlock(struct bpf_map *map, u32 pages)
|
||||
{
|
||||
bpf_uncharge_memlock(map->user, pages);
|
||||
map->pages -= pages;
|
||||
bpf_uncharge_memlock(map->memory.user, pages);
|
||||
map->memory.pages -= pages;
|
||||
}
|
||||
|
||||
static int bpf_map_alloc_id(struct bpf_map *map)
|
||||
|
|
@ -303,11 +307,13 @@ void bpf_map_free_id(struct bpf_map *map, bool do_idr_lock)
|
|||
static void bpf_map_free_deferred(struct work_struct *work)
|
||||
{
|
||||
struct bpf_map *map = container_of(work, struct bpf_map, work);
|
||||
struct bpf_map_memory mem;
|
||||
|
||||
bpf_map_release_memlock(map);
|
||||
bpf_map_charge_move(&mem, &map->memory);
|
||||
security_bpf_map_free(map);
|
||||
/* implementation dependent freeing */
|
||||
map->ops->map_free(map);
|
||||
bpf_map_charge_finish(&mem);
|
||||
}
|
||||
|
||||
static void bpf_map_put_uref(struct bpf_map *map)
|
||||
|
|
@ -395,7 +401,7 @@ static void bpf_map_show_fdinfo(struct seq_file *m, struct file *filp)
|
|||
map->value_size,
|
||||
map->max_entries,
|
||||
map->map_flags,
|
||||
map->pages * 1ULL << PAGE_SHIFT,
|
||||
map->memory.pages * 1ULL << PAGE_SHIFT,
|
||||
map->id,
|
||||
READ_ONCE(map->frozen));
|
||||
|
||||
|
|
@ -549,6 +555,7 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf,
|
|||
static int map_create(union bpf_attr *attr)
|
||||
{
|
||||
int numa_node = bpf_map_attr_numa_node(attr);
|
||||
struct bpf_map_memory mem;
|
||||
struct bpf_map *map;
|
||||
int f_flags;
|
||||
int err;
|
||||
|
|
@ -573,7 +580,7 @@ static int map_create(union bpf_attr *attr)
|
|||
|
||||
err = bpf_obj_name_cpy(map->name, attr->map_name);
|
||||
if (err)
|
||||
goto free_map_nouncharge;
|
||||
goto free_map;
|
||||
|
||||
atomic_set(&map->refcnt, 1);
|
||||
atomic_set(&map->usercnt, 1);
|
||||
|
|
@ -583,20 +590,20 @@ static int map_create(union bpf_attr *attr)
|
|||
|
||||
if (!attr->btf_value_type_id) {
|
||||
err = -EINVAL;
|
||||
goto free_map_nouncharge;
|
||||
goto free_map;
|
||||
}
|
||||
|
||||
btf = btf_get_by_fd(attr->btf_fd);
|
||||
if (IS_ERR(btf)) {
|
||||
err = PTR_ERR(btf);
|
||||
goto free_map_nouncharge;
|
||||
goto free_map;
|
||||
}
|
||||
|
||||
err = map_check_btf(map, btf, attr->btf_key_type_id,
|
||||
attr->btf_value_type_id);
|
||||
if (err) {
|
||||
btf_put(btf);
|
||||
goto free_map_nouncharge;
|
||||
goto free_map;
|
||||
}
|
||||
|
||||
map->btf = btf;
|
||||
|
|
@ -608,15 +615,11 @@ static int map_create(union bpf_attr *attr)
|
|||
|
||||
err = security_bpf_map_alloc(map);
|
||||
if (err)
|
||||
goto free_map_nouncharge;
|
||||
|
||||
err = bpf_map_init_memlock(map);
|
||||
if (err)
|
||||
goto free_map_sec;
|
||||
goto free_map;
|
||||
|
||||
err = bpf_map_alloc_id(map);
|
||||
if (err)
|
||||
goto free_map;
|
||||
goto free_map_sec;
|
||||
|
||||
err = bpf_map_new_fd(map, f_flags);
|
||||
if (err < 0) {
|
||||
|
|
@ -632,13 +635,13 @@ static int map_create(union bpf_attr *attr)
|
|||
|
||||
return err;
|
||||
|
||||
free_map:
|
||||
bpf_map_release_memlock(map);
|
||||
free_map_sec:
|
||||
security_bpf_map_free(map);
|
||||
free_map_nouncharge:
|
||||
free_map:
|
||||
btf_put(map->btf);
|
||||
bpf_map_charge_move(&mem, &map->memory);
|
||||
map->ops->map_free(map);
|
||||
bpf_map_charge_finish(&mem);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -1585,6 +1588,14 @@ bpf_prog_load_check_attach_type(enum bpf_prog_type prog_type,
|
|||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case BPF_PROG_TYPE_CGROUP_SKB:
|
||||
switch (expected_attach_type) {
|
||||
case BPF_CGROUP_INET_INGRESS:
|
||||
case BPF_CGROUP_INET_EGRESS:
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1604,7 +1615,9 @@ static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr)
|
|||
if (CHECK_ATTR(BPF_PROG_LOAD))
|
||||
return -EINVAL;
|
||||
|
||||
if (attr->prog_flags & ~(BPF_F_STRICT_ALIGNMENT | BPF_F_ANY_ALIGNMENT))
|
||||
if (attr->prog_flags & ~(BPF_F_STRICT_ALIGNMENT |
|
||||
BPF_F_ANY_ALIGNMENT |
|
||||
BPF_F_TEST_RND_HI32))
|
||||
return -EINVAL;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) &&
|
||||
|
|
@ -1834,6 +1847,10 @@ static int bpf_prog_attach_check_attach_type(const struct bpf_prog *prog,
|
|||
case BPF_PROG_TYPE_CGROUP_SOCK:
|
||||
case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
|
||||
return attach_type == prog->expected_attach_type ? 0 : -EINVAL;
|
||||
case BPF_PROG_TYPE_CGROUP_SKB:
|
||||
return prog->enforce_expected_attach_type &&
|
||||
prog->expected_attach_type != attach_type ?
|
||||
-EINVAL : 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ struct bpf_verifier_stack_elem {
|
|||
struct bpf_verifier_stack_elem *next;
|
||||
};
|
||||
|
||||
#define BPF_COMPLEXITY_LIMIT_STACK 1024
|
||||
#define BPF_COMPLEXITY_LIMIT_JMP_SEQ 8192
|
||||
#define BPF_COMPLEXITY_LIMIT_STATES 64
|
||||
|
||||
#define BPF_MAP_PTR_UNPRIV 1UL
|
||||
|
|
@ -782,8 +782,9 @@ static struct bpf_verifier_state *push_stack(struct bpf_verifier_env *env,
|
|||
if (err)
|
||||
goto err;
|
||||
elem->st.speculative |= speculative;
|
||||
if (env->stack_size > BPF_COMPLEXITY_LIMIT_STACK) {
|
||||
verbose(env, "BPF program is too complex\n");
|
||||
if (env->stack_size > BPF_COMPLEXITY_LIMIT_JMP_SEQ) {
|
||||
verbose(env, "The sequence of %d jumps is too complex.\n",
|
||||
env->stack_size);
|
||||
goto err;
|
||||
}
|
||||
return &elem->st;
|
||||
|
|
@ -981,6 +982,7 @@ static void mark_reg_not_init(struct bpf_verifier_env *env,
|
|||
__mark_reg_not_init(regs + regno);
|
||||
}
|
||||
|
||||
#define DEF_NOT_SUBREG (0)
|
||||
static void init_reg_state(struct bpf_verifier_env *env,
|
||||
struct bpf_func_state *state)
|
||||
{
|
||||
|
|
@ -991,6 +993,7 @@ static void init_reg_state(struct bpf_verifier_env *env,
|
|||
mark_reg_not_init(env, regs, i);
|
||||
regs[i].live = REG_LIVE_NONE;
|
||||
regs[i].parent = NULL;
|
||||
regs[i].subreg_def = DEF_NOT_SUBREG;
|
||||
}
|
||||
|
||||
/* frame pointer */
|
||||
|
|
@ -1136,7 +1139,7 @@ next:
|
|||
*/
|
||||
static int mark_reg_read(struct bpf_verifier_env *env,
|
||||
const struct bpf_reg_state *state,
|
||||
struct bpf_reg_state *parent)
|
||||
struct bpf_reg_state *parent, u8 flag)
|
||||
{
|
||||
bool writes = parent == state->parent; /* Observe write marks */
|
||||
int cnt = 0;
|
||||
|
|
@ -1151,17 +1154,26 @@ static int mark_reg_read(struct bpf_verifier_env *env,
|
|||
parent->var_off.value, parent->off);
|
||||
return -EFAULT;
|
||||
}
|
||||
if (parent->live & REG_LIVE_READ)
|
||||
/* The first condition is more likely to be true than the
|
||||
* second, checked it first.
|
||||
*/
|
||||
if ((parent->live & REG_LIVE_READ) == flag ||
|
||||
parent->live & REG_LIVE_READ64)
|
||||
/* The parentage chain never changes and
|
||||
* this parent was already marked as LIVE_READ.
|
||||
* There is no need to keep walking the chain again and
|
||||
* keep re-marking all parents as LIVE_READ.
|
||||
* This case happens when the same register is read
|
||||
* multiple times without writes into it in-between.
|
||||
* Also, if parent has the stronger REG_LIVE_READ64 set,
|
||||
* then no need to set the weak REG_LIVE_READ32.
|
||||
*/
|
||||
break;
|
||||
/* ... then we depend on parent's value */
|
||||
parent->live |= REG_LIVE_READ;
|
||||
parent->live |= flag;
|
||||
/* REG_LIVE_READ64 overrides REG_LIVE_READ32. */
|
||||
if (flag == REG_LIVE_READ64)
|
||||
parent->live &= ~REG_LIVE_READ32;
|
||||
state = parent;
|
||||
parent = state->parent;
|
||||
writes = true;
|
||||
|
|
@ -1173,12 +1185,129 @@ static int mark_reg_read(struct bpf_verifier_env *env,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* This function is supposed to be used by the following 32-bit optimization
|
||||
* code only. It returns TRUE if the source or destination register operates
|
||||
* on 64-bit, otherwise return FALSE.
|
||||
*/
|
||||
static bool is_reg64(struct bpf_verifier_env *env, struct bpf_insn *insn,
|
||||
u32 regno, struct bpf_reg_state *reg, enum reg_arg_type t)
|
||||
{
|
||||
u8 code, class, op;
|
||||
|
||||
code = insn->code;
|
||||
class = BPF_CLASS(code);
|
||||
op = BPF_OP(code);
|
||||
if (class == BPF_JMP) {
|
||||
/* BPF_EXIT for "main" will reach here. Return TRUE
|
||||
* conservatively.
|
||||
*/
|
||||
if (op == BPF_EXIT)
|
||||
return true;
|
||||
if (op == BPF_CALL) {
|
||||
/* BPF to BPF call will reach here because of marking
|
||||
* caller saved clobber with DST_OP_NO_MARK for which we
|
||||
* don't care the register def because they are anyway
|
||||
* marked as NOT_INIT already.
|
||||
*/
|
||||
if (insn->src_reg == BPF_PSEUDO_CALL)
|
||||
return false;
|
||||
/* Helper call will reach here because of arg type
|
||||
* check, conservatively return TRUE.
|
||||
*/
|
||||
if (t == SRC_OP)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (class == BPF_ALU64 || class == BPF_JMP ||
|
||||
/* BPF_END always use BPF_ALU class. */
|
||||
(class == BPF_ALU && op == BPF_END && insn->imm == 64))
|
||||
return true;
|
||||
|
||||
if (class == BPF_ALU || class == BPF_JMP32)
|
||||
return false;
|
||||
|
||||
if (class == BPF_LDX) {
|
||||
if (t != SRC_OP)
|
||||
return BPF_SIZE(code) == BPF_DW;
|
||||
/* LDX source must be ptr. */
|
||||
return true;
|
||||
}
|
||||
|
||||
if (class == BPF_STX) {
|
||||
if (reg->type != SCALAR_VALUE)
|
||||
return true;
|
||||
return BPF_SIZE(code) == BPF_DW;
|
||||
}
|
||||
|
||||
if (class == BPF_LD) {
|
||||
u8 mode = BPF_MODE(code);
|
||||
|
||||
/* LD_IMM64 */
|
||||
if (mode == BPF_IMM)
|
||||
return true;
|
||||
|
||||
/* Both LD_IND and LD_ABS return 32-bit data. */
|
||||
if (t != SRC_OP)
|
||||
return false;
|
||||
|
||||
/* Implicit ctx ptr. */
|
||||
if (regno == BPF_REG_6)
|
||||
return true;
|
||||
|
||||
/* Explicit source could be any width. */
|
||||
return true;
|
||||
}
|
||||
|
||||
if (class == BPF_ST)
|
||||
/* The only source register for BPF_ST is a ptr. */
|
||||
return true;
|
||||
|
||||
/* Conservatively return true at default. */
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Return TRUE if INSN doesn't have explicit value define. */
|
||||
static bool insn_no_def(struct bpf_insn *insn)
|
||||
{
|
||||
u8 class = BPF_CLASS(insn->code);
|
||||
|
||||
return (class == BPF_JMP || class == BPF_JMP32 ||
|
||||
class == BPF_STX || class == BPF_ST);
|
||||
}
|
||||
|
||||
/* Return TRUE if INSN has defined any 32-bit value explicitly. */
|
||||
static bool insn_has_def32(struct bpf_verifier_env *env, struct bpf_insn *insn)
|
||||
{
|
||||
if (insn_no_def(insn))
|
||||
return false;
|
||||
|
||||
return !is_reg64(env, insn, insn->dst_reg, NULL, DST_OP);
|
||||
}
|
||||
|
||||
static void mark_insn_zext(struct bpf_verifier_env *env,
|
||||
struct bpf_reg_state *reg)
|
||||
{
|
||||
s32 def_idx = reg->subreg_def;
|
||||
|
||||
if (def_idx == DEF_NOT_SUBREG)
|
||||
return;
|
||||
|
||||
env->insn_aux_data[def_idx - 1].zext_dst = true;
|
||||
/* The dst will be zero extended, so won't be sub-register anymore. */
|
||||
reg->subreg_def = DEF_NOT_SUBREG;
|
||||
}
|
||||
|
||||
static int check_reg_arg(struct bpf_verifier_env *env, u32 regno,
|
||||
enum reg_arg_type t)
|
||||
{
|
||||
struct bpf_verifier_state *vstate = env->cur_state;
|
||||
struct bpf_func_state *state = vstate->frame[vstate->curframe];
|
||||
struct bpf_insn *insn = env->prog->insnsi + env->insn_idx;
|
||||
struct bpf_reg_state *reg, *regs = state->regs;
|
||||
bool rw64;
|
||||
|
||||
if (regno >= MAX_BPF_REG) {
|
||||
verbose(env, "R%d is invalid\n", regno);
|
||||
|
|
@ -1186,6 +1315,7 @@ static int check_reg_arg(struct bpf_verifier_env *env, u32 regno,
|
|||
}
|
||||
|
||||
reg = ®s[regno];
|
||||
rw64 = is_reg64(env, insn, regno, reg, t);
|
||||
if (t == SRC_OP) {
|
||||
/* check whether register used as source operand can be read */
|
||||
if (reg->type == NOT_INIT) {
|
||||
|
|
@ -1196,7 +1326,11 @@ static int check_reg_arg(struct bpf_verifier_env *env, u32 regno,
|
|||
if (regno == BPF_REG_FP)
|
||||
return 0;
|
||||
|
||||
return mark_reg_read(env, reg, reg->parent);
|
||||
if (rw64)
|
||||
mark_insn_zext(env, reg);
|
||||
|
||||
return mark_reg_read(env, reg, reg->parent,
|
||||
rw64 ? REG_LIVE_READ64 : REG_LIVE_READ32);
|
||||
} else {
|
||||
/* check whether register used as dest operand can be written to */
|
||||
if (regno == BPF_REG_FP) {
|
||||
|
|
@ -1204,6 +1338,7 @@ static int check_reg_arg(struct bpf_verifier_env *env, u32 regno,
|
|||
return -EACCES;
|
||||
}
|
||||
reg->live |= REG_LIVE_WRITTEN;
|
||||
reg->subreg_def = rw64 ? DEF_NOT_SUBREG : env->insn_idx + 1;
|
||||
if (t == DST_OP)
|
||||
mark_reg_unknown(env, regs, regno);
|
||||
}
|
||||
|
|
@ -1383,7 +1518,8 @@ static int check_stack_read(struct bpf_verifier_env *env,
|
|||
state->regs[value_regno].live |= REG_LIVE_WRITTEN;
|
||||
}
|
||||
mark_reg_read(env, ®_state->stack[spi].spilled_ptr,
|
||||
reg_state->stack[spi].spilled_ptr.parent);
|
||||
reg_state->stack[spi].spilled_ptr.parent,
|
||||
REG_LIVE_READ64);
|
||||
return 0;
|
||||
} else {
|
||||
int zeros = 0;
|
||||
|
|
@ -1400,7 +1536,8 @@ static int check_stack_read(struct bpf_verifier_env *env,
|
|||
return -EACCES;
|
||||
}
|
||||
mark_reg_read(env, ®_state->stack[spi].spilled_ptr,
|
||||
reg_state->stack[spi].spilled_ptr.parent);
|
||||
reg_state->stack[spi].spilled_ptr.parent,
|
||||
REG_LIVE_READ64);
|
||||
if (value_regno >= 0) {
|
||||
if (zeros == size) {
|
||||
/* any size read into register is zero extended,
|
||||
|
|
@ -2109,6 +2246,12 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
|
|||
value_regno);
|
||||
if (reg_type_may_be_null(reg_type))
|
||||
regs[value_regno].id = ++env->id_gen;
|
||||
/* A load of ctx field could have different
|
||||
* actual load size with the one encoded in the
|
||||
* insn. When the dst is PTR, it is for sure not
|
||||
* a sub-register.
|
||||
*/
|
||||
regs[value_regno].subreg_def = DEF_NOT_SUBREG;
|
||||
}
|
||||
regs[value_regno].type = reg_type;
|
||||
}
|
||||
|
|
@ -2368,7 +2511,8 @@ mark:
|
|||
* the whole slot to be marked as 'read'
|
||||
*/
|
||||
mark_reg_read(env, &state->stack[spi].spilled_ptr,
|
||||
state->stack[spi].spilled_ptr.parent);
|
||||
state->stack[spi].spilled_ptr.parent,
|
||||
REG_LIVE_READ64);
|
||||
}
|
||||
return update_stack_depth(env, state, min_off);
|
||||
}
|
||||
|
|
@ -3332,6 +3476,9 @@ static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn
|
|||
check_reg_arg(env, caller_saved[i], DST_OP_NO_MARK);
|
||||
}
|
||||
|
||||
/* helper call returns 64-bit value. */
|
||||
regs[BPF_REG_0].subreg_def = DEF_NOT_SUBREG;
|
||||
|
||||
/* update return register (already marked as written above) */
|
||||
if (fn->ret_type == RET_INTEGER) {
|
||||
/* sets type to SCALAR_VALUE */
|
||||
|
|
@ -4263,6 +4410,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
|
|||
*/
|
||||
*dst_reg = *src_reg;
|
||||
dst_reg->live |= REG_LIVE_WRITTEN;
|
||||
dst_reg->subreg_def = DEF_NOT_SUBREG;
|
||||
} else {
|
||||
/* R1 = (u32) R2 */
|
||||
if (is_pointer_value(env, insn->src_reg)) {
|
||||
|
|
@ -4273,6 +4421,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
|
|||
} else if (src_reg->type == SCALAR_VALUE) {
|
||||
*dst_reg = *src_reg;
|
||||
dst_reg->live |= REG_LIVE_WRITTEN;
|
||||
dst_reg->subreg_def = env->insn_idx + 1;
|
||||
} else {
|
||||
mark_reg_unknown(env, regs,
|
||||
insn->dst_reg);
|
||||
|
|
@ -5352,16 +5501,23 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)
|
|||
* Already marked as written above.
|
||||
*/
|
||||
mark_reg_unknown(env, regs, BPF_REG_0);
|
||||
/* ld_abs load up to 32-bit skb data. */
|
||||
regs[BPF_REG_0].subreg_def = env->insn_idx + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_return_code(struct bpf_verifier_env *env)
|
||||
{
|
||||
struct tnum enforce_attach_type_range = tnum_unknown;
|
||||
struct bpf_reg_state *reg;
|
||||
struct tnum range = tnum_range(0, 1);
|
||||
|
||||
switch (env->prog->type) {
|
||||
case BPF_PROG_TYPE_CGROUP_SKB:
|
||||
if (env->prog->expected_attach_type == BPF_CGROUP_INET_EGRESS) {
|
||||
range = tnum_range(0, 3);
|
||||
enforce_attach_type_range = tnum_range(2, 3);
|
||||
}
|
||||
case BPF_PROG_TYPE_CGROUP_SOCK:
|
||||
case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
|
||||
case BPF_PROG_TYPE_SOCK_OPS:
|
||||
|
|
@ -5380,18 +5536,23 @@ static int check_return_code(struct bpf_verifier_env *env)
|
|||
}
|
||||
|
||||
if (!tnum_in(range, reg->var_off)) {
|
||||
char tn_buf[48];
|
||||
|
||||
verbose(env, "At program exit the register R0 ");
|
||||
if (!tnum_is_unknown(reg->var_off)) {
|
||||
char tn_buf[48];
|
||||
|
||||
tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off);
|
||||
verbose(env, "has value %s", tn_buf);
|
||||
} else {
|
||||
verbose(env, "has unknown scalar value");
|
||||
}
|
||||
verbose(env, " should have been 0 or 1\n");
|
||||
tnum_strn(tn_buf, sizeof(tn_buf), range);
|
||||
verbose(env, " should have been %s\n", tn_buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!tnum_is_unknown(enforce_attach_type_range) &&
|
||||
tnum_in(enforce_attach_type_range, reg->var_off))
|
||||
env->prog->enforce_expected_attach_type = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -5435,7 +5596,25 @@ enum {
|
|||
BRANCH = 2,
|
||||
};
|
||||
|
||||
#define STATE_LIST_MARK ((struct bpf_verifier_state_list *) -1L)
|
||||
static u32 state_htab_size(struct bpf_verifier_env *env)
|
||||
{
|
||||
return env->prog->len;
|
||||
}
|
||||
|
||||
static struct bpf_verifier_state_list **explored_state(
|
||||
struct bpf_verifier_env *env,
|
||||
int idx)
|
||||
{
|
||||
struct bpf_verifier_state *cur = env->cur_state;
|
||||
struct bpf_func_state *state = cur->frame[cur->curframe];
|
||||
|
||||
return &env->explored_states[(idx ^ state->callsite) % state_htab_size(env)];
|
||||
}
|
||||
|
||||
static void init_explored_state(struct bpf_verifier_env *env, int idx)
|
||||
{
|
||||
env->insn_aux_data[idx].prune_point = true;
|
||||
}
|
||||
|
||||
/* t, w, e - match pseudo-code above:
|
||||
* t - index of current instruction
|
||||
|
|
@ -5461,7 +5640,7 @@ static int push_insn(int t, int w, int e, struct bpf_verifier_env *env)
|
|||
|
||||
if (e == BRANCH)
|
||||
/* mark branch target for state pruning */
|
||||
env->explored_states[w] = STATE_LIST_MARK;
|
||||
init_explored_state(env, w);
|
||||
|
||||
if (insn_state[w] == 0) {
|
||||
/* tree-edge */
|
||||
|
|
@ -5529,9 +5708,9 @@ peek_stack:
|
|||
else if (ret < 0)
|
||||
goto err_free;
|
||||
if (t + 1 < insn_cnt)
|
||||
env->explored_states[t + 1] = STATE_LIST_MARK;
|
||||
init_explored_state(env, t + 1);
|
||||
if (insns[t].src_reg == BPF_PSEUDO_CALL) {
|
||||
env->explored_states[t] = STATE_LIST_MARK;
|
||||
init_explored_state(env, t);
|
||||
ret = push_insn(t, t + insns[t].imm + 1, BRANCH, env);
|
||||
if (ret == 1)
|
||||
goto peek_stack;
|
||||
|
|
@ -5554,10 +5733,10 @@ peek_stack:
|
|||
* after every call and jump
|
||||
*/
|
||||
if (t + 1 < insn_cnt)
|
||||
env->explored_states[t + 1] = STATE_LIST_MARK;
|
||||
init_explored_state(env, t + 1);
|
||||
} else {
|
||||
/* conditional jump with two edges */
|
||||
env->explored_states[t] = STATE_LIST_MARK;
|
||||
init_explored_state(env, t);
|
||||
ret = push_insn(t, t + 1, FALLTHROUGH, env);
|
||||
if (ret == 1)
|
||||
goto peek_stack;
|
||||
|
|
@ -6005,12 +6184,10 @@ static void clean_live_states(struct bpf_verifier_env *env, int insn,
|
|||
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)
|
||||
sl = *explored_state(env, insn);
|
||||
while (sl) {
|
||||
if (sl->state.insn_idx != insn ||
|
||||
sl->state.curframe != cur->curframe)
|
||||
goto next;
|
||||
for (i = 0; i <= cur->curframe; i++)
|
||||
if (sl->state.frame[i]->callsite != cur->frame[i]->callsite)
|
||||
|
|
@ -6292,20 +6469,33 @@ static bool states_equal(struct bpf_verifier_env *env,
|
|||
return true;
|
||||
}
|
||||
|
||||
/* Return 0 if no propagation happened. Return negative error code if error
|
||||
* happened. Otherwise, return the propagated bit.
|
||||
*/
|
||||
static int propagate_liveness_reg(struct bpf_verifier_env *env,
|
||||
struct bpf_reg_state *reg,
|
||||
struct bpf_reg_state *parent_reg)
|
||||
{
|
||||
u8 parent_flag = parent_reg->live & REG_LIVE_READ;
|
||||
u8 flag = reg->live & REG_LIVE_READ;
|
||||
int err;
|
||||
|
||||
if (parent_reg->live & REG_LIVE_READ || !(reg->live & REG_LIVE_READ))
|
||||
/* When comes here, read flags of PARENT_REG or REG could be any of
|
||||
* REG_LIVE_READ64, REG_LIVE_READ32, REG_LIVE_NONE. There is no need
|
||||
* of propagation if PARENT_REG has strongest REG_LIVE_READ64.
|
||||
*/
|
||||
if (parent_flag == REG_LIVE_READ64 ||
|
||||
/* Or if there is no read flag from REG. */
|
||||
!flag ||
|
||||
/* Or if the read flag from REG is the same as PARENT_REG. */
|
||||
parent_flag == flag)
|
||||
return 0;
|
||||
|
||||
err = mark_reg_read(env, reg, parent_reg);
|
||||
err = mark_reg_read(env, reg, parent_reg, flag);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
return flag;
|
||||
}
|
||||
|
||||
/* A write screens off any subsequent reads; but write marks come from the
|
||||
|
|
@ -6339,8 +6529,10 @@ static int propagate_liveness(struct bpf_verifier_env *env,
|
|||
for (i = frame < vstate->curframe ? BPF_REG_6 : 0; i < BPF_REG_FP; i++) {
|
||||
err = propagate_liveness_reg(env, &state_reg[i],
|
||||
&parent_reg[i]);
|
||||
if (err)
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (err == REG_LIVE_READ64)
|
||||
mark_insn_zext(env, &parent_reg[i]);
|
||||
}
|
||||
|
||||
/* Propagate stack slots. */
|
||||
|
|
@ -6350,11 +6542,11 @@ static int propagate_liveness(struct bpf_verifier_env *env,
|
|||
state_reg = &state->stack[i].spilled_ptr;
|
||||
err = propagate_liveness_reg(env, state_reg,
|
||||
parent_reg);
|
||||
if (err)
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
|
||||
|
|
@ -6364,18 +6556,21 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
|
|||
struct bpf_verifier_state *cur = env->cur_state, *new;
|
||||
int i, j, err, states_cnt = 0;
|
||||
|
||||
pprev = &env->explored_states[insn_idx];
|
||||
sl = *pprev;
|
||||
|
||||
if (!sl)
|
||||
if (!env->insn_aux_data[insn_idx].prune_point)
|
||||
/* this 'insn_idx' instruction wasn't marked, so we will not
|
||||
* be doing state search here
|
||||
*/
|
||||
return 0;
|
||||
|
||||
pprev = explored_state(env, insn_idx);
|
||||
sl = *pprev;
|
||||
|
||||
clean_live_states(env, insn_idx, cur);
|
||||
|
||||
while (sl != STATE_LIST_MARK) {
|
||||
while (sl) {
|
||||
states_cnt++;
|
||||
if (sl->state.insn_idx != insn_idx)
|
||||
goto next;
|
||||
if (states_equal(env, &sl->state, cur)) {
|
||||
sl->hit_cnt++;
|
||||
/* reached equivalent register/stack state,
|
||||
|
|
@ -6393,7 +6588,6 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
|
|||
return err;
|
||||
return 1;
|
||||
}
|
||||
states_cnt++;
|
||||
sl->miss_cnt++;
|
||||
/* heuristic to determine whether this state is beneficial
|
||||
* to keep checking from state equivalence point of view.
|
||||
|
|
@ -6420,6 +6614,7 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
|
|||
sl = *pprev;
|
||||
continue;
|
||||
}
|
||||
next:
|
||||
pprev = &sl->next;
|
||||
sl = *pprev;
|
||||
}
|
||||
|
|
@ -6451,8 +6646,9 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
|
|||
kfree(new_sl);
|
||||
return err;
|
||||
}
|
||||
new_sl->next = env->explored_states[insn_idx];
|
||||
env->explored_states[insn_idx] = new_sl;
|
||||
new->insn_idx = insn_idx;
|
||||
new_sl->next = *explored_state(env, insn_idx);
|
||||
*explored_state(env, insn_idx) = new_sl;
|
||||
/* connect new state to parentage chain. Current frame needs all
|
||||
* registers connected. Only r6 - r9 of the callers are alive (pushed
|
||||
* to the stack implicitly by JITs) so in callers' frames connect just
|
||||
|
|
@ -7130,14 +7326,23 @@ static void convert_pseudo_ld_imm64(struct bpf_verifier_env *env)
|
|||
* insni[off, off + cnt). Adjust corresponding insn_aux_data by copying
|
||||
* [0, off) and [off, end) to new locations, so the patched range stays zero
|
||||
*/
|
||||
static int adjust_insn_aux_data(struct bpf_verifier_env *env, u32 prog_len,
|
||||
u32 off, u32 cnt)
|
||||
static int adjust_insn_aux_data(struct bpf_verifier_env *env,
|
||||
struct bpf_prog *new_prog, u32 off, u32 cnt)
|
||||
{
|
||||
struct bpf_insn_aux_data *new_data, *old_data = env->insn_aux_data;
|
||||
struct bpf_insn *insn = new_prog->insnsi;
|
||||
u32 prog_len;
|
||||
int i;
|
||||
|
||||
/* aux info at OFF always needs adjustment, no matter fast path
|
||||
* (cnt == 1) is taken or not. There is no guarantee INSN at OFF is the
|
||||
* original insn at old prog.
|
||||
*/
|
||||
old_data[off].zext_dst = insn_has_def32(env, insn + off + cnt - 1);
|
||||
|
||||
if (cnt == 1)
|
||||
return 0;
|
||||
prog_len = new_prog->len;
|
||||
new_data = vzalloc(array_size(prog_len,
|
||||
sizeof(struct bpf_insn_aux_data)));
|
||||
if (!new_data)
|
||||
|
|
@ -7145,8 +7350,10 @@ static int adjust_insn_aux_data(struct bpf_verifier_env *env, u32 prog_len,
|
|||
memcpy(new_data, old_data, sizeof(struct bpf_insn_aux_data) * off);
|
||||
memcpy(new_data + off + cnt - 1, old_data + off,
|
||||
sizeof(struct bpf_insn_aux_data) * (prog_len - off - cnt + 1));
|
||||
for (i = off; i < off + cnt - 1; i++)
|
||||
for (i = off; i < off + cnt - 1; i++) {
|
||||
new_data[i].seen = true;
|
||||
new_data[i].zext_dst = insn_has_def32(env, insn + i);
|
||||
}
|
||||
env->insn_aux_data = new_data;
|
||||
vfree(old_data);
|
||||
return 0;
|
||||
|
|
@ -7179,7 +7386,7 @@ static struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 of
|
|||
env->insn_aux_data[off].orig_idx);
|
||||
return NULL;
|
||||
}
|
||||
if (adjust_insn_aux_data(env, new_prog->len, off, len))
|
||||
if (adjust_insn_aux_data(env, new_prog, off, len))
|
||||
return NULL;
|
||||
adjust_subprog_starts(env, off, len);
|
||||
return new_prog;
|
||||
|
|
@ -7443,6 +7650,84 @@ static int opt_remove_nops(struct bpf_verifier_env *env)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,
|
||||
const union bpf_attr *attr)
|
||||
{
|
||||
struct bpf_insn *patch, zext_patch[2], rnd_hi32_patch[4];
|
||||
struct bpf_insn_aux_data *aux = env->insn_aux_data;
|
||||
int i, patch_len, delta = 0, len = env->prog->len;
|
||||
struct bpf_insn *insns = env->prog->insnsi;
|
||||
struct bpf_prog *new_prog;
|
||||
bool rnd_hi32;
|
||||
|
||||
rnd_hi32 = attr->prog_flags & BPF_F_TEST_RND_HI32;
|
||||
zext_patch[1] = BPF_ZEXT_REG(0);
|
||||
rnd_hi32_patch[1] = BPF_ALU64_IMM(BPF_MOV, BPF_REG_AX, 0);
|
||||
rnd_hi32_patch[2] = BPF_ALU64_IMM(BPF_LSH, BPF_REG_AX, 32);
|
||||
rnd_hi32_patch[3] = BPF_ALU64_REG(BPF_OR, 0, BPF_REG_AX);
|
||||
for (i = 0; i < len; i++) {
|
||||
int adj_idx = i + delta;
|
||||
struct bpf_insn insn;
|
||||
|
||||
insn = insns[adj_idx];
|
||||
if (!aux[adj_idx].zext_dst) {
|
||||
u8 code, class;
|
||||
u32 imm_rnd;
|
||||
|
||||
if (!rnd_hi32)
|
||||
continue;
|
||||
|
||||
code = insn.code;
|
||||
class = BPF_CLASS(code);
|
||||
if (insn_no_def(&insn))
|
||||
continue;
|
||||
|
||||
/* NOTE: arg "reg" (the fourth one) is only used for
|
||||
* BPF_STX which has been ruled out in above
|
||||
* check, it is safe to pass NULL here.
|
||||
*/
|
||||
if (is_reg64(env, &insn, insn.dst_reg, NULL, DST_OP)) {
|
||||
if (class == BPF_LD &&
|
||||
BPF_MODE(code) == BPF_IMM)
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ctx load could be transformed into wider load. */
|
||||
if (class == BPF_LDX &&
|
||||
aux[adj_idx].ptr_type == PTR_TO_CTX)
|
||||
continue;
|
||||
|
||||
imm_rnd = get_random_int();
|
||||
rnd_hi32_patch[0] = insn;
|
||||
rnd_hi32_patch[1].imm = imm_rnd;
|
||||
rnd_hi32_patch[3].dst_reg = insn.dst_reg;
|
||||
patch = rnd_hi32_patch;
|
||||
patch_len = 4;
|
||||
goto apply_patch_buffer;
|
||||
}
|
||||
|
||||
if (!bpf_jit_needs_zext())
|
||||
continue;
|
||||
|
||||
zext_patch[0] = insn;
|
||||
zext_patch[1].dst_reg = insn.dst_reg;
|
||||
zext_patch[1].src_reg = insn.dst_reg;
|
||||
patch = zext_patch;
|
||||
patch_len = 2;
|
||||
apply_patch_buffer:
|
||||
new_prog = bpf_patch_insn_data(env, adj_idx, patch, patch_len);
|
||||
if (!new_prog)
|
||||
return -ENOMEM;
|
||||
env->prog = new_prog;
|
||||
insns = new_prog->insnsi;
|
||||
aux = env->insn_aux_data;
|
||||
delta += patch_len - 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* convert load instructions that access fields of a context type into a
|
||||
* sequence of instructions that access fields of the underlying structure:
|
||||
* struct __sk_buff -> struct sk_buff
|
||||
|
|
@ -8130,16 +8415,15 @@ static void free_states(struct bpf_verifier_env *env)
|
|||
if (!env->explored_states)
|
||||
return;
|
||||
|
||||
for (i = 0; i < env->prog->len; i++) {
|
||||
for (i = 0; i < state_htab_size(env); i++) {
|
||||
sl = env->explored_states[i];
|
||||
|
||||
if (sl)
|
||||
while (sl != STATE_LIST_MARK) {
|
||||
sln = sl->next;
|
||||
free_verifier_state(&sl->state, false);
|
||||
kfree(sl);
|
||||
sl = sln;
|
||||
}
|
||||
while (sl) {
|
||||
sln = sl->next;
|
||||
free_verifier_state(&sl->state, false);
|
||||
kfree(sl);
|
||||
sl = sln;
|
||||
}
|
||||
}
|
||||
|
||||
kvfree(env->explored_states);
|
||||
|
|
@ -8239,7 +8523,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr,
|
|||
goto skip_full_check;
|
||||
}
|
||||
|
||||
env->explored_states = kvcalloc(env->prog->len,
|
||||
env->explored_states = kvcalloc(state_htab_size(env),
|
||||
sizeof(struct bpf_verifier_state_list *),
|
||||
GFP_USER);
|
||||
ret = -ENOMEM;
|
||||
|
|
@ -8294,6 +8578,15 @@ skip_full_check:
|
|||
if (ret == 0)
|
||||
ret = fixup_bpf_calls(env);
|
||||
|
||||
/* do 32-bit optimization after insn patching has done so those patched
|
||||
* insns could be handled correctly.
|
||||
*/
|
||||
if (ret == 0 && !bpf_prog_is_dev_bound(env->prog->aux)) {
|
||||
ret = opt_subreg_zext_lo32_rnd_hi32(env, attr);
|
||||
env->prog->aux->verifier_zext = bpf_jit_needs_zext() ? !ret
|
||||
: false;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
ret = fixup_call_args(env);
|
||||
|
||||
|
|
|
|||
|
|
@ -37,13 +37,9 @@ static struct bpf_map *xsk_map_alloc(union bpf_attr *attr)
|
|||
|
||||
cost = (u64)m->map.max_entries * sizeof(struct xdp_sock *);
|
||||
cost += sizeof(struct list_head) * num_possible_cpus();
|
||||
if (cost >= U32_MAX - PAGE_SIZE)
|
||||
goto free_m;
|
||||
|
||||
m->map.pages = round_up(cost, PAGE_SIZE) >> PAGE_SHIFT;
|
||||
|
||||
/* Notice returns -EPERM on if map size is larger than memlock limit */
|
||||
err = bpf_map_precharge_memlock(m->map.pages);
|
||||
err = bpf_map_charge_init(&m->map.memory, cost);
|
||||
if (err)
|
||||
goto free_m;
|
||||
|
||||
|
|
@ -51,7 +47,7 @@ static struct bpf_map *xsk_map_alloc(union bpf_attr *attr)
|
|||
|
||||
m->flush_list = alloc_percpu(struct list_head);
|
||||
if (!m->flush_list)
|
||||
goto free_m;
|
||||
goto free_charge;
|
||||
|
||||
for_each_possible_cpu(cpu)
|
||||
INIT_LIST_HEAD(per_cpu_ptr(m->flush_list, cpu));
|
||||
|
|
@ -65,6 +61,8 @@ static struct bpf_map *xsk_map_alloc(union bpf_attr *attr)
|
|||
|
||||
free_percpu:
|
||||
free_percpu(m->flush_list);
|
||||
free_charge:
|
||||
bpf_map_charge_finish(&m->map.memory);
|
||||
free_m:
|
||||
kfree(m);
|
||||
return ERR_PTR(err);
|
||||
|
|
|
|||
|
|
@ -4955,8 +4955,6 @@ static void css_release_work_fn(struct work_struct *work)
|
|||
if (cgrp->kn)
|
||||
RCU_INIT_POINTER(*(void __rcu __force **)&cgrp->kn->priv,
|
||||
NULL);
|
||||
|
||||
cgroup_bpf_put(cgrp);
|
||||
}
|
||||
|
||||
mutex_unlock(&cgroup_mutex);
|
||||
|
|
@ -5482,6 +5480,8 @@ static int cgroup_destroy_locked(struct cgroup *cgrp)
|
|||
|
||||
cgroup1_check_for_release(parent);
|
||||
|
||||
cgroup_bpf_offline(cgrp);
|
||||
|
||||
/* put the base reference */
|
||||
percpu_ref_kill(&cgrp->self.refcnt);
|
||||
|
||||
|
|
@ -6221,6 +6221,7 @@ void cgroup_sk_alloc(struct sock_cgroup_data *skcd)
|
|||
* Don't use cgroup_get_live().
|
||||
*/
|
||||
cgroup_get(sock_cgroup_ptr(skcd));
|
||||
cgroup_bpf_get(sock_cgroup_ptr(skcd));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -6232,6 +6233,7 @@ void cgroup_sk_alloc(struct sock_cgroup_data *skcd)
|
|||
cset = task_css_set(current);
|
||||
if (likely(cgroup_tryget(cset->dfl_cgrp))) {
|
||||
skcd->val = (unsigned long)cset->dfl_cgrp;
|
||||
cgroup_bpf_get(cset->dfl_cgrp);
|
||||
break;
|
||||
}
|
||||
cpu_relax();
|
||||
|
|
@ -6242,7 +6244,10 @@ void cgroup_sk_alloc(struct sock_cgroup_data *skcd)
|
|||
|
||||
void cgroup_sk_free(struct sock_cgroup_data *skcd)
|
||||
{
|
||||
cgroup_put(sock_cgroup_ptr(skcd));
|
||||
struct cgroup *cgrp = sock_cgroup_ptr(skcd);
|
||||
|
||||
cgroup_bpf_put(cgrp);
|
||||
cgroup_put(cgrp);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SOCK_CGROUP_DATA */
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@
|
|||
#include "trace_probe.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define bpf_event_rcu_dereference(p) \
|
||||
rcu_dereference_protected(p, lockdep_is_held(&bpf_event_mutex))
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
struct bpf_trace_module {
|
||||
struct module *module;
|
||||
|
|
@ -567,6 +570,69 @@ static const struct bpf_func_proto bpf_probe_read_str_proto = {
|
|||
.arg3_type = ARG_ANYTHING,
|
||||
};
|
||||
|
||||
struct send_signal_irq_work {
|
||||
struct irq_work irq_work;
|
||||
struct task_struct *task;
|
||||
u32 sig;
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct send_signal_irq_work, send_signal_work);
|
||||
|
||||
static void do_bpf_send_signal(struct irq_work *entry)
|
||||
{
|
||||
struct send_signal_irq_work *work;
|
||||
|
||||
work = container_of(entry, struct send_signal_irq_work, irq_work);
|
||||
group_send_sig_info(work->sig, SEND_SIG_PRIV, work->task, PIDTYPE_TGID);
|
||||
}
|
||||
|
||||
BPF_CALL_1(bpf_send_signal, u32, sig)
|
||||
{
|
||||
struct send_signal_irq_work *work = NULL;
|
||||
|
||||
/* Similar to bpf_probe_write_user, task needs to be
|
||||
* in a sound condition and kernel memory access be
|
||||
* permitted in order to send signal to the current
|
||||
* task.
|
||||
*/
|
||||
if (unlikely(current->flags & (PF_KTHREAD | PF_EXITING)))
|
||||
return -EPERM;
|
||||
if (unlikely(uaccess_kernel()))
|
||||
return -EPERM;
|
||||
if (unlikely(!nmi_uaccess_okay()))
|
||||
return -EPERM;
|
||||
|
||||
if (in_nmi()) {
|
||||
/* Do an early check on signal validity. Otherwise,
|
||||
* the error is lost in deferred irq_work.
|
||||
*/
|
||||
if (unlikely(!valid_signal(sig)))
|
||||
return -EINVAL;
|
||||
|
||||
work = this_cpu_ptr(&send_signal_work);
|
||||
if (work->irq_work.flags & IRQ_WORK_BUSY)
|
||||
return -EBUSY;
|
||||
|
||||
/* Add the current task, which is the target of sending signal,
|
||||
* to the irq_work. The current task may change when queued
|
||||
* irq works get executed.
|
||||
*/
|
||||
work->task = current;
|
||||
work->sig = sig;
|
||||
irq_work_queue(&work->irq_work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return group_send_sig_info(sig, SEND_SIG_PRIV, current, PIDTYPE_TGID);
|
||||
}
|
||||
|
||||
static const struct bpf_func_proto bpf_send_signal_proto = {
|
||||
.func = bpf_send_signal,
|
||||
.gpl_only = false,
|
||||
.ret_type = RET_INTEGER,
|
||||
.arg1_type = ARG_ANYTHING,
|
||||
};
|
||||
|
||||
static const struct bpf_func_proto *
|
||||
tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
||||
{
|
||||
|
|
@ -617,6 +683,8 @@ tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
|||
case BPF_FUNC_get_current_cgroup_id:
|
||||
return &bpf_get_current_cgroup_id_proto;
|
||||
#endif
|
||||
case BPF_FUNC_send_signal:
|
||||
return &bpf_send_signal_proto;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -1034,7 +1102,7 @@ static DEFINE_MUTEX(bpf_event_mutex);
|
|||
int perf_event_attach_bpf_prog(struct perf_event *event,
|
||||
struct bpf_prog *prog)
|
||||
{
|
||||
struct bpf_prog_array __rcu *old_array;
|
||||
struct bpf_prog_array *old_array;
|
||||
struct bpf_prog_array *new_array;
|
||||
int ret = -EEXIST;
|
||||
|
||||
|
|
@ -1052,7 +1120,7 @@ int perf_event_attach_bpf_prog(struct perf_event *event,
|
|||
if (event->prog)
|
||||
goto unlock;
|
||||
|
||||
old_array = event->tp_event->prog_array;
|
||||
old_array = bpf_event_rcu_dereference(event->tp_event->prog_array);
|
||||
if (old_array &&
|
||||
bpf_prog_array_length(old_array) >= BPF_TRACE_MAX_PROGS) {
|
||||
ret = -E2BIG;
|
||||
|
|
@ -1075,7 +1143,7 @@ unlock:
|
|||
|
||||
void perf_event_detach_bpf_prog(struct perf_event *event)
|
||||
{
|
||||
struct bpf_prog_array __rcu *old_array;
|
||||
struct bpf_prog_array *old_array;
|
||||
struct bpf_prog_array *new_array;
|
||||
int ret;
|
||||
|
||||
|
|
@ -1084,7 +1152,7 @@ void perf_event_detach_bpf_prog(struct perf_event *event)
|
|||
if (!event->prog)
|
||||
goto unlock;
|
||||
|
||||
old_array = event->tp_event->prog_array;
|
||||
old_array = bpf_event_rcu_dereference(event->tp_event->prog_array);
|
||||
ret = bpf_prog_array_copy(old_array, event->prog, NULL, &new_array);
|
||||
if (ret == -ENOENT)
|
||||
goto unlock;
|
||||
|
|
@ -1106,6 +1174,7 @@ int perf_event_query_prog_array(struct perf_event *event, void __user *info)
|
|||
{
|
||||
struct perf_event_query_bpf __user *uquery = info;
|
||||
struct perf_event_query_bpf query = {};
|
||||
struct bpf_prog_array *progs;
|
||||
u32 *ids, prog_cnt, ids_len;
|
||||
int ret;
|
||||
|
||||
|
|
@ -1130,10 +1199,8 @@ int perf_event_query_prog_array(struct perf_event *event, void __user *info)
|
|||
*/
|
||||
|
||||
mutex_lock(&bpf_event_mutex);
|
||||
ret = bpf_prog_array_copy_info(event->tp_event->prog_array,
|
||||
ids,
|
||||
ids_len,
|
||||
&prog_cnt);
|
||||
progs = bpf_event_rcu_dereference(event->tp_event->prog_array);
|
||||
ret = bpf_prog_array_copy_info(progs, ids, ids_len, &prog_cnt);
|
||||
mutex_unlock(&bpf_event_mutex);
|
||||
|
||||
if (copy_to_user(&uquery->prog_cnt, &prog_cnt, sizeof(prog_cnt)) ||
|
||||
|
|
@ -1343,5 +1410,18 @@ static int __init bpf_event_init(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __init send_signal_irq_work_init(void)
|
||||
{
|
||||
int cpu;
|
||||
struct send_signal_irq_work *work;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
work = per_cpu_ptr(&send_signal_work, cpu);
|
||||
init_irq_work(&work->irq_work, do_bpf_send_signal);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
fs_initcall(bpf_event_init);
|
||||
subsys_initcall(send_signal_irq_work_init);
|
||||
#endif /* CONFIG_MODULES */
|
||||
|
|
|
|||
Loading…
Reference in a new issue