From nobody Fri Dec 19 19:37:39 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 59B4A2FABE3; Thu, 4 Dec 2025 03:32:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764819151; cv=none; b=KzXnU8Rn/k46NerIHsJD5MsQ5dW5W9GMa67wyWjGty/BH0zqdgyKwfghQO27355FCHfn1vd2+8KUflMWHATsCD8N+t/AEDxbsHVuwSoUFvbE8jixh06I3z1jIG7NHFlkM8gV2EZu4GgF661kOnhfnQF3RBhO+11kpLikOSOlb24= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764819151; c=relaxed/simple; bh=yDtvvG/5I3bNM5cx+KLJA0l/qkwgemErJcJUz4VFjOI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rSNQOVr47UjpRl+4moKZ0WCCkCq0nJJpIXjPRCQ9zjEXeXHUmAL6OWGzy2ElxXUqGXc96rvS1cRxWrXofYG0ONOh+wrSXQI/vAstMbPyJvlUPrbK0HGkRPEu0DBznXWpMiNen1DuZ+3ijYAkOgvOysSLEj0xOaBaPNz8qZ52SS0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=e4ueK4fO; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="e4ueK4fO" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4CCC6C19423; Thu, 4 Dec 2025 03:32:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1764819150; bh=yDtvvG/5I3bNM5cx+KLJA0l/qkwgemErJcJUz4VFjOI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=e4ueK4fODazjXLi8qe+Z4foE7PjfpYx01jgvSsNAFlIzAX+HT2ZHUkLMzSgqIKc15 Owcjpm2cScJJiKjgrfGefIm2UgOyrM7NGDVQgXln0g4xysKFmF0QKoDcYpI/PmNW/8 WgkGA5ib3SUu92OsEpD+Nmjy28ua6yS7obB26FrWt/HsqKI185TxWJYJPOJ4c9Mv0W K/6EiV8B7EKb1SQJY7TUdSCzkgewVKjlAwu4qVGfYfFbUjIMzGj/EbNyUr/iVEuHXg IaUXSXj3ihmIiwMq882mQpL8BGi/tTjKAox8yPRIObrme8rpMfOwY2iKkj+do5OguR 3xCCj6ql56U9Q== From: Josh Poimboeuf To: x86@kernel.org Cc: linux-kernel@vger.kernel.org, live-patching@vger.kernel.org, bpf@vger.kernel.org, Andrey Grodzovsky , Petr Mladek , Song Liu , Raja Khan , Miroslav Benes , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Peter Zijlstra Subject: [PATCH v2 1/2] bpf: Add bpf_has_frame_pointer() Date: Wed, 3 Dec 2025 19:32:15 -0800 Message-ID: X-Mailer: git-send-email 2.51.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Introduce a bpf_has_frame_pointer() helper that unwinders can call to determine whether a given instruction pointer is within the valid frame pointer region of a BPF JIT program or trampoline (i.e., after the prologue, before the epilogue). This will enable livepatch (with the ORC unwinder) to reliably unwind through BPF JIT frames. Acked-by: Song Liu Acked-and-tested-by: Andrey Grodzovsky Signed-off-by: Josh Poimboeuf Reviewed-by: Jiri Olsa --- arch/x86/net/bpf_jit_comp.c | 12 ++++++++++++ include/linux/bpf.h | 3 +++ kernel/bpf/core.c | 16 ++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index de5083cb1d37..3ec4fa94086a 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -1661,6 +1661,9 @@ static int do_jit(struct bpf_prog *bpf_prog, int *add= rs, 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 =3D prog - temp; + /* Exception callback will clobber callee regs for its own use, and * restore the original callee regs from main prog's stack frame. */ @@ -2716,6 +2719,8 @@ st: if (is_imm8(insn->off)) pop_r12(&prog); } EMIT1(0xC9); /* leave */ + bpf_prog->aux->ksym.fp_end =3D prog - temp; + emit_return(&prog, image + addrs[i - 1] + (prog - temp)); break; =20 @@ -3299,6 +3304,9 @@ static int __arch_prepare_bpf_trampoline(struct bpf_t= ramp_image *im, void *rw_im } EMIT1(0x55); /* push rbp */ EMIT3(0x48, 0x89, 0xE5); /* mov rbp, rsp */ + if (im) + im->ksym.fp_start =3D prog - (u8 *)rw_image; + if (!is_imm8(stack_size)) { /* sub rsp, stack_size */ EMIT3_off32(0x48, 0x81, 0xEC, stack_size); @@ -3436,7 +3444,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); =20 emit_ldx(&prog, BPF_DW, BPF_REG_6, BPF_REG_FP, -rbx_off); + EMIT1(0xC9); /* leave */ + if (im) + im->ksym.fp_end =3D 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 */ diff --git a/include/linux/bpf.h b/include/linux/bpf.h index d808253f2e94..e3f56e8443da 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1257,6 +1257,8 @@ struct bpf_ksym { struct list_head lnode; struct latch_tree_node tnode; bool prog; + u32 fp_start; + u32 fp_end; }; =20 enum bpf_tramp_prog_type { @@ -1483,6 +1485,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); diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index d595fe512498..7cd8382d1152 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -760,6 +760,22 @@ struct bpf_prog *bpf_prog_ksym_find(unsigned long addr) NULL; } =20 +bool bpf_has_frame_pointer(unsigned long ip) +{ + struct bpf_ksym *ksym; + unsigned long offset; + + guard(rcu)(); + + ksym =3D bpf_ksym_find(ip); + if (!ksym || !ksym->fp_start || !ksym->fp_end) + return false; + + offset =3D ip - ksym->start; + + return offset >=3D ksym->fp_start && offset < ksym->fp_end; +} + const struct exception_table_entry *search_bpf_extables(unsigned long addr) { const struct exception_table_entry *e =3D NULL; --=20 2.51.1 From nobody Fri Dec 19 19:37:39 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D6F583128C2; Thu, 4 Dec 2025 03:32:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764819152; cv=none; b=E9lcY8MCr0k8HmSkwk2VfXzKalttfVj4JS/rTKC0K/AmymoVpOV9wF7OEiIo9IxKU3jAAX4b5UqRNCpULrmmbkAQb1rKcLMD2ZFt0GRYkojGTvCq7iqboNy/OUM7ebQt5ZCOr6GR5uize0nhoD/kW8+MiKOjdx0EFRodPxtzQsg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764819152; c=relaxed/simple; bh=SLkkaavShBRgwwTQr3WXw+gnG4olkqmXKxVcZJeylvE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZanNDaCYAOTm5hhdrfhq4zWeT6TcZQLddRIjPeS0cKEUsyHadekCGSiVcsBavHAzjzkFRF2T3AQn7j4hRcMR/tQu99hhGYSiGz0NKjp1IGxglBGxEEAFgkoI9ptF1yRDG2vN/97PMtGzuorYbTVQV6TA2IXnLkGb2wJwDiTonJk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=RI+eAnMb; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="RI+eAnMb" Received: by smtp.kernel.org (Postfix) with ESMTPSA id F3C71C113D0; Thu, 4 Dec 2025 03:32:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1764819151; bh=SLkkaavShBRgwwTQr3WXw+gnG4olkqmXKxVcZJeylvE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=RI+eAnMbOeXV9VrXuF3NkNTkzWCN/0C4+soag4VjRI5Te1tyvZAecMTY+8bm7hOkO xVrIVqOsuX/BrOO459dWLkPLHPOtgAutn2wdTfpFmZsGCsWyoY7O3Rs19pcjw3tsgJ 7Q3uXIXGlYoFV+BvYF0Zl96gsh2LY+DJFImm0q36R4P1ENcLsLRs4dW3j8w0ptWPZd T3X7f64JXXWJ9DSpSxgtrEPKZtiEFczmQzC011SR56S7lajemmJgZJFL+len/swjKA wPwUMDgdSautHGcVTSaMoWnSV2JIvDLwJgSAZfeRyZ7BF964gTtTintsTw09sfbjty wSryD3QjQ+NZw== From: Josh Poimboeuf To: x86@kernel.org Cc: linux-kernel@vger.kernel.org, live-patching@vger.kernel.org, bpf@vger.kernel.org, Andrey Grodzovsky , Petr Mladek , Song Liu , Raja Khan , Miroslav Benes , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Peter Zijlstra Subject: [PATCH v2 2/2] x86/unwind/orc: Support reliable unwinding through BPF stack frames Date: Wed, 3 Dec 2025 19:32:16 -0800 Message-ID: X-Mailer: git-send-email 2.51.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" BPF JIT programs and trampolines use a frame pointer, so the current ORC unwinder strategy of falling back to frame pointers (when an ORC entry is missing) usually works in practice when unwinding through BPF JIT stack frames. However, that frame pointer fallback is just a guess, so the unwind gets marked unreliable for live patching, which can cause livepatch transition stalls. Make the common case reliable by calling the bpf_has_frame_pointer() helper to detect the valid frame pointer region of BPF JIT programs and trampolines. Fixes: ee9f8fce9964 ("x86/unwind: Add the ORC unwinder") Reported-by: Andrey Grodzovsky Closes: https://lore.kernel.org/0e555733-c670-4e84-b2e6-abb8b84ade38@crowds= trike.com Acked-by: Song Liu Acked-and-tested-by: Andrey Grodzovsky Signed-off-by: Josh Poimboeuf Reviewed-by: Jiri Olsa --- arch/x86/kernel/unwind_orc.c | 39 +++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/arch/x86/kernel/unwind_orc.c b/arch/x86/kernel/unwind_orc.c index 977ee75e047c..f610fde2d5c4 100644 --- a/arch/x86/kernel/unwind_orc.c +++ b/arch/x86/kernel/unwind_orc.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -172,6 +173,25 @@ static struct orc_entry *orc_ftrace_find(unsigned long= ip) } #endif =20 +/* Fake frame pointer entry -- used as a fallback for generated code */ +static struct orc_entry orc_fp_entry =3D { + .type =3D ORC_TYPE_CALL, + .sp_reg =3D ORC_REG_BP, + .sp_offset =3D 16, + .bp_reg =3D ORC_REG_PREV_SP, + .bp_offset =3D -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=3D=3D0, 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 =3D { .type =3D ORC_TYPE_CALL }; =20 -/* Fake frame pointer entry -- used as a fallback for generated code */ -static struct orc_entry orc_fp_entry =3D { - .type =3D ORC_TYPE_CALL, - .sp_reg =3D ORC_REG_BP, - .sp_offset =3D 16, - .bp_reg =3D ORC_REG_PREV_SP, - .bp_offset =3D -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; =20 + /* BPF lookup: */ + orc =3D orc_bpf_find(ip); + if (orc) + return orc; + return orc_ftrace_find(ip); } =20 @@ -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 =3D &orc_fp_entry; state->error =3D true; --=20 2.51.1