Skip to content

Commit a6a6a0a

Browse files
committed
Alexei Starovoitov says: ==================== pull-request: bpf 2023-11-15 We've added 7 non-merge commits during the last 6 day(s) which contain a total of 9 files changed, 200 insertions(+), 49 deletions(-). The main changes are: 1) Do not allocate bpf specific percpu memory unconditionally, from Yonghong. 2) Fix precision backtracking instruction iteration, from Andrii. 3) Fix control flow graph checking, from Andrii. 4) Fix xskxceiver selftest build, from Anders. * https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf: bpf: Do not allocate percpu memory at init stage selftests/bpf: add more test cases for check_cfg() bpf: fix control-flow graph checking in privileged mode selftests/bpf: add edge case backtracking logic test bpf: fix precision backtracking instruction iteration bpf: handle ldimm64 properly in check_cfg() selftests: bpf: xskxceiver: ksft_print_msg: fix format type error ==================== Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
2 parents 674e318 + 1fda5bb commit a6a6a0a

File tree

9 files changed

+200
-49
lines changed

9 files changed

+200
-49
lines changed

include/linux/bpf.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ extern struct idr btf_idr;
5656
extern spinlock_t btf_idr_lock;
5757
extern struct kobject *btf_kobj;
5858
extern struct bpf_mem_alloc bpf_global_ma, bpf_global_percpu_ma;
59-
extern bool bpf_global_ma_set, bpf_global_percpu_ma_set;
59+
extern bool bpf_global_ma_set;
6060

6161
typedef u64 (*bpf_callback_t)(u64, u64, u64, u64, u64);
6262
typedef int (*bpf_iter_init_seq_priv_t)(void *private_data,
@@ -909,10 +909,14 @@ bpf_ctx_record_field_size(struct bpf_insn_access_aux *aux, u32 size)
909909
aux->ctx_field_size = size;
910910
}
911911

912+
static bool bpf_is_ldimm64(const struct bpf_insn *insn)
913+
{
914+
return insn->code == (BPF_LD | BPF_IMM | BPF_DW);
915+
}
916+
912917
static inline bool bpf_pseudo_func(const struct bpf_insn *insn)
913918
{
914-
return insn->code == (BPF_LD | BPF_IMM | BPF_DW) &&
915-
insn->src_reg == BPF_PSEUDO_FUNC;
919+
return bpf_is_ldimm64(insn) && insn->src_reg == BPF_PSEUDO_FUNC;
916920
}
917921

