summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexei Starovoitov <ast@kernel.org>2026-01-16 21:08:59 +0300
committerAlexei Starovoitov <ast@kernel.org>2026-01-16 21:08:59 +0300
commit2acbd053c8463c005a0671b2d14a78f38be77c5c (patch)
treecb171dde5e450288a3fd77a160ee63c3cd149208
parent934d9746ed0206e93506a68c838fe82ef748576a (diff)
parent086c99fbe45070d02851427eab5ae26fe7d0f3c0 (diff)
downloadlinux-2acbd053c8463c005a0671b2d14a78f38be77c5c.tar.xz
Merge branch 'bpf-fix-linked-register-tracking'
Puranjay Mohan says: ==================== bpf: Fix linked register tracking This patch fixes the linked register tracking when multiple links from the same register are created with a sync between the creation of these links. The sync corrupts the id of the register and therefore the second link is not created properly. See the patch description to understand more. The fix is to preserve the id while doing the sync similar to the off. ==================== Link: https://patch.msgid.link/20260115151143.1344724-1-puranjay@kernel.org Signed-off-by: Alexei Starovoitov <ast@kernel.org>
-rw-r--r--kernel/bpf/verifier.c4
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_linked_scalars.c33
2 files changed, 36 insertions, 1 deletions
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 7a375f608263..9de0ec0c3ed9 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -16871,6 +16871,7 @@ static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_s
} else {
s32 saved_subreg_def = reg->subreg_def;
s32 saved_off = reg->off;
+ u32 saved_id = reg->id;
fake_reg.type = SCALAR_VALUE;
__mark_reg_known(&fake_reg, (s32)reg->off - (s32)known_reg->off);
@@ -16878,10 +16879,11 @@ static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_s
/* reg = known_reg; reg += delta */
copy_register_state(reg, known_reg);
/*
- * Must preserve off, id and add_const flag,
+ * Must preserve off, id and subreg_def flag,
* otherwise another sync_linked_regs() will be incorrect.
*/
reg->off = saved_off;
+ reg->id = saved_id;
reg->subreg_def = saved_subreg_def;
scalar32_min_max_add(reg, &fake_reg);
diff --git a/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c b/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c
index 8f755d2464cf..5f41bbb730a7 100644
--- a/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c
+++ b/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c
@@ -31,4 +31,37 @@ l1: \
" ::: __clobber_all);
}
+/*
+ * Test that sync_linked_regs() preserves register IDs.
+ *
+ * The sync_linked_regs() function copies bounds from known_reg to linked
+ * registers. When doing so, it must preserve each register's original id
+ * to allow subsequent syncs from the same source to work correctly.
+ *
+ */
+SEC("socket")
+__success
+__naked void sync_linked_regs_preserves_id(void)
+{
+ asm volatile (" \
+ call %[bpf_get_prandom_u32]; \
+ r0 &= 0xff; /* r0 in [0, 255] */ \
+ r1 = r0; /* r0, r1 linked with id 1 */ \
+ r1 += 4; /* r1 has id=1 and off=4 in [4, 259] */ \
+ if r1 < 10 goto l0_%=; \
+ /* r1 in [10, 259], r0 synced to [6, 255] */ \
+ r2 = r0; /* r2 has id=1 and in [6, 255] */ \
+ if r1 < 14 goto l0_%=; \
+ /* r1 in [14, 259], r0 synced to [10, 255] */ \
+ if r0 >= 10 goto l0_%=; \
+ /* Never executed */ \
+ r0 /= 0; \
+l0_%=: \
+ r0 = 0; \
+ exit; \
+" :
+ : __imm(bpf_get_prandom_u32)
+ : __clobber_all);
+}
+
char _license[] SEC("license") = "GPL";