summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/bpf/verifier.c109
1 files changed, 76 insertions, 33 deletions
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 1f2a846ae415..81660ffce99a 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -219,6 +219,7 @@ static int ref_set_non_owning(struct bpf_verifier_env *env,
struct bpf_reg_state *reg);
static bool is_trusted_reg(struct bpf_verifier_env *env, const struct bpf_reg_state *reg);
static inline bool in_sleepable_context(struct bpf_verifier_env *env);
+static bool in_rcu_callback(struct bpf_verifier_env *env);
static const char *non_sleepable_context_description(struct bpf_verifier_env *env);
static void scalar32_min_max_add(struct bpf_reg_state *dst_reg, struct bpf_reg_state *src_reg);
static void scalar_min_max_add(struct bpf_reg_state *dst_reg, struct bpf_reg_state *src_reg);
@@ -10379,12 +10380,71 @@ bool bpf_allow_tail_call_in_subprogs(struct bpf_verifier_env *env)
bpf_jit_supports_subprog_tailcalls();
}
+/*
+ * Is rcu_read_lock_held() true at run time? Unlike for in_rcu_cs(), disabled
+ * preemption or interrupts will not do.
+ */
+static bool has_rcu_read_lock(struct bpf_verifier_env *env)
+{
+ struct bpf_verifier_state *state = env->cur_state;
+
+ if (state->active_rcu_locks || in_rcu_callback(env))
+ return true;
+ if (in_sleepable(env))
+ return false;
+ return !env->no_rcu_read_lock || state->frame[0]->in_async_callback_fn;
+}
+
+/* May the map be used outside of an RCU read side critical section? */
+static bool map_needs_no_rcu(const struct bpf_map *map)
+{
+ switch (map->map_type) {
+ case BPF_MAP_TYPE_HASH:
+ case BPF_MAP_TYPE_RHASH:
+ case BPF_MAP_TYPE_LRU_HASH:
+ case BPF_MAP_TYPE_ARRAY:
+ case BPF_MAP_TYPE_PERCPU_HASH:
+ case BPF_MAP_TYPE_PERCPU_ARRAY:
+ case BPF_MAP_TYPE_LRU_PERCPU_HASH:
+ case BPF_MAP_TYPE_LPM_TRIE:
+ case BPF_MAP_TYPE_ARRAY_OF_MAPS:
+ case BPF_MAP_TYPE_HASH_OF_MAPS:
+ case BPF_MAP_TYPE_RINGBUF:
+ case BPF_MAP_TYPE_USER_RINGBUF:
+ case BPF_MAP_TYPE_INODE_STORAGE:
+ case BPF_MAP_TYPE_SK_STORAGE:
+ case BPF_MAP_TYPE_TASK_STORAGE:
+ case BPF_MAP_TYPE_CGRP_STORAGE:
+ case BPF_MAP_TYPE_QUEUE:
+ case BPF_MAP_TYPE_STACK:
+ case BPF_MAP_TYPE_ARENA:
+ case BPF_MAP_TYPE_INSN_ARRAY:
+ case BPF_MAP_TYPE_PROG_ARRAY:
+ case BPF_MAP_TYPE_BLOOM_FILTER:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int check_map_func_compatibility(struct bpf_verifier_env *env,
struct bpf_map *map, int func_id)
{
if (!map)
return 0;
+ /*
+ * check_map_prog_compatibility() asks the program. That misses the
+ * sleepable callback of another program, and an inner map. They may
+ * use any map under the RCU read lock. Disabled preemption or
+ * interrupts will not do: the maps ask for rcu_read_lock_held().
+ */
+ if (!has_rcu_read_lock(env) && !map_needs_no_rcu(map)) {
+ verbose(env, "map '%s' needs bpf_rcu_read_lock() in sleepable context\n",
+ map->name);
+ return -EINVAL;
+ }
+
/* We need a two way check, first is from map perspective ... */
switch (map->map_type) {
case BPF_MAP_TYPE_PROG_ARRAY:
@@ -11373,7 +11433,12 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
if (env->log.level & BPF_LOG_LEVEL)
verbose(env, "Func#%d ('%s') is global and assumed valid.\n",
subprog, sub_name);
- sub_aux->called[in_sleepable_context(env)] = true;
+ if (in_sleepable_context(env))
+ sub_aux->called[BPF_FUNC_CTX_SLEEPABLE] = true;
+ else if (has_rcu_read_lock(env))
+ sub_aux->called[BPF_FUNC_CTX_NON_SLEEPABLE] = true;
+ else
+ sub_aux->called[BPF_FUNC_CTX_NO_RCU_LOCK] = true;
returns_void = subprog_returns_void(env, subprog);
if (env->subprog_info[subprog].changes_pkt_data)
clear_all_pkt_pointers(env);
@@ -20676,35 +20741,11 @@ static int check_map_prog_compatibility(struct bpf_verifier_env *env,
return -EINVAL;
}
- if (prog->sleepable)
- switch (map->map_type) {
- case BPF_MAP_TYPE_HASH:
- case BPF_MAP_TYPE_RHASH:
- case BPF_MAP_TYPE_LRU_HASH:
- case BPF_MAP_TYPE_ARRAY:
- case BPF_MAP_TYPE_PERCPU_HASH:
- case BPF_MAP_TYPE_PERCPU_ARRAY:
- case BPF_MAP_TYPE_LRU_PERCPU_HASH:
- case BPF_MAP_TYPE_LPM_TRIE:
- case BPF_MAP_TYPE_ARRAY_OF_MAPS:
- case BPF_MAP_TYPE_HASH_OF_MAPS:
- case BPF_MAP_TYPE_RINGBUF:
- case BPF_MAP_TYPE_USER_RINGBUF:
- case BPF_MAP_TYPE_INODE_STORAGE:
- case BPF_MAP_TYPE_SK_STORAGE:
- case BPF_MAP_TYPE_TASK_STORAGE:
- case BPF_MAP_TYPE_CGRP_STORAGE:
- case BPF_MAP_TYPE_QUEUE:
- case BPF_MAP_TYPE_STACK:
- case BPF_MAP_TYPE_ARENA:
- case BPF_MAP_TYPE_INSN_ARRAY:
- case BPF_MAP_TYPE_PROG_ARRAY:
- break;
- default:
- verbose(env,
- "Sleepable programs can only use array, hash, ringbuf and local storage maps\n");
- return -EINVAL;
- }
+ if (prog->sleepable && !map_needs_no_rcu(map)) {
+ verbose(env,
+ "Sleepable programs can only use array, hash, ringbuf and local storage maps\n");
+ return -EINVAL;
+ }
if (bpf_map_is_cgroup_storage(map) &&
bpf_cgroup_storage_assign(env->prog->aux, map)) {
@@ -21426,7 +21467,8 @@ static void free_states(struct bpf_verifier_env *env)
}
}
-static int do_check_common(struct bpf_verifier_env *env, int subprog, bool is_sleepable)
+static int do_check_common(struct bpf_verifier_env *env, int subprog,
+ enum bpf_func_context context)
{
bool pop_log = !(env->log.level & BPF_LOG_LEVEL2);
struct bpf_subprog_info *sub = subprog_info(env, subprog);
@@ -21446,7 +21488,8 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog, bool is_sl
state->curframe = 0;
state->speculative = false;
state->branches = 1;
- state->in_sleepable = is_sleepable;
+ state->in_sleepable = context == BPF_FUNC_CTX_SLEEPABLE;
+ env->no_rcu_read_lock = context == BPF_FUNC_CTX_NO_RCU_LOCK;
state->frame[0] = kzalloc_obj(struct bpf_func_state, GFP_KERNEL_ACCOUNT);
if (!state->frame[0]) {
kfree(state);
@@ -21663,7 +21706,7 @@ again:
/*
* We can't loop forever as each pass verifies at least one new context,
- * and there are only two contexts per global subprog.
+ * and there are only three contexts per global subprog.
*/
if (new_cnt)
goto again;