Commit 54e9c9d4 authored by Stanislav Fomichev's avatar Stanislav Fomichev Committed by Daniel Borkmann

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: default avatarRoman Gushchin <guro@fb.com>
Signed-off-by: default avatarStanislav Fomichev <sdf@google.com>
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
parent fe937ea1
...@@ -514,17 +514,17 @@ struct bpf_prog_array { ...@@ -514,17 +514,17 @@ struct bpf_prog_array {
}; };
struct bpf_prog_array *bpf_prog_array_alloc(u32 prog_cnt, gfp_t flags); 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); void bpf_prog_array_free(struct bpf_prog_array *progs);
int bpf_prog_array_length(struct bpf_prog_array __rcu *progs); int bpf_prog_array_length(struct bpf_prog_array *progs);
int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs, int bpf_prog_array_copy_to_user(struct bpf_prog_array *progs,
__u32 __user *prog_ids, u32 cnt); __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); 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_ids, u32 request_cnt,
u32 *prog_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 *exclude_prog,
struct bpf_prog *include_prog, struct bpf_prog *include_prog,
struct bpf_prog_array **new_array); struct bpf_prog_array **new_array);
......
...@@ -1795,38 +1795,33 @@ struct bpf_prog_array *bpf_prog_array_alloc(u32 prog_cnt, gfp_t flags) ...@@ -1795,38 +1795,33 @@ struct bpf_prog_array *bpf_prog_array_alloc(u32 prog_cnt, gfp_t flags)
return &empty_prog_array.hdr; 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 || if (!progs || progs == &empty_prog_array.hdr)
progs == (struct bpf_prog_array __rcu *)&empty_prog_array.hdr)
return; return;
kfree_rcu(progs, rcu); 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; struct bpf_prog_array_item *item;
u32 cnt = 0; u32 cnt = 0;
rcu_read_lock(); for (item = array->items; item->prog; item++)
item = rcu_dereference(array)->items;
for (; item->prog; item++)
if (item->prog != &dummy_bpf_prog.prog) if (item->prog != &dummy_bpf_prog.prog)
cnt++; cnt++;
rcu_read_unlock();
return cnt; 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 *prog_ids,
u32 request_cnt) u32 request_cnt)
{ {
struct bpf_prog_array_item *item; struct bpf_prog_array_item *item;
int i = 0; int i = 0;
item = rcu_dereference_check(array, 1)->items; for (item = array->items; item->prog; item++) {
for (; item->prog; item++) {
if (item->prog == &dummy_bpf_prog.prog) if (item->prog == &dummy_bpf_prog.prog)
continue; continue;
prog_ids[i] = item->prog->aux->id; prog_ids[i] = item->prog->aux->id;
...@@ -1839,7 +1834,7 @@ static bool bpf_prog_array_copy_core(struct bpf_prog_array __rcu *array, ...@@ -1839,7 +1834,7 @@ static bool bpf_prog_array_copy_core(struct bpf_prog_array __rcu *array,
return !!(item->prog); 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) __u32 __user *prog_ids, u32 cnt)
{ {
unsigned long err = 0; unsigned long err = 0;
...@@ -1850,18 +1845,12 @@ int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *array, ...@@ -1850,18 +1845,12 @@ int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *array,
* cnt = bpf_prog_array_length(); * cnt = bpf_prog_array_length();
* if (cnt > 0) * if (cnt > 0)
* bpf_prog_array_copy_to_user(..., cnt); * bpf_prog_array_copy_to_user(..., cnt);
* so below kcalloc doesn't need extra cnt > 0 check, but * so below kcalloc doesn't need extra cnt > 0 check.
* 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
*/ */
ids = kcalloc(cnt, sizeof(u32), GFP_USER | __GFP_NOWARN); ids = kcalloc(cnt, sizeof(u32), GFP_USER | __GFP_NOWARN);
if (!ids) if (!ids)
return -ENOMEM; return -ENOMEM;
rcu_read_lock();
nospc = bpf_prog_array_copy_core(array, ids, cnt); nospc = bpf_prog_array_copy_core(array, ids, cnt);
rcu_read_unlock();
err = copy_to_user(prog_ids, ids, cnt * sizeof(u32)); err = copy_to_user(prog_ids, ids, cnt * sizeof(u32));
kfree(ids); kfree(ids);
if (err) if (err)
...@@ -1871,19 +1860,19 @@ int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *array, ...@@ -1871,19 +1860,19 @@ int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *array,
return 0; 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 *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) { if (item->prog == old_prog) {
WRITE_ONCE(item->prog, &dummy_bpf_prog.prog); WRITE_ONCE(item->prog, &dummy_bpf_prog.prog);
break; 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 *exclude_prog,
struct bpf_prog *include_prog, struct bpf_prog *include_prog,
struct bpf_prog_array **new_array) struct bpf_prog_array **new_array)
...@@ -1947,7 +1936,7 @@ int bpf_prog_array_copy(struct bpf_prog_array __rcu *old_array, ...@@ -1947,7 +1936,7 @@ int bpf_prog_array_copy(struct bpf_prog_array __rcu *old_array,
return 0; 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_ids, u32 request_cnt,
u32 *prog_cnt) u32 *prog_cnt)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment