Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpf: test the proper verification of tail calls

Three tests are added:

- invalidate_pkt_pointers_by_tail_call checks that one can use the
packet pointer after a tail call. This was originally possible
and also poses not problems, but was made impossible by 1a4607ffba35.

- invalidate_pkt_pointers_by_static_tail_call tests a corner case
found by Eduard Zingerman during the discussion of the original fix,
which was broken in that fix.

- subprog_result_tail_call tests that precision propagation works
correctly across tail calls. This did not work before.

Signed-off-by: Martin Teichmann <martin.teichmann@xfel.eu>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20251119160355.1160932-3-martin.teichmann@xfel.eu
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Martin Teichmann and committed by
Alexei Starovoitov
978da762 e3245f89

+90 -2
+37 -2
tools/testing/selftests/bpf/progs/verifier_sock.c
··· 1117 1117 return 0; 1118 1118 } 1119 1119 1120 - /* Tail calls invalidate packet pointers. */ 1120 + static __noinline 1121 + int static_tail_call(struct __sk_buff *sk) 1122 + { 1123 + bpf_tail_call_static(sk, &jmp_table, 0); 1124 + return 0; 1125 + } 1126 + 1127 + /* Tail calls in sub-programs invalidate packet pointers. */ 1121 1128 SEC("tc") 1122 1129 __failure __msg("invalid mem access") 1123 - int invalidate_pkt_pointers_by_tail_call(struct __sk_buff *sk) 1130 + int invalidate_pkt_pointers_by_global_tail_call(struct __sk_buff *sk) 1124 1131 { 1125 1132 int *p = (void *)(long)sk->data; 1126 1133 ··· 1135 1128 return TCX_DROP; 1136 1129 tail_call(sk); 1137 1130 *p = 42; /* this is unsafe */ 1131 + return TCX_PASS; 1132 + } 1133 + 1134 + /* Tail calls in static sub-programs invalidate packet pointers. */ 1135 + SEC("tc") 1136 + __failure __msg("invalid mem access") 1137 + int invalidate_pkt_pointers_by_static_tail_call(struct __sk_buff *sk) 1138 + { 1139 + int *p = (void *)(long)sk->data; 1140 + 1141 + if ((void *)(p + 1) > (void *)(long)sk->data_end) 1142 + return TCX_DROP; 1143 + static_tail_call(sk); 1144 + *p = 42; /* this is unsafe */ 1145 + return TCX_PASS; 1146 + } 1147 + 1148 + /* Direct tail calls do not invalidate packet pointers. */ 1149 + SEC("tc") 1150 + __success 1151 + int invalidate_pkt_pointers_by_tail_call(struct __sk_buff *sk) 1152 + { 1153 + int *p = (void *)(long)sk->data; 1154 + 1155 + if ((void *)(p + 1) > (void *)(long)sk->data_end) 1156 + return TCX_DROP; 1157 + bpf_tail_call_static(sk, &jmp_table, 0); 1158 + *p = 42; /* this is NOT unsafe: tail calls don't return */ 1138 1159 return TCX_PASS; 1139 1160 } 1140 1161
+53
tools/testing/selftests/bpf/progs/verifier_subprog_precision.c
··· 793 793 ); 794 794 } 795 795 796 + struct { 797 + __uint(type, BPF_MAP_TYPE_PROG_ARRAY); 798 + __uint(max_entries, 1); 799 + __type(key, __u32); 800 + __type(value, __u32); 801 + } map_array SEC(".maps"); 802 + 803 + __naked __noinline __used 804 + static unsigned long identity_tail_call(void) 805 + { 806 + /* the simplest identity function involving a tail call */ 807 + asm volatile ( 808 + "r6 = r2;" 809 + "r2 = %[map_array] ll;" 810 + "r3 = 0;" 811 + "call %[bpf_tail_call];" 812 + "r0 = r6;" 813 + "exit;" 814 + : 815 + : __imm(bpf_tail_call), 816 + __imm_addr(map_array) 817 + : __clobber_all); 818 + } 819 + 820 + SEC("?raw_tp") 821 + __failure __log_level(2) 822 + __msg("13: (85) call bpf_tail_call#12") 823 + __msg("mark_precise: frame1: last_idx 13 first_idx 0 subseq_idx -1 ") 824 + __msg("returning from callee:") 825 + __msg("frame1: R0=scalar() R6=3 R10=fp0") 826 + __msg("to caller at 4:") 827 + __msg("R0=scalar() R6=map_value(map=.data.vals,ks=4,vs=16) R10=fp0") 828 + __msg("6: (0f) r1 += r0") 829 + __msg("mark_precise: frame0: regs=r0 stack= before 5: (bf) r1 = r6") 830 + __msg("mark_precise: frame0: regs=r0 stack= before 4: (27) r0 *= 4") 831 + __msg("mark_precise: frame0: parent state regs=r0 stack=: R0=Pscalar() R6=map_value(map=.data.vals,ks=4,vs=16) R10=fp0") 832 + __msg("math between map_value pointer and register with unbounded min value is not allowed") 833 + __naked int subprog_result_tail_call(void) 834 + { 835 + asm volatile ( 836 + "r2 = 3;" 837 + "call identity_tail_call;" 838 + "r0 *= 4;" 839 + "r1 = %[vals];" 840 + "r1 += r0;" 841 + "r0 = *(u32 *)(r1 + 0);" 842 + "exit;" 843 + : 844 + : __imm_ptr(vals) 845 + : __clobber_common 846 + ); 847 + } 848 + 796 849 char _license[] SEC("license") = "GPL";