mirror of
https://github.com/torvalds/linux.git
synced 2024-11-29 23:51:37 +00:00
bpf: remove __rcu annotations from bpf_prog_array
Drop __rcu annotations and rcu read sections from bpf_prog_array helper functions. They are not needed since all existing callers call those helpers from the rcu update side while holding a mutex. This guarantees that use-after-free could not happen. In the next patches I'll fix the callers with missing rcu_dereference_protected to make sparse/lockdep happy, the proper way to use these helpers is: struct bpf_prog_array __rcu *progs = ...; struct bpf_prog_array *p; mutex_lock(&mtx); p = rcu_dereference_protected(progs, lockdep_is_held(&mtx)); bpf_prog_array_length(p); bpf_prog_array_copy_to_user(p, ...); bpf_prog_array_delete_safe(p, ...); bpf_prog_array_copy_info(p, ...); bpf_prog_array_copy(p, ...); bpf_prog_array_free(p); mutex_unlock(&mtx); No functional changes! rcu_dereference_protected with lockdep_is_held should catch any cases where we update prog array without a mutex (I've looked at existing call sites and I think we hold a mutex everywhere). Motivation is to fix sparse warnings: kernel/bpf/core.c:1803:9: warning: incorrect type in argument 1 (different address spaces) kernel/bpf/core.c:1803:9: expected struct callback_head *head kernel/bpf/core.c:1803:9: got struct callback_head [noderef] <asn:4> * kernel/bpf/core.c:1877:44: warning: incorrect type in initializer (different address spaces) kernel/bpf/core.c:1877:44: expected struct bpf_prog_array_item *item kernel/bpf/core.c:1877:44: got struct bpf_prog_array_item [noderef] <asn:4> * kernel/bpf/core.c:1901:26: warning: incorrect type in assignment (different address spaces) kernel/bpf/core.c:1901:26: expected struct bpf_prog_array_item *existing kernel/bpf/core.c:1901:26: got struct bpf_prog_array_item [noderef] <asn:4> * kernel/bpf/core.c:1935:26: warning: incorrect type in assignment (different address spaces) kernel/bpf/core.c:1935:26: expected struct bpf_prog_array_item *[assigned] existing kernel/bpf/core.c:1935:26: got struct bpf_prog_array_item [noderef] <asn:4> * v2: * remove comment about potential race; that can't happen because all callers are in rcu-update section Cc: Roman Gushchin <guro@fb.com> Acked-by: Roman Gushchin <guro@fb.com> Signed-off-by: Stanislav Fomichev <sdf@google.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
This commit is contained in:
parent
fe937ea12e
commit
54e9c9d4b5
@ -514,17 +514,17 @@ struct bpf_prog_array {
|
||||
};
|
||||
|
||||
struct bpf_prog_array *bpf_prog_array_alloc(u32 prog_cnt, gfp_t flags);
|
||||
void bpf_prog_array_free(struct bpf_prog_array __rcu *progs);
|
||||
int bpf_prog_array_length(struct bpf_prog_array __rcu *progs);
|
||||
int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs,
|
||||
void bpf_prog_array_free(struct bpf_prog_array *progs);
|
||||
int bpf_prog_array_length(struct bpf_prog_array *progs);
|
||||
int bpf_prog_array_copy_to_user(struct bpf_prog_array *progs,
|
||||
__u32 __user *prog_ids, u32 cnt);
|
||||
|
||||
void bpf_prog_array_delete_safe(struct bpf_prog_array __rcu *progs,
|
||||
void bpf_prog_array_delete_safe(struct bpf_prog_array *progs,
|
||||
struct bpf_prog *old_prog);
|
||||
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);
|
||||
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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user