Skip to content

Commit f7f4bd1

Browse files
Xu KuohaiKernel Patches Daemon
authored andcommitted
bpf: arm64: Fix panic due to missing BTI at indirect jump targets
When BTI is enabled, the indirect jump selftest triggers BTI exception: Internal error: Oops - BTI: 0000000036000003 [#1] SMP ... Call trace: bpf_prog_2e5f1c71c13ac3e0_big_jump_table+0x54/0xf8 (P) bpf_prog_run_pin_on_cpu+0x140/0x468 bpf_prog_test_run_syscall+0x280/0x3b8 bpf_prog_test_run+0x22c/0x2c0 __sys_bpf+0x4d8/0x5c8 __arm64_sys_bpf+0x88/0xa8 invoke_syscall+0x80/0x220 el0_svc_common+0x160/0x1d0 do_el0_svc+0x54/0x70 el0_svc+0x54/0x188 el0t_64_sync_handler+0x84/0x130 el0t_64_sync+0x198/0x1a0 This happens because no BTI instruction is generated by the JIT for indirect jump targets. Fix it by emitting BTI instruction for every possible indirect jump targets when BTI is enabled. The targets are identified by traversing all instruction arrays used by the BPF program, since indirect jump targets can only be read from instruction arrays. Fixes: f4a66cf ("bpf: arm64: Add support for indirect jumps") Signed-off-by: Xu Kuohai <[email protected]>
1 parent f015201 commit f7f4bd1

File tree

3 files changed

+75
-0
lines changed

3 files changed

+75
-0
lines changed

arch/arm64/net/bpf_jit_comp.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ static const int bpf2a64[] = {
7878

7979
struct jit_ctx {
8080
const struct bpf_prog *prog;
81+
unsigned long *indirect_targets;
8182
int idx;
8283
int epilogue_offset;
8384
int *offset;
@@ -1199,6 +1200,11 @@ static int add_exception_handler(const struct bpf_insn *insn,
11991200
return 0;
12001201
}
12011202

1203+
static bool maybe_indirect_target(int insn_off, unsigned long *targets_bitmap)
1204+
{
1205+
return targets_bitmap && test_bit(insn_off, targets_bitmap);
1206+
}
1207+
12021208
/* JITs an eBPF instruction.
12031209
* Returns:
12041210
* 0 - successfully JITed an 8-byte eBPF instruction.
@@ -1231,6 +1237,9 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
12311237
int ret;
12321238
bool sign_extend;
12331239

1240+
if (maybe_indirect_target(i, ctx->indirect_targets))
1241+
emit_bti(A64_BTI_J, ctx);
1242+
12341243
switch (code) {
12351244
/* dst = src */
12361245
case BPF_ALU | BPF_MOV | BPF_X:
@@ -2085,6 +2094,16 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
20852094
memset(&ctx, 0, sizeof(ctx));
20862095
ctx.prog = prog;
20872096

2097+
if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) && bpf_prog_has_insn_array(prog)) {
2098+
ctx.indirect_targets = kvcalloc(BITS_TO_LONGS(prog->len), sizeof(unsigned long),
2099+
GFP_KERNEL);
2100+
if (ctx.indirect_targets == NULL) {
2101+
prog = orig_prog;
2102+
goto out_off;
2103+
}
2104+
bpf_prog_collect_indirect_targets(prog, ctx.indirect_targets);
2105+
}
2106+
20882107
ctx.offset = kvcalloc(prog->len + 1, sizeof(int), GFP_KERNEL);
20892108
if (ctx.offset == NULL) {
20902109
prog = orig_prog;
@@ -2248,6 +2267,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
22482267
prog->aux->priv_stack_ptr = NULL;
22492268
}
22502269
kvfree(ctx.offset);
2270+
kvfree(ctx.indirect_targets);
22512271
out_priv_stack:
22522272
kfree(jit_data);
22532273
prog->aux->jit_data = NULL;

include/linux/bpf.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3822,11 +3822,23 @@ void bpf_insn_array_adjust_after_remove(struct bpf_map *map, u32 off, u32 len);
38223822

38233823
#ifdef CONFIG_BPF_SYSCALL
38243824
void bpf_prog_update_insn_ptrs(struct bpf_prog *prog, u32 *offsets, void *image);
3825+
void bpf_prog_collect_indirect_targets(const struct bpf_prog *prog, unsigned long *bitmap);
3826+
bool bpf_prog_has_insn_array(const struct bpf_prog *prog);
38253827
#else
38263828
static inline void
38273829
bpf_prog_update_insn_ptrs(struct bpf_prog *prog, u32 *offsets, void *image)
38283830
{
38293831
}
3832+
3833+
static inline bool bpf_prog_has_insn_array(const struct bpf_prog *prog)
3834+
{
3835+
return false;
3836+
}
3837+
3838+
static inline void
3839+
bpf_prog_collect_indirect_targets(const struct bpf_prog *prog, unsigned long *bitmap)
3840+
{
3841+
}
38303842
#endif
38313843

38323844
static inline int bpf_map_check_op_flags(struct bpf_map *map, u64 flags, u64 allowed_flags)

kernel/bpf/bpf_insn_array.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,3 +302,46 @@ void bpf_prog_update_insn_ptrs(struct bpf_prog *prog, u32 *offsets, void *image)
302302
}
303303
}
304304
}
305+
306+
bool bpf_prog_has_insn_array(const struct bpf_prog *prog)
307+
{
308+
int i;
309+
310+
for (i = 0; i < prog->aux->used_map_cnt; i++) {
311+
if (is_insn_array(prog->aux->used_maps[i]))
312+
return true;
313+
}
314+
return false;
315+
}
316+
317+
/*
318+
* This function collects possible indirect jump targets in a BPF program. Since indirect jump
319+
* targets can only be read from instruction arrays, it traverses all instruction arrays used
320+
* by @prog. For each instruction in the arrays, it sets the corresponding bit in @bitmap.
321+
*/
322+
void bpf_prog_collect_indirect_targets(const struct bpf_prog *prog, unsigned long *bitmap)
323+
{
324+
struct bpf_insn_array *insn_array;
325+
struct bpf_map *map;
326+
u32 xlated_off;
327+
int i, j;
328+
329+
for (i = 0; i < prog->aux->used_map_cnt; i++) {
330+
map = prog->aux->used_maps[i];
331+
if (!is_insn_array(map))
332+
continue;
333+
334+
insn_array = cast_insn_array(map);
335+
for (j = 0; j < map->max_entries; j++) {
336+
xlated_off = insn_array->values[j].xlated_off;
337+
if (xlated_off == INSN_DELETED)
338+
continue;
339+
if (xlated_off < prog->aux->subprog_start)
340+
continue;
341+
xlated_off -= prog->aux->subprog_start;
342+
if (xlated_off >= prog->len)
343+
continue;
344+
__set_bit(xlated_off, bitmap);
345+
}
346+
}
347+
}

0 commit comments

Comments
 (0)