918922
struct bpf_prog_ops {

kernel/bpf/core.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@
6464
#define OFF insn->off
6565
#define IMM insn->imm
6666

67-
struct bpf_mem_alloc bpf_global_ma, bpf_global_percpu_ma;
68-
bool bpf_global_ma_set, bpf_global_percpu_ma_set;
67+
struct bpf_mem_alloc bpf_global_ma;
68+
bool bpf_global_ma_set;
6969

7070
/* No hurry in this branch
7171
*
@@ -2934,9 +2934,7 @@ static int __init bpf_global_ma_init(void)
29342934

29352935
ret = bpf_mem_alloc_init(&bpf_global_ma, 0, false);
29362936
bpf_global_ma_set = !ret;
2937-
ret = bpf_mem_alloc_init(&bpf_global_percpu_ma, 0, true);
2938-
bpf_global_percpu_ma_set = !ret;
2939-
return !bpf_global_ma_set || !bpf_global_percpu_ma_set;
2937+
return ret;
29402938
}
29412939
late_initcall(bpf_global_ma_init);
29422940
#endif

kernel/bpf/verifier.c

Lines changed: 63 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <linux/poison.h>
2727
#include <linux/module.h>
2828
#include <linux/cpumask.h>
29+
#include <linux/bpf_mem_alloc.h>
2930
#include <net/xdp.h>
3031

3132
#include "disasm.h"
@@ -41,6 +42,9 @@ static const struct bpf_verifier_ops * const bpf_verifier_ops[] = {
4142
#undef BPF_LINK_TYPE
4243
};
4344

45+
struct bpf_mem_alloc bpf_global_percpu_ma;
46+
static bool bpf_global_percpu_ma_set;
47+
4448
/* bpf_check() is a static code analyzer that walks eBPF program
4549
* instruction by instruction and updates register/stack state.
4650
* All paths of conditional branches are analyzed until 'bpf_exit' insn.
@@ -336,6 +340,7 @@ struct bpf_kfunc_call_arg_meta {
336340
struct btf *btf_vmlinux;
337341

338342
static DEFINE_MUTEX(bpf_verifier_lock);
343+
static DEFINE_MUTEX(bpf_percpu_ma_lock);
339344

340345
static const struct bpf_line_info *
341346
find_linfo(const struct bpf_verifier_env *env, u32 insn_off)
@@ -3516,12 +3521,29 @@ static int push_jmp_history(struct bpf_verifier_env *env,
35163521

35173522
/* Backtrack one insn at a time. If idx is not at the top of recorded
35183523
* history then previous instruction came from straight line execution.
3524+
* Return -ENOENT if we exhausted all instructions within given state.
3525+
*
3526+
* It's legal to have a bit of a looping with the same starting and ending
3527+
* insn index within the same state, e.g.: 3->4->5->3, so just because current
3528+
* instruction index is the same as state's first_idx doesn't mean we are
3529+
* done. If there is still some jump history left, we should keep going. We
3530+
* need to take into account that we might have a jump history between given
3531+
* state's parent and itself, due to checkpointing. In this case, we'll have
3532+
* history entry recording a jump from last instruction of parent state and
3533+
* first instruction of given state.
35193534
*/
35203535
static int get_prev_insn_idx(struct bpf_verifier_state *st, int i,
35213536
u32 *history)
35223537
{
35233538
u32 cnt = *history;
35243539

3540+
if (i == st->first_insn_idx) {
3541+
if (cnt == 0)
3542+
return -ENOENT;
3543+
if (cnt == 1 && st->jmp_history[0].idx == i)
3544+
return -ENOENT;
3545+
}
3546+
35253547
if (cnt && st->jmp_history[cnt - 1].idx == i) {
35263548
i = st->jmp_history[cnt - 1].prev_idx;
35273549
(*history)--;
@@ -4401,10 +4423,10 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int regno)
44014423
* Nothing to be tracked further in the parent state.
44024424
*/
44034425
return 0;
4404-
if (i == first_idx)
4405-
break;
44064426
subseq_idx = i;
44074427
i = get_prev_insn_idx(st, i, &history);
4428+
if (i == -ENOENT)
4429+
break;
44084430
if (i >= env->prog->len) {
44094431
/* This can happen if backtracking reached insn 0
44104432
* and there are still reg_mask or stack_mask
@@ -12074,8 +12096,19 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
1207412096
if (meta.func_id == special_kfunc_list[KF_bpf_obj_new_impl] && !bpf_global_ma_set)
1207512097
return -ENOMEM;
1207612098

12077-
if (meta.func_id == special_kfunc_list[KF_bpf_percpu_obj_new_impl] && !bpf_global_percpu_ma_set)
12078-
return -ENOMEM;
12099+
if (meta.func_id == special_kfunc_list[KF_bpf_percpu_obj_new_impl]) {
12100+
if (!bpf_global_percpu_ma_set) {
12101+
mutex_lock(&bpf_percpu_ma_lock);
12102+
if (!bpf_global_percpu_ma_set) {
12103+
err = bpf_mem_alloc_init(&bpf_global_percpu_ma, 0, true);
12104+
if (!err)
12105+
bpf_global_percpu_ma_set = true;
12106+
}
12107+
mutex_unlock(&bpf_percpu_ma_lock);
12108+
if (err)
12109+
return err;
12110+
}
12111+
}
1207912112

1208012113
if (((u64)(u32)meta.arg_constant.value) != meta.arg_constant.value) {
1208112114
verbose(env, "local type ID argument must be in range [0, U32_MAX]\n");
@@ -15386,8 +15419,7 @@ enum {
1538615419
* w - next instruction
1538715420
* e - edge
1538815421
*/
15389-
static int push_insn(int t, int w, int e, struct bpf_verifier_env *env,
15390-
bool loop_ok)
15422+
static int push_insn(int t, int w, int e, struct bpf_verifier_env *env)
1539115423
{
1539215424
int *insn_stack = env->cfg.insn_stack;
1539315425
int *insn_state = env->cfg.insn_state;
@@ -15419,7 +15451,7 @@ static int push_insn(int t, int w, int e, struct bpf_verifier_env *env,
1541915451
insn_stack[env->cfg.cur_stack++] = w;
1542015452
return KEEP_EXPLORING;
1542115453
} else if ((insn_state[w] & 0xF0) == DISCOVERED) {
15422-
if (loop_ok && env->bpf_capable)
15454+
if (env->bpf_capable)
1542315455
return DONE_EXPLORING;
1542415456
verbose_linfo(env, t, "%d: ", t);
1542515457
verbose_linfo(env, w, "%d: ", w);
@@ -15439,24 +15471,20 @@ static int visit_func_call_insn(int t, struct bpf_insn *insns,
1543915471
struct bpf_verifier_env *env,
1544015472
bool visit_callee)
1544115473
{
15442-
int ret;
15474+
int ret, insn_sz;
1544315475

15444-
ret = push_insn(t, t + 1, FALLTHROUGH, env, false);
15476+
insn_sz = bpf_is_ldimm64(&insns[t]) ? 2 : 1;
15477+
ret = push_insn(t, t + insn_sz, FALLTHROUGH, env);
1544515478
if (ret)
1544615479
return ret;
1544715480

15448-
mark_prune_point(env, t + 1);
15481+
mark_prune_point(env, t + insn_sz);
1544915482
/* when we exit from subprog, we need to record non-linear history */
15450-
mark_jmp_point(env, t + 1);
15483+
mark_jmp_point(env, t + insn_sz);
1545115484

1545215485
if (visit_callee) {
1545315486
mark_prune_point(env, t);
15454-
ret = push_insn(t, t + insns[t].imm + 1, BRANCH, env,
15455-
/* It's ok to allow recursion from CFG point of
15456-
* view. __check_func_call() will do the actual
15457-
* check.
15458-
*/
15459-
bpf_pseudo_func(insns + t));
15487+
ret = push_insn(t, t + insns[t].imm + 1, BRANCH, env);
1546015488
}
1546115489
return ret;
1546215490
}
@@ -15469,15 +15497,17 @@ static int visit_func_call_insn(int t, struct bpf_insn *insns,
1546915497
static int visit_insn(int t, struct bpf_verifier_env *env)
1547015498
{
1547115499
struct bpf_insn *insns = env->prog->insnsi, *insn = &insns[t];
15472-
int ret, off;
15500+
int ret, off, insn_sz;
1547315501

1547415502
if (bpf_pseudo_func(insn))
1547515503
return visit_func_call_insn(t, insns, env, true);
1547615504

1547715505
/* All non-branch instructions have a single fall-through edge. */
1547815506
if (BPF_CLASS(insn->code) != BPF_JMP &&
15479-
BPF_CLASS(insn->code) != BPF_JMP32)
15480-
return push_insn(t, t + 1, FALLTHROUGH, env, false);
15507+
BPF_CLASS(insn->code) != BPF_JMP32) {
15508+
insn_sz = bpf_is_ldimm64(insn) ? 2 : 1;
15509+
return push_insn(t, t + insn_sz, FALLTHROUGH, env);
15510+
}
1548115511

1548215512
switch (BPF_OP(insn->code)) {
1548315513
case BPF_EXIT:
@@ -15523,8 +15553,7 @@ static int visit_insn(int t, struct bpf_verifier_env *env)
1552315553
off = insn->imm;
1552415554

1552515555
/* unconditional jump with single edge */
15526-
ret = push_insn(t, t + off + 1, FALLTHROUGH, env,
15527-
true);
15556+
ret = push_insn(t, t + off + 1, FALLTHROUGH, env);
1552815557
if (ret)
1552915558
return ret;
1553015559

@@ -15537,11 +15566,11 @@ static int visit_insn(int t, struct bpf_verifier_env *env)
1553715566
/* conditional jump with two edges */
1553815567
mark_prune_point(env, t);
1553915568

15540-
ret = push_insn(t, t + 1, FALLTHROUGH, env, true);
15569+
ret = push_insn(t, t + 1, FALLTHROUGH, env);
1554115570
if (ret)
1554215571
return ret;
1554315572

15544-
return push_insn(t, t + insn->off + 1, BRANCH, env, true);
15573+
return push_insn(t, t + insn->off + 1, BRANCH, env);
1554515574
}
1554615575
}
1554715576

@@ -15607,11 +15636,21 @@ static int check_cfg(struct bpf_verifier_env *env)
1560715636
}
1560815637

1560915638
for (i = 0; i < insn_cnt; i++) {
15639+
struct bpf_insn *insn = &env->prog->insnsi[i];
15640+
1561015641
if (insn_state[i] != EXPLORED) {
1561115642
verbose(env, "unreachable insn %d\n", i);
1561215643
ret = -EINVAL;
1561315644
goto err_free;
1561415645
}
15646+
if (bpf_is_ldimm64(insn)) {
15647+
if (insn_state[i + 1] != 0) {
15648+
verbose(env, "jump into the middle of ldimm64 insn %d\n", i);
15649+
ret = -EINVAL;
15650+
goto err_free;
15651+
}
15652+
i++; /* skip second half of ldimm64 */
15653+
}
1561515654
}
1561615655
ret = 0; /* cfg looks good */
1561715656

tools/testing/selftests/bpf/progs/verifier_cfg.c

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,66 @@ l0_%=: r2 = r0; \
9797
" ::: __clobber_all);
9898
}
9999

100+
SEC("socket")
101+
__description("conditional loop (2)")
102+
__success
103+
__failure_unpriv __msg_unpriv("back-edge from insn 10 to 11")
104+
__naked void conditional_loop2(void)
105+
{
106+
asm volatile (" \
107+
r9 = 2 ll; \
108+
r3 = 0x20 ll; \
109+
r4 = 0x35 ll; \
110+
r8 = r4; \
111+
goto l1_%=; \
112+
l0_%=: r9 -= r3; \
113+
r9 -= r4; \
114+
r9 -= r8; \
115+
l1_%=: r8 += r4; \
116+
if r8 < 0x64 goto l0_%=; \
117+
r0 = r9; \
118+
exit; \
119+
" ::: __clobber_all);
120+
}
121+
122+
SEC("socket")
123+
__description("unconditional loop after conditional jump")
124+
__failure __msg("infinite loop detected")
125+
__failure_unpriv __msg_unpriv("back-edge from insn 3 to 2")
126+
__naked void uncond_loop_after_cond_jmp(void)
127+
{
128+
asm volatile (" \
129+
r0 = 0; \
130+
if r0 > 0 goto l1_%=; \
131+
l0_%=: r0 = 1; \
132+
goto l0_%=; \
133+
l1_%=: exit; \
134+
" ::: __clobber_all);
135+
}
136+
137+
138+
__naked __noinline __used
139+
static unsigned long never_ending_subprog()
140+
{
141+
asm volatile (" \
142+
r0 = r1; \
143+
goto -1; \
144+
" ::: __clobber_all);
145+
}
146+
147+
SEC("socket")
148+
__description("unconditional loop after conditional jump")
149+
/* infinite loop is detected *after* check_cfg() */
150+
__failure __msg("infinite loop detected")
151+
__naked void uncond_loop_in_subprog_after_cond_jmp(void)
152+
{
153+
asm volatile (" \
154+
r0 = 0; \
155+
if r0 > 0 goto l1_%=; \
156+
l0_%=: r0 += 1; \
157+
call never_ending_subprog; \
158+
l1_%=: exit; \
159+
" ::: __clobber_all);
160+
}
161+
100162
char _license[] SEC("license") = "GPL";

tools/testing/selftests/bpf/progs/verifier_loops1.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,10 @@ l0_%=: r0 += 1; \
7575
" ::: __clobber_all);
7676
}
7777

78-
SEC("tracepoint")
78+
SEC("socket")
7979
__description("bounded loop, start in the middle")
80-
__failure __msg("back-edge")
80+
__success
81+
__failure_unpriv __msg_unpriv("back-edge")
8182
__naked void loop_start_in_the_middle(void)
8283
{
8384
asm volatile (" \
@@ -136,7 +137,9 @@ l0_%=: exit; \
136137

137138
SEC("tracepoint")
138139
__description("bounded recursion")
139-
__failure __msg("back-edge")
140+
__failure
141+
/* verifier limitation in detecting max stack depth */
142+
__msg("the call stack of 8 frames is too deep !")
140143
__naked void bounded_recursion(void)
141144
{
142145
asm volatile (" \

tools/testing/selftests/bpf/progs/verifier_precision.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,43 @@ __naked int bpf_end_bswap(void)
9191
}
9292

9393
#endif /* v4 instruction */
94+
95+
SEC("?raw_tp")
96+
__success __log_level(2)
97+
/*
98+
* Without the bug fix there will be no history between "last_idx 3 first_idx 3"
99+
* and "parent state regs=" lines. "R0_w=6" parts are here to help anchor
100+
* expected log messages to the one specific mark_chain_precision operation.
101+
*
102+
* This is quite fragile: if verifier checkpointing heuristic changes, this
103+
* might need adjusting.
104+
*/
105+
__msg("2: (07) r0 += 1 ; R0_w=6")
106+
__msg("3: (35) if r0 >= 0xa goto pc+1")
107+
__msg("mark_precise: frame0: last_idx 3 first_idx 3 subseq_idx -1")
108+
__msg("mark_precise: frame0: regs=r0 stack= before 2: (07) r0 += 1")
109+
__msg("mark_precise: frame0: regs=r0 stack= before 1: (07) r0 += 1")
110+
__msg("mark_precise: frame0: regs=r0 stack= before 4: (05) goto pc-4")
111+
__msg("mark_precise: frame0: regs=r0 stack= before 3: (35) if r0 >= 0xa goto pc+1")
112+
__msg("mark_precise: frame0: parent state regs= stack=: R0_rw=P4")
113+
__msg("3: R0_w=6")
114+
__naked int state_loop_first_last_equal(void)
115+
{
116+
asm volatile (
117+
"r0 = 0;"
118+
"l0_%=:"
119+
"r0 += 1;"
120+
"r0 += 1;"
121+
/* every few iterations we'll have a checkpoint here with
122+
* first_idx == last_idx, potentially confusing precision
123+
* backtracking logic
124+
*/
125+
"if r0 >= 10 goto l1_%=;" /* checkpoint + mark_precise */
126+
"goto l0_%=;"
127+
"l1_%=:"
128+
"exit;"
129+
::: __clobber_common
130+
);
131+
}
132+
133+
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)