1// SPDX-License-Identifier: GPL-2.0 2/* Converted from tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c */ 3 4#include <linux/bpf.h> 5#include <bpf/bpf_helpers.h> 6#include "bpf_misc.h" 7 8struct { 9 __uint(type, BPF_MAP_TYPE_XSKMAP); 10 __uint(max_entries, 1); 11 __type(key, int); 12 __type(value, int); 13} map_xskmap SEC(".maps"); 14 15/* This is equivalent to the following program: 16 * 17 * r6 = skb->sk; 18 * r7 = sk_fullsock(r6); 19 * r0 = sk_fullsock(r6); 20 * if (r0 == 0) return 0; (a) 21 * if (r0 != r7) return 0; (b) 22 * *r7->type; (c) 23 * return 0; 24 * 25 * It is safe to dereference r7 at point (c), because of (a) and (b). 26 * The test verifies that relation r0 == r7 is propagated from (b) to (c). 27 */ 28SEC("cgroup/skb") 29__description("jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL -> PTR_TO_SOCKET for JNE false branch") 30__success __failure_unpriv __msg_unpriv("R7 pointer comparison") 31__retval(0) 32__naked void socket_for_jne_false_branch(void) 33{ 34 asm volatile (" \ 35 /* r6 = skb->sk; */ \ 36 r6 = *(u64*)(r1 + %[__sk_buff_sk]); \ 37 /* if (r6 == 0) return 0; */ \ 38 if r6 == 0 goto l0_%=; \ 39 /* r7 = sk_fullsock(skb); */ \ 40 r1 = r6; \ 41 call %[bpf_sk_fullsock]; \ 42 r7 = r0; \ 43 /* r0 = sk_fullsock(skb); */ \ 44 r1 = r6; \ 45 call %[bpf_sk_fullsock]; \ 46 /* if (r0 == null) return 0; */ \ 47 if r0 == 0 goto l0_%=; \ 48 /* if (r0 == r7) r0 = *(r7->type); */ \ 49 if r0 != r7 goto l0_%=; /* Use ! JNE ! */\ 50 r0 = *(u32*)(r7 + %[bpf_sock_type]); \ 51l0_%=: /* return 0 */ \ 52 r0 = 0; \ 53 exit; \ 54" : 55 : __imm(bpf_sk_fullsock), 56 __imm_const(__sk_buff_sk, offsetof(struct __sk_buff, sk)), 57 __imm_const(bpf_sock_type, offsetof(struct bpf_sock, type)) 58 : __clobber_all); 59} 60 61/* Same as above, but verify that another branch of JNE still 62 * prohibits access to PTR_MAYBE_NULL. 63 */ 64SEC("cgroup/skb") 65__description("jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL unchanged for JNE true branch") 66__failure __msg("R7 invalid mem access 'sock_or_null'") 67__failure_unpriv __msg_unpriv("R7 pointer comparison") 68__naked void unchanged_for_jne_true_branch(void) 69{ 70 asm volatile (" \ 71 /* r6 = skb->sk */ \ 72 r6 = *(u64*)(r1 + %[__sk_buff_sk]); \ 73 /* if (r6 == 0) return 0; */ \ 74 if r6 == 0 goto l0_%=; \ 75 /* r7 = sk_fullsock(skb); */ \ 76 r1 = r6; \ 77 call %[bpf_sk_fullsock]; \ 78 r7 = r0; \ 79 /* r0 = sk_fullsock(skb); */ \ 80 r1 = r6; \ 81 call %[bpf_sk_fullsock]; \ 82 /* if (r0 == null) return 0; */ \ 83 if r0 != 0 goto l0_%=; \ 84 /* if (r0 == r7) return 0; */ \ 85 if r0 != r7 goto l1_%=; /* Use ! JNE ! */\ 86 goto l0_%=; \ 87l1_%=: /* r0 = *(r7->type); */ \ 88 r0 = *(u32*)(r7 + %[bpf_sock_type]); \ 89l0_%=: /* return 0 */ \ 90 r0 = 0; \ 91 exit; \ 92" : 93 : __imm(bpf_sk_fullsock), 94 __imm_const(__sk_buff_sk, offsetof(struct __sk_buff, sk)), 95 __imm_const(bpf_sock_type, offsetof(struct bpf_sock, type)) 96 : __clobber_all); 97} 98 99/* Same as a first test, but not null should be inferred for JEQ branch */ 100SEC("cgroup/skb") 101__description("jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL -> PTR_TO_SOCKET for JEQ true branch") 102__success __failure_unpriv __msg_unpriv("R7 pointer comparison") 103__retval(0) 104__naked void socket_for_jeq_true_branch(void) 105{ 106 asm volatile (" \ 107 /* r6 = skb->sk; */ \ 108 r6 = *(u64*)(r1 + %[__sk_buff_sk]); \ 109 /* if (r6 == null) return 0; */ \ 110 if r6 == 0 goto l0_%=; \ 111 /* r7 = sk_fullsock(skb); */ \ 112 r1 = r6; \ 113 call %[bpf_sk_fullsock]; \ 114 r7 = r0; \ 115 /* r0 = sk_fullsock(skb); */ \ 116 r1 = r6; \ 117 call %[bpf_sk_fullsock]; \ 118 /* if (r0 == null) return 0; */ \ 119 if r0 == 0 goto l0_%=; \ 120 /* if (r0 != r7) return 0; */ \ 121 if r0 == r7 goto l1_%=; /* Use ! JEQ ! */\ 122 goto l0_%=; \ 123l1_%=: /* r0 = *(r7->type); */ \ 124 r0 = *(u32*)(r7 + %[bpf_sock_type]); \ 125l0_%=: /* return 0; */ \ 126 r0 = 0; \ 127 exit; \ 128" : 129 : __imm(bpf_sk_fullsock), 130 __imm_const(__sk_buff_sk, offsetof(struct __sk_buff, sk)), 131 __imm_const(bpf_sock_type, offsetof(struct bpf_sock, type)) 132 : __clobber_all); 133} 134 135/* Same as above, but verify that another branch of JNE still 136 * prohibits access to PTR_MAYBE_NULL. 137 */ 138SEC("cgroup/skb") 139__description("jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL unchanged for JEQ false branch") 140__failure __msg("R7 invalid mem access 'sock_or_null'") 141__failure_unpriv __msg_unpriv("R7 pointer comparison") 142__naked void unchanged_for_jeq_false_branch(void) 143{ 144 asm volatile (" \ 145 /* r6 = skb->sk; */ \ 146 r6 = *(u64*)(r1 + %[__sk_buff_sk]); \ 147 /* if (r6 == null) return 0; */ \ 148 if r6 == 0 goto l0_%=; \ 149 /* r7 = sk_fullsock(skb); */ \ 150 r1 = r6; \ 151 call %[bpf_sk_fullsock]; \ 152 r7 = r0; \ 153 /* r0 = sk_fullsock(skb); */ \ 154 r1 = r6; \ 155 call %[bpf_sk_fullsock]; \ 156 /* if (r0 == null) return 0; */ \ 157 if r0 == 0 goto l0_%=; \ 158 /* if (r0 != r7) r0 = *(r7->type); */ \ 159 if r0 == r7 goto l0_%=; /* Use ! JEQ ! */\ 160 r0 = *(u32*)(r7 + %[bpf_sock_type]); \ 161l0_%=: /* return 0; */ \ 162 r0 = 0; \ 163 exit; \ 164" : 165 : __imm(bpf_sk_fullsock), 166 __imm_const(__sk_buff_sk, offsetof(struct __sk_buff, sk)), 167 __imm_const(bpf_sock_type, offsetof(struct bpf_sock, type)) 168 : __clobber_all); 169} 170 171/* Maps are treated in a different branch of `mark_ptr_not_null_reg`, 172 * so separate test for maps case. 173 */ 174SEC("xdp") 175__description("jne/jeq infer not null, PTR_TO_MAP_VALUE_OR_NULL -> PTR_TO_MAP_VALUE") 176__success __retval(0) 177__naked void null_ptr_to_map_value(void) 178{ 179 asm volatile (" \ 180 /* r9 = &some stack to use as key */ \ 181 r1 = 0; \ 182 *(u32*)(r10 - 8) = r1; \ 183 r9 = r10; \ 184 r9 += -8; \ 185 /* r8 = process local map */ \ 186 r8 = %[map_xskmap] ll; \ 187 /* r6 = map_lookup_elem(r8, r9); */ \ 188 r1 = r8; \ 189 r2 = r9; \ 190 call %[bpf_map_lookup_elem]; \ 191 r6 = r0; \ 192 /* r7 = map_lookup_elem(r8, r9); */ \ 193 r1 = r8; \ 194 r2 = r9; \ 195 call %[bpf_map_lookup_elem]; \ 196 r7 = r0; \ 197 /* if (r6 == 0) return 0; */ \ 198 if r6 == 0 goto l0_%=; \ 199 /* if (r6 != r7) return 0; */ \ 200 if r6 != r7 goto l0_%=; \ 201 /* read *r7; */ \ 202 r0 = *(u32*)(r7 + %[bpf_xdp_sock_queue_id]); \ 203l0_%=: /* return 0; */ \ 204 r0 = 0; \ 205 exit; \ 206" : 207 : __imm(bpf_map_lookup_elem), 208 __imm_addr(map_xskmap), 209 __imm_const(bpf_xdp_sock_queue_id, offsetof(struct bpf_xdp_sock, queue_id)) 210 : __clobber_all); 211} 212 213char _license[] SEC("license") = "GPL"; 214