Skip to content

Commit 41188e9

Browse files
eddyz87borkmann
authored andcommitted
selftest/bpf: Test for use-after-free bug fix in inline_bpf_loop
This test verifies that bpf_loop() inlining works as expected when address of `env->prog` is updated. This address is updated upon BPF program reallocation. Reallocation is handled by bpf_prog_realloc(), which reuses old memory if page boundary is not crossed. The value of `len` in the test is chosen to cross this boundary on bpf_loop() patching. Verify that the use-after-free bug in inline_bpf_loop() reported by Dan Carpenter is fixed. Signed-off-by: Eduard Zingerman <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent fb4e3b3 commit 41188e9

File tree

2 files changed

+50
-0
lines changed

2 files changed

+50
-0
lines changed

tools/testing/selftests/bpf/test_verifier.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,45 @@ static void bpf_fill_torturous_jumps(struct bpf_test *self)
425425
}
426426
}
427427

428+
static void bpf_fill_big_prog_with_loop_1(struct bpf_test *self)
429+
{
430+
struct bpf_insn *insn = self->fill_insns;
431+
/* This test was added to catch a specific use after free
432+
* error, which happened upon BPF program reallocation.
433+
* Reallocation is handled by core.c:bpf_prog_realloc, which
434+
* reuses old memory if page boundary is not crossed. The
435+
* value of `len` is chosen to cross this boundary on bpf_loop
436+
* patching.
437+
*/
438+
const int len = getpagesize() - 25;
439+
int callback_load_idx;
440+
int callback_idx;
441+
int i = 0;
442+
443+
insn[i++] = BPF_ALU64_IMM(BPF_MOV, BPF_REG_1, 1);
444+
callback_load_idx = i;
445+
insn[i++] = BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW,
446+
BPF_REG_2, BPF_PSEUDO_FUNC, 0,
447+
777 /* filled below */);
448+
insn[i++] = BPF_RAW_INSN(0, 0, 0, 0, 0);
449+
insn[i++] = BPF_ALU64_IMM(BPF_MOV, BPF_REG_3, 0);
450+
insn[i++] = BPF_ALU64_IMM(BPF_MOV, BPF_REG_4, 0);
451+
insn[i++] = BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_loop);
452+
453+
while (i < len - 3)
454+
insn[i++] = BPF_ALU64_IMM(BPF_MOV, BPF_REG_0, 0);
455+
insn[i++] = BPF_EXIT_INSN();
456+
457+
callback_idx = i;
458+
insn[i++] = BPF_ALU64_IMM(BPF_MOV, BPF_REG_0, 0);
459+
insn[i++] = BPF_EXIT_INSN();
460+
461+
insn[callback_load_idx].imm = callback_idx - callback_load_idx - 1;
462+
self->func_info[1].insn_off = callback_idx;
463+
self->prog_len = i;
464+
assert(i == len);
465+
}
466+
428467
/* BPF_SK_LOOKUP contains 13 instructions, if you need to fix up maps */
429468
#define BPF_SK_LOOKUP(func) \
430469
/* struct bpf_sock_tuple tuple = {} */ \

tools/testing/selftests/bpf/verifier/bpf_loop_inline.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,17 @@
244244
.func_info_cnt = 3,
245245
BTF_TYPES
246246
},
247+
{
248+
"inline bpf_loop call in a big program",
249+
.insns = {},
250+
.fill_helper = bpf_fill_big_prog_with_loop_1,
251+
.expected_insns = { PSEUDO_CALL_INSN() },
252+
.unexpected_insns = { HELPER_CALL_INSN() },
253+
.result = ACCEPT,
254+
.func_info = { { 0, MAIN_TYPE }, { 16, CALLBACK_TYPE } },
255+
.func_info_cnt = 2,
256+
BTF_TYPES
257+
},
247258

248259
#undef HELPER_CALL_INSN
249260
#undef PSEUDO_CALL_INSN

0 commit comments

Comments
 (0)