Skip to content

Commit 297c3fb

Browse files
author
Alexei Starovoitov
committed
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/[email protected]): - fix NULL ptr deref in __arch_prepare_bpf_trampoline() ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
2 parents 189e5de + 01bc3b6 commit 297c3fb

File tree

4 files changed

+58
-12
lines changed

4 files changed

+58
-12
lines changed

arch/x86/kernel/unwind_orc.c

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include <linux/objtool.h>
33
#include <linux/module.h>
44
#include <linux/sort.h>
5+
#include <linux/bpf.h>
56
#include <asm/ptrace.h>
67
#include <asm/stacktrace.h>
78
#include <asm/unwind.h>
@@ -172,6 +173,25 @@ static struct orc_entry *orc_ftrace_find(unsigned long ip)
172173
}
173174
#endif
174175

176+
/* Fake frame pointer entry -- used as a fallback for generated code */
177+
static struct orc_entry orc_fp_entry = {
178+
.type = ORC_TYPE_CALL,
179+
.sp_reg = ORC_REG_BP,
180+
.sp_offset = 16,
181+
.bp_reg = ORC_REG_PREV_SP,
182+
.bp_offset = -16,
183+
};
184+
185+
static struct orc_entry *orc_bpf_find(unsigned long ip)
186+
{
187+
#ifdef CONFIG_BPF_JIT
188+
if (bpf_has_frame_pointer(ip))
189+
return &orc_fp_entry;
190+
#endif
191+
192+
return NULL;
193+
}
194+
175195
/*
176196
* If we crash with IP==0, the last successfully executed instruction
177197
* was probably an indirect function call with a NULL function pointer,
@@ -186,15 +206,6 @@ static struct orc_entry null_orc_entry = {
186206
.type = ORC_TYPE_CALL
187207
};
188208

189-
/* Fake frame pointer entry -- used as a fallback for generated code */
190-
static struct orc_entry orc_fp_entry = {
191-
.type = ORC_TYPE_CALL,
192-
.sp_reg = ORC_REG_BP,
193-
.sp_offset = 16,
194-
.bp_reg = ORC_REG_PREV_SP,
195-
.bp_offset = -16,
196-
};
197-
198209
static struct orc_entry *orc_find(unsigned long ip)
199210
{
200211
static struct orc_entry *orc;
@@ -238,6 +249,11 @@ static struct orc_entry *orc_find(unsigned long ip)
238249
if (orc)
239250
return orc;
240251

252+
/* BPF lookup: */
253+
orc = orc_bpf_find(ip);
254+
if (orc)
255+
return orc;
256+
241257
return orc_ftrace_find(ip);
242258
}
243259

@@ -495,9 +511,8 @@ bool unwind_next_frame(struct unwind_state *state)
495511
if (!orc) {
496512
/*
497513
* As a fallback, try to assume this code uses a frame pointer.
498-
* This is useful for generated code, like BPF, which ORC
499-
* doesn't know about. This is just a guess, so the rest of
500-
* the unwind is no longer considered reliable.
514+
* This is just a guess, so the rest of the unwind is no longer
515+
* considered reliable.
501516
*/
502517
orc = &orc_fp_entry;
503518
state->error = true;

arch/x86/net/bpf_jit_comp.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1678,6 +1678,9 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image
16781678
emit_prologue(&prog, image, stack_depth,
16791679
bpf_prog_was_classic(bpf_prog), tail_call_reachable,
16801680
bpf_is_subprog(bpf_prog), bpf_prog->aux->exception_cb);
1681+
1682+
bpf_prog->aux->ksym.fp_start = prog - temp;
1683+
16811684
/* Exception callback will clobber callee regs for its own use, and
16821685
* restore the original callee regs from main prog's stack frame.
16831686
*/
@@ -2736,6 +2739,8 @@ st: if (is_imm8(insn->off))
27362739
pop_r12(&prog);
27372740
}
27382741
EMIT1(0xC9); /* leave */
2742+
bpf_prog->aux->ksym.fp_end = prog - temp;
2743+
27392744
emit_return(&prog, image + addrs[i - 1] + (prog - temp));
27402745
break;
27412746

@@ -3325,6 +3330,9 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
33253330
}
33263331
EMIT1(0x55); /* push rbp */
33273332
EMIT3(0x48, 0x89, 0xE5); /* mov rbp, rsp */
3333+
if (im)
3334+
im->ksym.fp_start = prog - (u8 *)rw_image;
3335+
33283336
if (!is_imm8(stack_size)) {
33293337
/* sub rsp, stack_size */
33303338
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
34623470
emit_ldx(&prog, BPF_DW, BPF_REG_0, BPF_REG_FP, -8);
34633471

34643472
emit_ldx(&prog, BPF_DW, BPF_REG_6, BPF_REG_FP, -rbx_off);
3473+
34653474
EMIT1(0xC9); /* leave */
3475+
if (im)
3476+
im->ksym.fp_end = prog - (u8 *)rw_image;
3477+
34663478
if (flags & BPF_TRAMP_F_SKIP_FRAME) {
34673479
/* skip our return address and return to parent */
34683480
EMIT4(0x48, 0x83, 0xC4, 8); /* add rsp, 8 */

include/linux/bpf.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,6 +1283,8 @@ struct bpf_ksym {
12831283
struct list_head lnode;
12841284
struct latch_tree_node tnode;
12851285
bool prog;
1286+
u32 fp_start;
1287+
u32 fp_end;
12861288
};
12871289

12881290
enum bpf_tramp_prog_type {
@@ -1511,6 +1513,7 @@ void bpf_image_ksym_add(struct bpf_ksym *ksym);
15111513
void bpf_image_ksym_del(struct bpf_ksym *ksym);
15121514
void bpf_ksym_add(struct bpf_ksym *ksym);
15131515
void bpf_ksym_del(struct bpf_ksym *ksym);
1516+
bool bpf_has_frame_pointer(unsigned long ip);
15141517
int bpf_jit_charge_modmem(u32 size);
15151518
void bpf_jit_uncharge_modmem(u32 size);
15161519
bool bpf_prog_has_trampoline(const struct bpf_prog *prog);

kernel/bpf/core.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,22 @@ struct bpf_prog *bpf_prog_ksym_find(unsigned long addr)
760760
NULL;
761761
}
762762

763+
bool bpf_has_frame_pointer(unsigned long ip)
764+
{
765+
struct bpf_ksym *ksym;
766+
unsigned long offset;
767+
768+
guard(rcu)();
769+
770+
ksym = bpf_ksym_find(ip);
771+
if (!ksym || !ksym->fp_start || !ksym->fp_end)
772+
return false;
773+
774+
offset = ip - ksym->start;
775+
776+
return offset >= ksym->fp_start && offset < ksym->fp_end;
777+
}
778+
763779
const struct exception_table_entry *search_bpf_extables(unsigned long addr)
764780
{
765781
const struct exception_table_entry *e = NULL;

0 commit comments

Comments
 (0)