xref: /linux/tools/testing/selftests/bpf/progs/file_reader.c (revision 2b5440b31cafad2fb4b4a83efb780a6d7430a385)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
3 
4 #include <vmlinux.h>
5 #include <string.h>
6 #include <stdbool.h>
7 #include <bpf/bpf_tracing.h>
8 #include "bpf_misc.h"
9 #include "errno.h"
10 
11 char _license[] SEC("license") = "GPL";
12 
13 struct {
14 	__uint(type, BPF_MAP_TYPE_ARRAY);
15 	__uint(max_entries, 1);
16 	__type(key, int);
17 	__type(value, struct elem);
18 } arrmap SEC(".maps");
19 
20 struct elem {
21 	struct file *file;
22 	struct bpf_task_work tw;
23 };
24 
25 char user_buf[256000];
26 char tmp_buf[256000];
27 
28 int pid = 0;
29 int err, run_success = 0;
30 __u64 beyond_eof_offset;
31 
32 static int validate_file_read(struct file *file);
33 static int task_work_callback(struct bpf_map *map, void *key, void *value);
34 static int sleepable_second_callback(struct bpf_map *map, void *key, void *value);
35 
36 void bpf_rcu_read_lock(void) __ksym;
37 void bpf_rcu_read_unlock(void) __ksym;
38 
39 SEC("lsm/file_open")
40 int on_open_expect_fault(void *c)
41 {
42 	struct bpf_dynptr dynptr;
43 	struct file *file;
44 	int local_err = 1;
45 	__u32 user_buf_sz = sizeof(user_buf);
46 
47 	if (bpf_get_current_pid_tgid() >> 32 != pid)
48 		return 0;
49 
50 	file = bpf_get_task_exe_file(bpf_get_current_task_btf());
51 	if (!file)
52 		return 0;
53 
54 	if (bpf_dynptr_from_file(file, 0, &dynptr))
55 		goto out;
56 
57 	local_err = bpf_dynptr_read(tmp_buf, user_buf_sz, &dynptr, user_buf_sz, 0);
58 	if (local_err == -EFAULT || local_err == 0) { /* Expect page fault or success */
59 		local_err = 0;
60 		run_success = 1;
61 	}
62 out:
63 	bpf_dynptr_file_discard(&dynptr);
64 	if (local_err)
65 		err = local_err;
66 	bpf_put_file(file);
67 	return 0;
68 }
69 
70 SEC("lsm/file_open")
71 int on_open_validate_file_read(void *c)
72 {
73 	struct task_struct *task = bpf_get_current_task_btf();
74 	struct elem *work;
75 	int key = 0;
76 
77 	if (bpf_get_current_pid_tgid() >> 32 != pid)
78 		return 0;
79 
80 	work = bpf_map_lookup_elem(&arrmap, &key);
81 	if (!work) {
82 		err = 1;
83 		return 0;
84 	}
85 	bpf_task_work_schedule_signal(task, &work->tw, &arrmap, task_work_callback);
86 	return 0;
87 }
88 
89 /*
90  * Exercise bpf_dynptr_from_file() first from a non-sleepable LSM program and
91  * then from its sleepable task-work callback. Reading beyond EOF makes the two
92  * backing implementations return different errors.
93  */
94 SEC("lsm/file_open")
95 int on_open_non_sleepable_first(void *c)
96 {
97 	struct task_struct *task = bpf_get_current_task_btf();
98 	struct bpf_dynptr dynptr;
99 	struct elem *work;
100 	struct file *file;
101 	int key = 0;
102 	int ret;
103 
104 	if (bpf_get_current_pid_tgid() >> 32 != pid)
105 		return 0;
106 
107 	file = bpf_get_task_exe_file(task);
108 	if (!file) {
109 		err = 1;
110 		return 0;
111 	}
112 
113 	/* The non-sleepable reader cannot fault in an uncached folio. */
114 	ret = bpf_dynptr_from_file(file, 0, &dynptr);
115 	if (!ret)
116 		ret = bpf_dynptr_read(tmp_buf, 1, &dynptr, beyond_eof_offset, 0);
117 	bpf_dynptr_file_discard(&dynptr);
118 	bpf_put_file(file);
119 	if (ret != -EFAULT) {
120 		err = 2;
121 		return 0;
122 	}
123 
124 	work = bpf_map_lookup_elem(&arrmap, &key);
125 	if (!work) {
126 		err = 3;
127 		return 0;
128 	}
129 
130 	ret = bpf_task_work_schedule_signal(task, &work->tw, &arrmap,
131 					    sleepable_second_callback);
132 	if (ret)
133 		err = 4;
134 	return 0;
135 }
136 
137 /*
138  * Exercise the opposite fixup order: the first call is made from a sleepable
139  * LSM program, while the RCU read-side section makes the second non-sleepable.
140  */
141 SEC("lsm.s/file_open")
142 int on_open_sleepable_first(void *c)
143 {
144 	struct task_struct *task = bpf_get_current_task_btf();
145 	struct bpf_dynptr dynptr;
146 	struct file *file;
147 	int ret;
148 
149 	if (bpf_get_current_pid_tgid() >> 32 != pid)
150 		return 0;
151 
152 	file = bpf_get_task_exe_file(task);
153 	if (!file) {
154 		err = 7;
155 		return 0;
156 	}
157 
158 	ret = bpf_dynptr_from_file(file, 0, &dynptr);
159 	if (!ret)
160 		ret = bpf_dynptr_read(tmp_buf, 1, &dynptr, beyond_eof_offset, 0);
161 	bpf_dynptr_file_discard(&dynptr);
162 	if (ret != -EIO) {
163 		err = 8;
164 		goto out;
165 	}
166 
167 	bpf_rcu_read_lock();
168 	ret = bpf_dynptr_from_file(file, 0, &dynptr);
169 	bpf_rcu_read_unlock();
170 	if (!ret)
171 		ret = bpf_dynptr_read(tmp_buf, 1, &dynptr, beyond_eof_offset, 0);
172 	bpf_dynptr_file_discard(&dynptr);
173 	if (ret != -EFAULT) {
174 		err = 9;
175 		goto out;
176 	}
177 
178 	run_success = 1;
179 out:
180 	bpf_put_file(file);
181 	return 0;
182 }
183 
184 /* Called in a sleepable context, read 256K bytes, cross check with user space read data */
185 static int task_work_callback(struct bpf_map *map, void *key, void *value)
186 {
187 	struct task_struct *task = bpf_get_current_task_btf();
188 	struct file *file = bpf_get_task_exe_file(task);
189 
190 	if (!file)
191 		return 0;
192 
193 	err = validate_file_read(file);
194 	if (!err)
195 		run_success = 1;
196 	bpf_put_file(file);
197 	return 0;
198 }
199 
200 /* Task-work callbacks are verified as sleepable. */
201 static int sleepable_second_callback(struct bpf_map *map, void *key, void *value)
202 {
203 	struct task_struct *task = bpf_get_current_task_btf();
204 	struct bpf_dynptr dynptr;
205 	struct file *file;
206 	int ret;
207 
208 	file = bpf_get_task_exe_file(task);
209 	if (!file) {
210 		err = 5;
211 		return 0;
212 	}
213 
214 	/* freader_fetch() converts __kernel_read()'s short read at EOF to -EIO. */
215 	ret = bpf_dynptr_from_file(file, 0, &dynptr);
216 	if (!ret)
217 		ret = bpf_dynptr_read(tmp_buf, 1, &dynptr, beyond_eof_offset, 0);
218 	bpf_dynptr_file_discard(&dynptr);
219 	bpf_put_file(file);
220 	if (ret != -EIO) {
221 		err = 6;
222 		return 0;
223 	}
224 
225 	run_success = 1;
226 	return 0;
227 }
228 
229 static int verify_dynptr_read(struct bpf_dynptr *ptr, u32 off, char *user_buf, u32 len)
230 {
231 	int i;
232 
233 	if (bpf_dynptr_read(tmp_buf, len, ptr, off, 0))
234 		return 1;
235 
236 	/* Verify file contents read from BPF is the same as the one read from userspace */
237 	bpf_for(i, 0, len)
238 	{
239 		if (tmp_buf[i] != user_buf[i])
240 			return 1;
241 	}
242 	return 0;
243 }
244 
245 static int validate_file_read(struct file *file)
246 {
247 	struct bpf_dynptr dynptr;
248 	int loc_err = 1, off;
249 	__u32 user_buf_sz = sizeof(user_buf);
250 
251 	if (bpf_dynptr_from_file(file, 0, &dynptr))
252 		goto cleanup;
253 
254 	loc_err = verify_dynptr_read(&dynptr, 0, user_buf, user_buf_sz);
255 	off = 1;
256 	loc_err = loc_err ?: verify_dynptr_read(&dynptr, off, user_buf + off, user_buf_sz - off);
257 	off = user_buf_sz - 1;
258 	loc_err = loc_err ?: verify_dynptr_read(&dynptr, off, user_buf + off, user_buf_sz - off);
259 	/* Read file with random offset and length */
260 	off = 4097;
261 	loc_err = loc_err ?: verify_dynptr_read(&dynptr, off, user_buf + off, 100);
262 
263 	/* Adjust dynptr, verify read */
264 	loc_err = loc_err ?: bpf_dynptr_adjust(&dynptr, off, off + 1);
265 	loc_err = loc_err ?: verify_dynptr_read(&dynptr, 0, user_buf + off, 1);
266 	/* Can't read more than 1 byte */
267 	loc_err = loc_err ?: verify_dynptr_read(&dynptr, 0, user_buf + off, 2) == 0;
268 	/* Can't read with far offset */
269 	loc_err = loc_err ?: verify_dynptr_read(&dynptr, 1, user_buf + off, 1) == 0;
270 
271 cleanup:
272 	bpf_dynptr_file_discard(&dynptr);
273 	return loc_err;
274 }
275