xref: /linux/tools/testing/selftests/bpf/progs/kfunc_call_fail.c (revision 5763790965eb3414148720f030b20fd5ccc438ca)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2021 Facebook */
3 #include <vmlinux.h>
4 #include <bpf/bpf_helpers.h>
5 #include "../test_kmods/bpf_testmod_kfunc.h"
6 
7 struct syscall_test_args {
8 	__u8 data[16];
9 	size_t size;
10 };
11 
12 SEC("?syscall")
13 int kfunc_syscall_test_fail(struct syscall_test_args *args)
14 {
15 	bpf_kfunc_call_test_mem_len_pass1(&args->data, sizeof(*args) + 1);
16 
17 	return 0;
18 }
19 
20 SEC("?syscall")
21 int kfunc_syscall_test_null_fail(struct syscall_test_args *args)
22 {
23 	/* Must be called with args as a NULL pointer
24 	 * we do not check for it to have the verifier consider that
25 	 * the pointer might not be null, and so we can load it.
26 	 *
27 	 * So the following can not be added:
28 	 *
29 	 * if (args)
30 	 *      return -22;
31 	 */
32 
33 	bpf_kfunc_call_test_mem_len_pass1(args, sizeof(*args));
34 
35 	return 0;
36 }
37 
38 SEC("?tc")
39 int kfunc_call_test_get_mem_fail_rdonly(struct __sk_buff *skb)
40 {
41 	struct prog_test_ref_kfunc *pt;
42 	unsigned long s = 0;
43 	int *p = NULL;
44 	int ret = 0;
45 
46 	pt = bpf_kfunc_call_test_acquire(&s);
47 	if (pt) {
48 		p = bpf_kfunc_call_test_get_rdonly_mem(pt, 2 * sizeof(int));
49 		if (p)
50 			p[0] = 42; /* this is a read-only buffer, so -EACCES */
51 		else
52 			ret = -1;
53 
54 		bpf_kfunc_call_test_release(pt);
55 	}
56 	return ret;
57 }
58 
59 SEC("?tc")
60 int kfunc_call_test_get_mem_fail_use_after_free(struct __sk_buff *skb)
61 {
62 	struct prog_test_ref_kfunc *pt;
63 	unsigned long s = 0;
64 	int *p = NULL;
65 	int ret = 0;
66 
67 	pt = bpf_kfunc_call_test_acquire(&s);
68 	if (pt) {
69 		p = bpf_kfunc_call_test_get_rdwr_mem(pt, 2 * sizeof(int));
70 		if (p) {
71 			p[0] = 42;
72 			ret = p[1]; /* 108 */
73 		} else {
74 			ret = -1;
75 		}
76 
77 		bpf_kfunc_call_test_release(pt);
78 	}
79 	if (p)
80 		ret = p[0]; /* p is not valid anymore */
81 
82 	return ret;
83 }
84 
85 SEC("?tc")
86 int kfunc_call_test_get_mem_fail_oob(struct __sk_buff *skb)
87 {
88 	struct prog_test_ref_kfunc *pt;
89 	unsigned long s = 0;
90 	int *p = NULL;
91 	int ret = 0;
92 
93 	pt = bpf_kfunc_call_test_acquire(&s);
94 	if (pt) {
95 		p = bpf_kfunc_call_test_get_rdonly_mem(pt, 2 * sizeof(int));
96 		if (p)
97 			ret = p[2 * sizeof(int)]; /* oob access, so -EACCES */
98 		else
99 			ret = -1;
100 
101 		bpf_kfunc_call_test_release(pt);
102 	}
103 	return ret;
104 }
105 
106 SEC("?tc")
107 int kfunc_call_test_get_mem_fail_zero_size(struct __sk_buff *skb)
108 {
109 	struct prog_test_ref_kfunc *pt;
110 	unsigned long s = 0;
111 	int *p = NULL;
112 	int ret = 0;
113 
114 	pt = bpf_kfunc_call_test_acquire(&s);
115 	if (pt) {
116 		/*
117 		 * An explicit rdwr_buf_size of 0 gives R0 a zero-sized buffer,
118 		 * so any access is out of bounds, hence -EACCES. Previously the
119 		 * verifier treated a zero size as "no size argument" and sized
120 		 * R0 after the pointed-to return type, wrongly allowing the read.
121 		 */
122 		p = bpf_kfunc_call_test_get_rdwr_mem(pt, 0);
123 		if (p)
124 			ret = p[0];
125 		else
126 			ret = -1;
127 
128 		bpf_kfunc_call_test_release(pt);
129 	}
130 	return ret;
131 }
132 
133 SEC("?tc")
134 int kfunc_call_test_get_mem_fail_oversized(struct __sk_buff *skb)
135 {
136 	struct prog_test_ref_kfunc *pt;
137 	unsigned long s = 0;
138 	int *p = NULL;
139 	int ret = 0;
140 
141 	pt = bpf_kfunc_call_test_acquire(&s);
142 	if (pt) {
143 		/*
144 		 * rdwr_buf_size is a const int, so a C literal is narrowed to
145 		 * 32 bits before the call. Force the full 64-bit value 2^64 - 192
146 		 * (0xffffffffffffff40, > U32_MAX) into the argument register with
147 		 * a 64-bit immediate load. The verifier records r0_size from the
148 		 * full register value and must reject it before that value is
149 		 * truncated into R0's u32 mem_size.
150 		 */
151 		asm volatile (
152 			"r1 = %[pt];"
153 			"r2 = %[oversized] ll;"
154 			"call %[get_rdwr_mem];"
155 			"%[p] = r0;"
156 			: [p] "=r"(p)
157 			: [pt] "r"(pt),
158 			  [oversized] "i"(0xffffffffffffff40LL),
159 			  [get_rdwr_mem] "i"(bpf_kfunc_call_test_get_rdwr_mem)
160 			: "r0", "r1", "r2", "r3", "r4", "r5");
161 		bpf_kfunc_call_test_release(pt);
162 	}
163 	return ret;
164 }
165 
166 int not_const_size = 2 * sizeof(int);
167 
168 SEC("?tc")
169 int kfunc_call_test_get_mem_fail_not_const(struct __sk_buff *skb)
170 {
171 	struct prog_test_ref_kfunc *pt;
172 	unsigned long s = 0;
173 	int *p = NULL;
174 	int ret = 0;
175 
176 	pt = bpf_kfunc_call_test_acquire(&s);
177 	if (pt) {
178 		p = bpf_kfunc_call_test_get_rdonly_mem(pt, not_const_size); /* non const size, -EINVAL */
179 		if (p)
180 			ret = p[0];
181 		else
182 			ret = -1;
183 
184 		bpf_kfunc_call_test_release(pt);
185 	}
186 	return ret;
187 }
188 
189 SEC("?tc")
190 int kfunc_call_test_mem_acquire_fail(struct __sk_buff *skb)
191 {
192 	struct prog_test_ref_kfunc *pt;
193 	unsigned long s = 0;
194 	int *p = NULL;
195 	int ret = 0;
196 
197 	pt = bpf_kfunc_call_test_acquire(&s);
198 	if (pt) {
199 		/* we are failing on this one, because we are not acquiring a PTR_TO_BTF_ID (a struct ptr) */
200 		p = bpf_kfunc_call_test_acq_rdonly_mem(pt, 2 * sizeof(int));
201 		if (p)
202 			ret = p[0];
203 		else
204 			ret = -1;
205 
206 		bpf_kfunc_call_int_mem_release(p);
207 
208 		bpf_kfunc_call_test_release(pt);
209 	}
210 	return ret;
211 }
212 
213 SEC("?tc")
214 int kfunc_call_test_pointer_arg_type_mismatch(struct __sk_buff *skb)
215 {
216 	bpf_kfunc_call_test_pass_ctx((void *)10);
217 	return 0;
218 }
219 
220 char _license[] SEC("license") = "GPL";
221