]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
bpf: Drop pkt_end markers on arithmetic to prevent is_pkt_ptr_branch_taken
authorDaniel Borkmann <daniel@iogearbox.net>
Thu, 9 Apr 2026 15:50:15 +0000 (17:50 +0200)
committerAlexei Starovoitov <ast@kernel.org>
Thu, 9 Apr 2026 20:11:31 +0000 (13:11 -0700)
When a pkt pointer acquires AT_PKT_END or BEYOND_PKT_END range from
a comparison, and then, known-constant arithmetic is performed,
adjust_ptr_min_max_vals() copies the stale range via dst_reg->raw =
ptr_reg->raw without clearing the negative reg->range sentinel values.

This lets is_pkt_ptr_branch_taken() choose one branch direction and
skip going through the other. Fix this by clearing negative pkt range
values (that is, AT_PKT_END and BEYOND_PKT_END) after arithmetic on
pkt pointers. This ensures is_pkt_ptr_branch_taken() returns unknown
and both branches are properly verified.

Fixes: 6d94e741a8ff ("bpf: Support for pointers beyond pkt_end.")
Reported-by: STAR Labs SG <info@starlabs.sg>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/r/20260409155016.536608-1-daniel@iogearbox.net
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
kernel/bpf/verifier.c

index 1227b168bb0786969ee047cb005aec5a1d7d9c69..9c1135d373e246ea9cc6dfd97bf96664151a5270 100644 (file)
@@ -15448,10 +15448,17 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
                }
                dst_reg->var_off = tnum_add(ptr_reg->var_off, off_reg->var_off);
                dst_reg->raw = ptr_reg->raw;
-               if (!known && reg_is_pkt_pointer(ptr_reg)) {
-                       dst_reg->id = ++env->id_gen;
-                       /* something was added to pkt_ptr, set range to zero */
-                       memset(&dst_reg->raw, 0, sizeof(dst_reg->raw));
+               if (reg_is_pkt_pointer(ptr_reg)) {
+                       if (!known)
+                               dst_reg->id = ++env->id_gen;
+                       /*
+                        * Clear range for unknown addends since we can't know
+                        * where the pkt pointer ended up. Also clear AT_PKT_END /
+                        * BEYOND_PKT_END from prior comparison as any pointer
+                        * arithmetic invalidates them.
+                        */
+                       if (!known || dst_reg->range < 0)
+                               memset(&dst_reg->raw, 0, sizeof(dst_reg->raw));
                }
                break;
        case BPF_SUB:
@@ -15490,10 +15497,17 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
                }
                dst_reg->var_off = tnum_sub(ptr_reg->var_off, off_reg->var_off);
                dst_reg->raw = ptr_reg->raw;
-               if (!known && reg_is_pkt_pointer(ptr_reg)) {
-                       dst_reg->id = ++env->id_gen;
-                       /* something was added to pkt_ptr, set range to zero */
-                       if (smin_val < 0)
+               if (reg_is_pkt_pointer(ptr_reg)) {
+                       if (!known)
+                               dst_reg->id = ++env->id_gen;
+                       /*
+                        * Clear range if the subtrahend may be negative since
+                        * pkt pointer could move past its bounds. A positive
+                        * subtrahend moves it backwards keeping positive range
+                        * intact. Also clear AT_PKT_END / BEYOND_PKT_END from
+                        * prior comparison as arithmetic invalidates them.
+                        */
+                       if ((!known && smin_val < 0) || dst_reg->range < 0)
                                memset(&dst_reg->raw, 0, sizeof(dst_reg->raw));
                }
                break;