[PATCH bpf-next 1/5] bpf: Put the line info back when the JIT falls back after blinding
From: Qiliang Yuan
Date: Mon Nov 02 2026 - 07:40:16 EST
bpf_jit_blind_constants() blinds the constants of a clone of the prog,
which shares the aux data with the prog. The line info moves with the
blinded insns.
When the JIT then fails, the kernel goes back to the prog that isn't
blinded and runs it in the interpreter (CONFIG_BPF_JIT_ALWAYS_ON=n). Its
line info keeps the offsets of the blinded insns. A prog of 4 insns with
a line info record at each insn, loaded with bpf_jit_harden=2 and an
allocation of the JIT failed through fail-nth:
xlated=4 insns linfo: 0 3 6 9
Keep the offsets from before blinding in aux->linfo_unblinded, and put
them back in bpf_jit_prog_release_other() when it keeps the prog that
isn't blinded. Free them either way.
Fixes: c454a46b5efd ("bpf: Add bpf_line_info support")
Reported-by: Alexei Starovoitov <ast@xxxxxxxxxx>
Closes: https://lore.kernel.org/bpf/DMKHZS5B5CNO.186F6YH1T6YU4@xxxxxxxxx/
Signed-off-by: Qiliang Yuan <odys.yuan@xxxxxxxxx>
---
include/linux/bpf.h | 6 ++++++
kernel/bpf/core.c | 29 ++++++++++++++++++++++++++++-
2 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index a80e81ca19ec1..83b3688f176e7 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1963,6 +1963,12 @@ struct bpf_prog_aux {
* using the linfo_idx.
*/
void **jited_linfo;
+ /*
+ * linfo->insn_off of the prog before constant blinding, while the
+ * blinded clone is JITed. The kernel puts it back when it falls back
+ * to the prog that isn't blinded.
+ */
+ u32 *linfo_unblinded;
u32 func_info_cnt;
u32 nr_linfo;
/* subprog can use linfo_idx to access its first linfo and
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 0586ed0203975..d45a82f1a42a5 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -1762,6 +1762,17 @@ static void bpf_prog_clone_free(struct bpf_prog *fp)
void bpf_jit_prog_release_other(struct bpf_prog *fp, struct bpf_prog *fp_other)
{
+ struct bpf_prog_aux *aux = fp->aux;
+ u32 i;
+
+ /* The line info moved with the blinded insns, move it back. */
+ if (aux->linfo_unblinded && !fp->blinded) {
+ for (i = 0; i < aux->nr_linfo; i++)
+ aux->linfo[i].insn_off = aux->linfo_unblinded[i];
+ }
+ kvfree(aux->linfo_unblinded);
+ aux->linfo_unblinded = NULL;
+
/* We have to repoint aux->prog to self, as we don't
* know whether fp here is the clone or the original.
*/
@@ -1808,6 +1819,7 @@ static struct bpf_prog *bpf_patch_list_apply(struct bpf_prog *prog, struct bpf_p
struct bpf_prog *bpf_jit_blind_constants(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
struct bpf_insn insn_buff[16], aux[2], *last;
+ u32 *linfo_unblinded = NULL, nr_linfo;
struct bpf_patch_list pl = {};
struct bpf_prog *clone, *tmp;
struct bpf_insn *insn;
@@ -1816,9 +1828,22 @@ struct bpf_prog *bpf_jit_blind_constants(struct bpf_verifier_env *env, struct bp
if (WARN_ON_ONCE(env && env->prog != prog))
return ERR_PTR(-EINVAL);
+ /* keep where the line info is, for a fallback to 'prog' */
+ nr_linfo = prog->aux->nr_linfo;
+ if (nr_linfo) {
+ linfo_unblinded = kvmalloc_array(nr_linfo, sizeof(*linfo_unblinded),
+ GFP_USER);
+ if (!linfo_unblinded)
+ return ERR_PTR(-ENOMEM);
+ for (i = 0; i < nr_linfo; i++)
+ linfo_unblinded[i] = prog->aux->linfo[i].insn_off;
+ }
+
clone = bpf_prog_clone_create(prog, GFP_USER);
- if (!clone)
+ if (!clone) {
+ kvfree(linfo_unblinded);
return ERR_PTR(-ENOMEM);
+ }
/* make sure the patches go to the clone */
if (env)
@@ -1883,11 +1908,13 @@ struct bpf_prog *bpf_jit_blind_constants(struct bpf_verifier_env *env, struct bp
* 'clone' is still valid.
*/
bpf_jit_prog_release_other(prog, clone);
+ kvfree(linfo_unblinded);
if (env && fatal_signal_pending(current))
return ERR_PTR(-EINTR);
return ERR_PTR(err);
}
+ tmp->aux->linfo_unblinded = linfo_unblinded;
tmp->blinded = 1;
return tmp;
}
--
2.43.0