* [PATCH bpf 0/2] bpf: Fix out-of-bound issue when jit-ing bpf_pseudo_func
@ 2021-11-06 1:40 Martin KaFai Lau
2021-11-06 1:40 ` [PATCH bpf 1/2] bpf: Stop caching subprog index in the bpf_pseudo_func insn Martin KaFai Lau
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Martin KaFai Lau @ 2021-11-06 1:40 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team,
Yonghong Song
This set fixes an out-of-bound access issue when jit-ing the
bpf_pseudo_func insn (i.e. ld_imm64 with src_reg == BPF_PSEUDO_FUNC)
Martin KaFai Lau (2):
bpf: Stop caching subprog index in the bpf_pseudo_func insn
bpf: selftest: Trigger a DCE on the whole subprog
include/linux/bpf.h | 6 +++
kernel/bpf/core.c | 7 ++++
kernel/bpf/verifier.c | 37 +++++++------------
.../bpf/progs/for_each_array_map_elem.c | 12 ++++++
4 files changed, 39 insertions(+), 23 deletions(-)
--
2.30.2
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH bpf 1/2] bpf: Stop caching subprog index in the bpf_pseudo_func insn
2021-11-06 1:40 [PATCH bpf 0/2] bpf: Fix out-of-bound issue when jit-ing bpf_pseudo_func Martin KaFai Lau
@ 2021-11-06 1:40 ` Martin KaFai Lau
2021-11-06 1:40 ` [PATCH bpf 2/2] bpf: selftest: Trigger a DCE on the whole subprog Martin KaFai Lau
2021-11-06 20:25 ` [PATCH bpf 0/2] bpf: Fix out-of-bound issue when jit-ing bpf_pseudo_func Alexei Starovoitov
2 siblings, 0 replies; 4+ messages in thread
From: Martin KaFai Lau @ 2021-11-06 1:40 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team,
Yonghong Song
This patch is to fix an out-of-bound access issue when jit-ing the
bpf_pseudo_func insn (i.e. ld_imm64 with src_reg == BPF_PSEUDO_FUNC)
In jit_subprog(), it currently reuses the subprog index cached in
insn[1].imm. This subprog index is an index into a few array related
to subprogs. For example, in jit_subprog(), it is an index to the newly
allocated 'struct bpf_prog **func' array.
The subprog index was cached in insn[1].imm after add_subprog(). However,
this could become outdated (and too big in this case) if some subprogs
are completely removed during dead code elimination (in
adjust_subprog_starts_after_remove). The cached index in insn[1].imm
is not updated accordingly and causing out-of-bound issue in the later
jit_subprog().
Unlike bpf_pseudo_'func' insn, the current bpf_pseudo_'call' insn
is handling the DCE properly by calling find_subprog(insn->imm) to
figure out the index instead of caching the subprog index.
The existing bpf_adj_branches() will adjust the insn->imm
whenever insn is added or removed.
Instead of having two ways handling subprog index,
this patch is to make bpf_pseudo_func works more like
bpf_pseudo_call.
First change is to stop caching the subprog index result
in insn[1].imm after add_subprog(). The verification
process will use find_subprog(insn->imm) to figure
out the subprog index.
Second change is in bpf_adj_branches() and have it to
adjust the insn->imm for the bpf_pseudo_func insn also
whenever insn is added or removed.
Third change is in jit_subprog(). Like the bpf_pseudo_call handling,
bpf_pseudo_func temporarily stores the find_subprog() result
in insn->off. It is fine because the prog's insn has been finalized
at this point. insn->off will be reset back to 0 later to avoid
confusing the userspace prog dump tool.
Fixes: 69c087ba6225 ("bpf: Add bpf_for_each_map_elem() helper")
Signed-off-by: Martin KaFai Lau <kafai@fb.com>
---
include/linux/bpf.h | 6 ++++++
kernel/bpf/core.c | 7 +++++++
kernel/bpf/verifier.c | 37 ++++++++++++++-----------------------
3 files changed, 27 insertions(+), 23 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 2be6dfd68df9..f715e8863f4d 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -484,6 +484,12 @@ bpf_ctx_record_field_size(struct bpf_insn_access_aux *aux, u32 size)
aux->ctx_field_size = size;
}
+static inline bool bpf_pseudo_func(const struct bpf_insn *insn)
+{
+ return insn->code == (BPF_LD | BPF_IMM | BPF_DW) &&
+ insn->src_reg == BPF_PSEUDO_FUNC;
+}
+
struct bpf_prog_ops {
int (*test_run)(struct bpf_prog *prog, const union bpf_attr *kattr,
union bpf_attr __user *uattr);
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 327e3996eadb..2405e39d800f 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -390,6 +390,13 @@ static int bpf_adj_branches(struct bpf_prog *prog, u32 pos, s32 end_old,
i = end_new;
insn = prog->insnsi + end_old;
}
+ if (bpf_pseudo_func(insn)) {
+ ret = bpf_adj_delta_to_imm(insn, pos, end_old,
+ end_new, i, probe_pass);
+ if (ret)
+ return ret;
+ continue;
+ }
code = insn->code;
if ((BPF_CLASS(code) != BPF_JMP &&
BPF_CLASS(code) != BPF_JMP32) ||
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 5f8d9128860a..890b3ec375a3 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -240,12 +240,6 @@ static bool bpf_pseudo_kfunc_call(const struct bpf_insn *insn)
insn->src_reg == BPF_PSEUDO_KFUNC_CALL;
}
-static bool bpf_pseudo_func(const struct bpf_insn *insn)
-{
- return insn->code == (BPF_LD | BPF_IMM | BPF_DW) &&
- insn->src_reg == BPF_PSEUDO_FUNC;
-}
-
struct bpf_call_arg_meta {
struct bpf_map *map_ptr;
bool raw_mode;
@@ -1960,16 +1954,10 @@ static int add_subprog_and_kfunc(struct bpf_verifier_env *env)
return -EPERM;
}
- if (bpf_pseudo_func(insn)) {
+ if (bpf_pseudo_func(insn) || bpf_pseudo_call(insn))
ret = add_subprog(env, i + insn->imm + 1);
- if (ret >= 0)
- /* remember subprog */
- insn[1].imm = ret;
- } else if (bpf_pseudo_call(insn)) {
- ret = add_subprog(env, i + insn->imm + 1);
- } else {
+ else
ret = add_kfunc_call(env, insn->imm, insn->off);
- }
if (ret < 0)
return ret;
@@ -9387,7 +9375,8 @@ static int check_ld_imm(struct bpf_verifier_env *env, struct bpf_insn *insn)
if (insn->src_reg == BPF_PSEUDO_FUNC) {
struct bpf_prog_aux *aux = env->prog->aux;
- u32 subprogno = insn[1].imm;
+ u32 subprogno = find_subprog(env,
+ env->insn_idx + insn->imm + 1);
if (!aux->func_info) {
verbose(env, "missing btf func_info\n");
@@ -12557,14 +12546,9 @@ static int jit_subprogs(struct bpf_verifier_env *env)
return 0;
for (i = 0, insn = prog->insnsi; i < prog->len; i++, insn++) {
- if (bpf_pseudo_func(insn)) {
- env->insn_aux_data[i].call_imm = insn->imm;
- /* subprog is encoded in insn[1].imm */
+ if (!bpf_pseudo_func(insn) && !bpf_pseudo_call(insn))
continue;
- }
- if (!bpf_pseudo_call(insn))
- continue;
/* Upon error here we cannot fall back to interpreter but
* need a hard reject of the program. Thus -EFAULT is
* propagated in any case.
@@ -12585,6 +12569,12 @@ static int jit_subprogs(struct bpf_verifier_env *env)
env->insn_aux_data[i].call_imm = insn->imm;
/* point imm to __bpf_call_base+1 from JITs point of view */
insn->imm = 1;
+ if (bpf_pseudo_func(insn))
+ /* jit (e.g. x86_64) may emit fewer instructions
+ * if it learns a u32 imm is the same as a u64 imm.
+ * Force a non zero here.
+ */
+ insn[1].imm = 1;
}
err = bpf_prog_alloc_jited_linfo(prog);
@@ -12669,7 +12659,7 @@ static int jit_subprogs(struct bpf_verifier_env *env)
insn = func[i]->insnsi;
for (j = 0; j < func[i]->len; j++, insn++) {
if (bpf_pseudo_func(insn)) {
- subprog = insn[1].imm;
+ subprog = insn->off;
insn[0].imm = (u32)(long)func[subprog]->bpf_func;
insn[1].imm = ((u64)(long)func[subprog]->bpf_func) >> 32;
continue;
@@ -12720,7 +12710,8 @@ static int jit_subprogs(struct bpf_verifier_env *env)
for (i = 0, insn = prog->insnsi; i < prog->len; i++, insn++) {
if (bpf_pseudo_func(insn)) {
insn[0].imm = env->insn_aux_data[i].call_imm;
- insn[1].imm = find_subprog(env, i + insn[0].imm + 1);
+ insn[1].imm = insn->off;
+ insn->off = 0;
continue;
}
if (!bpf_pseudo_call(insn))
--
2.30.2
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH bpf 2/2] bpf: selftest: Trigger a DCE on the whole subprog
2021-11-06 1:40 [PATCH bpf 0/2] bpf: Fix out-of-bound issue when jit-ing bpf_pseudo_func Martin KaFai Lau
2021-11-06 1:40 ` [PATCH bpf 1/2] bpf: Stop caching subprog index in the bpf_pseudo_func insn Martin KaFai Lau
@ 2021-11-06 1:40 ` Martin KaFai Lau
2021-11-06 20:25 ` [PATCH bpf 0/2] bpf: Fix out-of-bound issue when jit-ing bpf_pseudo_func Alexei Starovoitov
2 siblings, 0 replies; 4+ messages in thread
From: Martin KaFai Lau @ 2021-11-06 1:40 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team,
Yonghong Song
This patch adds a test to trigger the DCE to remove
the whole subprog to ensure the verifier does not
depend on a stable subprog index. The DCE is done
by testing a global const.
Signed-off-by: Martin KaFai Lau <kafai@fb.com>
---
.../selftests/bpf/progs/for_each_array_map_elem.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/tools/testing/selftests/bpf/progs/for_each_array_map_elem.c b/tools/testing/selftests/bpf/progs/for_each_array_map_elem.c
index df918b2469da..52f6995ff29c 100644
--- a/tools/testing/selftests/bpf/progs/for_each_array_map_elem.c
+++ b/tools/testing/selftests/bpf/progs/for_each_array_map_elem.c
@@ -23,6 +23,16 @@ struct callback_ctx {
int output;
};
+const volatile int bypass_unused = 1;
+
+static __u64
+unused_subprog(struct bpf_map *map, __u32 *key, __u64 *val,
+ struct callback_ctx *data)
+{
+ data->output = 0;
+ return 1;
+}
+
static __u64
check_array_elem(struct bpf_map *map, __u32 *key, __u64 *val,
struct callback_ctx *data)
@@ -54,6 +64,8 @@ int test_pkt_access(struct __sk_buff *skb)
data.output = 0;
bpf_for_each_map_elem(&arraymap, check_array_elem, &data, 0);
+ if (!bypass_unused)
+ bpf_for_each_map_elem(&arraymap, unused_subprog, &data, 0);
arraymap_output = data.output;
bpf_for_each_map_elem(&percpu_map, check_percpu_elem, (void *)0, 0);
--
2.30.2
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH bpf 0/2] bpf: Fix out-of-bound issue when jit-ing bpf_pseudo_func
2021-11-06 1:40 [PATCH bpf 0/2] bpf: Fix out-of-bound issue when jit-ing bpf_pseudo_func Martin KaFai Lau
2021-11-06 1:40 ` [PATCH bpf 1/2] bpf: Stop caching subprog index in the bpf_pseudo_func insn Martin KaFai Lau
2021-11-06 1:40 ` [PATCH bpf 2/2] bpf: selftest: Trigger a DCE on the whole subprog Martin KaFai Lau
@ 2021-11-06 20:25 ` Alexei Starovoitov
2 siblings, 0 replies; 4+ messages in thread
From: Alexei Starovoitov @ 2021-11-06 20:25 UTC (permalink / raw)
To: Martin KaFai Lau
Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Kernel Team, Yonghong Song, Yonatan Komornik
On Fri, Nov 5, 2021 at 6:40 PM Martin KaFai Lau <kafai@fb.com> wrote:
>
> This set fixes an out-of-bound access issue when jit-ing the
> bpf_pseudo_func insn (i.e. ld_imm64 with src_reg == BPF_PSEUDO_FUNC)
>
> Martin KaFai Lau (2):
> bpf: Stop caching subprog index in the bpf_pseudo_func insn
> bpf: selftest: Trigger a DCE on the whole subprog
>
> include/linux/bpf.h | 6 +++
> kernel/bpf/core.c | 7 ++++
> kernel/bpf/verifier.c | 37 +++++++------------
> .../bpf/progs/for_each_array_map_elem.c | 12 ++++++
> 4 files changed, 39 insertions(+), 23 deletions(-)
Thanks!
Applied to bpf tree with
Reported-by: Yonatan Komornik <yoniko@gmail.com>
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2021-11-06 20:25 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2021-11-06 1:40 [PATCH bpf 0/2] bpf: Fix out-of-bound issue when jit-ing bpf_pseudo_func Martin KaFai Lau
2021-11-06 1:40 ` [PATCH bpf 1/2] bpf: Stop caching subprog index in the bpf_pseudo_func insn Martin KaFai Lau
2021-11-06 1:40 ` [PATCH bpf 2/2] bpf: selftest: Trigger a DCE on the whole subprog Martin KaFai Lau
2021-11-06 20:25 ` [PATCH bpf 0/2] bpf: Fix out-of-bound issue when jit-ing bpf_pseudo_func Alexei Starovoitov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox