mirror of
https://github.com/torvalds/linux.git
synced 2026-05-12 16:18:45 +02:00
Merge branch 'bpf-x86-unwind-orc-support-reliable-unwinding-through-bpf-stack-frames'
Josh Poimboeuf says: ==================== bpf, x86/unwind/orc: Support reliable unwinding through BPF stack frames Fix livepatch stalls which may be seen when a task is blocked with BPF JIT on its kernel stack. Changes since v1 (https://lore.kernel.org/cover.1764699074.git.jpoimboe@kernel.org): - fix NULL ptr deref in __arch_prepare_bpf_trampoline() ==================== Link: https://patch.msgid.link/cover.1764818927.git.jpoimboe@kernel.org Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
commit
297c3fba9d
|
|
@ -2,6 +2,7 @@
|
|||
#include <linux/objtool.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sort.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/stacktrace.h>
|
||||
#include <asm/unwind.h>
|
||||
|
|
@ -172,6 +173,25 @@ static struct orc_entry *orc_ftrace_find(unsigned long ip)
|
|||
}
|
||||
#endif
|
||||
|
||||
/* Fake frame pointer entry -- used as a fallback for generated code */
|
||||
static struct orc_entry orc_fp_entry = {
|
||||
.type = ORC_TYPE_CALL,
|
||||
.sp_reg = ORC_REG_BP,
|
||||
.sp_offset = 16,
|
||||
.bp_reg = ORC_REG_PREV_SP,
|
||||
.bp_offset = -16,
|
||||
};
|
||||
|
||||
static struct orc_entry *orc_bpf_find(unsigned long ip)
|
||||
{
|
||||
#ifdef CONFIG_BPF_JIT
|
||||
if (bpf_has_frame_pointer(ip))
|
||||
return &orc_fp_entry;
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we crash with IP==0, the last successfully executed instruction
|
||||
* was probably an indirect function call with a NULL function pointer,
|
||||
|
|
@ -186,15 +206,6 @@ static struct orc_entry null_orc_entry = {
|
|||
.type = ORC_TYPE_CALL
|
||||
};
|
||||
|
||||
/* Fake frame pointer entry -- used as a fallback for generated code */
|
||||
static struct orc_entry orc_fp_entry = {
|
||||
.type = ORC_TYPE_CALL,
|
||||
.sp_reg = ORC_REG_BP,
|
||||
.sp_offset = 16,
|
||||
.bp_reg = ORC_REG_PREV_SP,
|
||||
.bp_offset = -16,
|
||||
};
|
||||
|
||||
static struct orc_entry *orc_find(unsigned long ip)
|
||||
{
|
||||
static struct orc_entry *orc;
|
||||
|
|
@ -238,6 +249,11 @@ static struct orc_entry *orc_find(unsigned long ip)
|
|||
if (orc)
|
||||
return orc;
|
||||
|
||||
/* BPF lookup: */
|
||||
orc = orc_bpf_find(ip);
|
||||
if (orc)
|
||||
return orc;
|
||||
|
||||
return orc_ftrace_find(ip);
|
||||
}
|
||||
|
||||
|
|
@ -495,9 +511,8 @@ bool unwind_next_frame(struct unwind_state *state)
|
|||
if (!orc) {
|
||||
/*
|
||||
* As a fallback, try to assume this code uses a frame pointer.
|
||||
* This is useful for generated code, like BPF, which ORC
|
||||
* doesn't know about. This is just a guess, so the rest of
|
||||
* the unwind is no longer considered reliable.
|
||||
* This is just a guess, so the rest of the unwind is no longer
|
||||
* considered reliable.
|
||||
*/
|
||||
orc = &orc_fp_entry;
|
||||
state->error = true;
|
||||
|
|
|
|||
|
|
@ -1678,6 +1678,9 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image
|
|||
emit_prologue(&prog, image, stack_depth,
|
||||
bpf_prog_was_classic(bpf_prog), tail_call_reachable,
|
||||
bpf_is_subprog(bpf_prog), bpf_prog->aux->exception_cb);
|
||||
|
||||
bpf_prog->aux->ksym.fp_start = prog - temp;
|
||||
|
||||
/* Exception callback will clobber callee regs for its own use, and
|
||||
* restore the original callee regs from main prog's stack frame.
|
||||
*/
|
||||
|
|
@ -2736,6 +2739,8 @@ st: if (is_imm8(insn->off))
|
|||
pop_r12(&prog);
|
||||
}
|
||||
EMIT1(0xC9); /* leave */
|
||||
bpf_prog->aux->ksym.fp_end = prog - temp;
|
||||
|
||||
emit_return(&prog, image + addrs[i - 1] + (prog - temp));
|
||||
break;
|
||||
|
||||
|
|
@ -3325,6 +3330,9 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
|
|||
}
|
||||
EMIT1(0x55); /* push rbp */
|
||||
EMIT3(0x48, 0x89, 0xE5); /* mov rbp, rsp */
|
||||
if (im)
|
||||
im->ksym.fp_start = prog - (u8 *)rw_image;
|
||||
|
||||
if (!is_imm8(stack_size)) {
|
||||
/* sub rsp, stack_size */
|
||||
EMIT3_off32(0x48, 0x81, 0xEC, stack_size);
|
||||
|
|
@ -3462,7 +3470,11 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
|
|||
emit_ldx(&prog, BPF_DW, BPF_REG_0, BPF_REG_FP, -8);
|
||||
|
||||
emit_ldx(&prog, BPF_DW, BPF_REG_6, BPF_REG_FP, -rbx_off);
|
||||
|
||||
EMIT1(0xC9); /* leave */
|
||||
if (im)
|
||||
im->ksym.fp_end = prog - (u8 *)rw_image;
|
||||
|
||||
if (flags & BPF_TRAMP_F_SKIP_FRAME) {
|
||||
/* skip our return address and return to parent */
|
||||
EMIT4(0x48, 0x83, 0xC4, 8); /* add rsp, 8 */
|
||||
|
|
|
|||
|
|
@ -1283,6 +1283,8 @@ struct bpf_ksym {
|
|||
struct list_head lnode;
|
||||
struct latch_tree_node tnode;
|
||||
bool prog;
|
||||
u32 fp_start;
|
||||
u32 fp_end;
|
||||
};
|
||||
|
||||
enum bpf_tramp_prog_type {
|
||||
|
|
@ -1511,6 +1513,7 @@ void bpf_image_ksym_add(struct bpf_ksym *ksym);
|
|||
void bpf_image_ksym_del(struct bpf_ksym *ksym);
|
||||
void bpf_ksym_add(struct bpf_ksym *ksym);
|
||||
void bpf_ksym_del(struct bpf_ksym *ksym);
|
||||
bool bpf_has_frame_pointer(unsigned long ip);
|
||||
int bpf_jit_charge_modmem(u32 size);
|
||||
void bpf_jit_uncharge_modmem(u32 size);
|
||||
bool bpf_prog_has_trampoline(const struct bpf_prog *prog);
|
||||
|
|
|
|||
|
|
@ -760,6 +760,22 @@ struct bpf_prog *bpf_prog_ksym_find(unsigned long addr)
|
|||
NULL;
|
||||
}
|
||||
|
||||
bool bpf_has_frame_pointer(unsigned long ip)
|
||||
{
|
||||
struct bpf_ksym *ksym;
|
||||
unsigned long offset;
|
||||
|
||||
guard(rcu)();
|
||||
|
||||
ksym = bpf_ksym_find(ip);
|
||||
if (!ksym || !ksym->fp_start || !ksym->fp_end)
|
||||
return false;
|
||||
|
||||
offset = ip - ksym->start;
|
||||
|
||||
return offset >= ksym->fp_start && offset < ksym->fp_end;
|
||||
}
|
||||
|
||||
const struct exception_table_entry *search_bpf_extables(unsigned long addr)
|
||||
{
|
||||
const struct exception_table_entry *e = NULL;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user