[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20191107054644.1285697-13-ast@kernel.org>
Date: Wed, 6 Nov 2019 21:46:39 -0800
From: Alexei Starovoitov <ast@...nel.org>
To: <davem@...emloft.net>
CC: <daniel@...earbox.net>, <x86@...nel.org>, <netdev@...r.kernel.org>,
<bpf@...r.kernel.org>, <kernel-team@...com>
Subject: [PATCH v2 bpf-next 12/17] bpf: Fix race in btf_resolve_helper_id()
btf_resolve_helper_id() caching logic is racy, since under root the verifier
can verify several programs in parallel. Fix it with extra spin_lock.
Fixes: a7658e1a4164 ("bpf: Check types of arguments passed into helpers")
Signed-off-by: Alexei Starovoitov <ast@...nel.org>
---
include/linux/bpf.h | 3 ++-
kernel/bpf/btf.c | 34 +++++++++++++++++++++++++++++++++-
kernel/bpf/verifier.c | 6 +-----
3 files changed, 36 insertions(+), 7 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 9206b7e86dde..c287dfce2a17 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -874,7 +874,8 @@ int btf_struct_access(struct bpf_verifier_log *log,
const struct btf_type *t, int off, int size,
enum bpf_access_type atype,
u32 *next_btf_id);
-u32 btf_resolve_helper_id(struct bpf_verifier_log *log, void *, int);
+u32 btf_resolve_helper_id(struct bpf_verifier_log *log,
+ const struct bpf_func_proto *fn, int);
int btf_distill_func_proto(struct bpf_verifier_log *log,
struct btf *btf,
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 78d9ceaabc00..7155787a0b13 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -3584,7 +3584,8 @@ int btf_struct_access(struct bpf_verifier_log *log,
return -EINVAL;
}
-u32 btf_resolve_helper_id(struct bpf_verifier_log *log, void *fn, int arg)
+static u32 __btf_resolve_helper_id(struct bpf_verifier_log *log, void *fn,
+ int arg)
{
char fnname[KSYM_SYMBOL_LEN + 4] = "btf_";
const struct btf_param *args;
@@ -3652,6 +3653,37 @@ u32 btf_resolve_helper_id(struct bpf_verifier_log *log, void *fn, int arg)
return btf_id;
}
+static DEFINE_SPINLOCK(btf_resolve_lock);
+
+u32 btf_resolve_helper_id(struct bpf_verifier_log *log,
+ const struct bpf_func_proto *fn, int arg)
+{
+ u32 *btf_id = &fn->btf_id[arg];
+ u32 ret;
+
+ if (fn->arg_type[arg] != ARG_PTR_TO_BTF_ID)
+ return -EINVAL;
+
+ if (*btf_id)
+ /* ok to do without lock. It only set once */
+ return *btf_id;
+ /* ok to race the search. The result is the same */
+ ret = __btf_resolve_helper_id(log, fn->func, arg);
+ if (!ret) {
+ bpf_log(log, "BTF resolution bug\n");
+ return -EFAULT;
+ }
+ spin_lock(&btf_resolve_lock);
+ if (*btf_id) {
+ ret = *btf_id;
+ goto out;
+ }
+ *btf_id = ret;
+out:
+ spin_unlock(&btf_resolve_lock);
+ return ret;
+}
+
static int __get_type_size(struct btf *btf, u32 btf_id,
const struct btf_type **bad_type)
{
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 36542edd4936..c4fd11a27d81 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -4147,11 +4147,7 @@ static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn
meta.func_id = func_id;
/* check args */
for (i = 0; i < 5; i++) {
- if (fn->arg_type[i] == ARG_PTR_TO_BTF_ID) {
- if (!fn->btf_id[i])
- fn->btf_id[i] = btf_resolve_helper_id(&env->log, fn->func, i);
- meta.btf_id = fn->btf_id[i];
- }
+ meta.btf_id = btf_resolve_helper_id(&env->log, fn, i);
err = check_func_arg(env, BPF_REG_1 + i, fn->arg_type[i], &meta);
if (err)
return err;
--
2.23.0
Powered by blists - more mailing lists