xref: /linux/tools/testing/selftests/bpf/progs/file_reader.c (revision c17ee635fd3a482b2ad2bf5e269755c2eae5f25e)
1784cdf93SMykyta Yatsenko // SPDX-License-Identifier: GPL-2.0
2784cdf93SMykyta Yatsenko /* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
3784cdf93SMykyta Yatsenko 
4784cdf93SMykyta Yatsenko #include <vmlinux.h>
5784cdf93SMykyta Yatsenko #include <string.h>
6784cdf93SMykyta Yatsenko #include <stdbool.h>
7784cdf93SMykyta Yatsenko #include <bpf/bpf_tracing.h>
8784cdf93SMykyta Yatsenko #include "bpf_misc.h"
9784cdf93SMykyta Yatsenko #include "errno.h"
10784cdf93SMykyta Yatsenko 
11784cdf93SMykyta Yatsenko char _license[] SEC("license") = "GPL";
12784cdf93SMykyta Yatsenko 
13784cdf93SMykyta Yatsenko struct {
14784cdf93SMykyta Yatsenko 	__uint(type, BPF_MAP_TYPE_ARRAY);
15784cdf93SMykyta Yatsenko 	__uint(max_entries, 1);
16784cdf93SMykyta Yatsenko 	__type(key, int);
17784cdf93SMykyta Yatsenko 	__type(value, struct elem);
18784cdf93SMykyta Yatsenko } arrmap SEC(".maps");
19784cdf93SMykyta Yatsenko 
20784cdf93SMykyta Yatsenko struct elem {
21784cdf93SMykyta Yatsenko 	struct file *file;
22784cdf93SMykyta Yatsenko 	struct bpf_task_work tw;
23784cdf93SMykyta Yatsenko };
24784cdf93SMykyta Yatsenko 
25784cdf93SMykyta Yatsenko char user_buf[256000];
26784cdf93SMykyta Yatsenko char tmp_buf[256000];
27784cdf93SMykyta Yatsenko 
28784cdf93SMykyta Yatsenko int pid = 0;
29784cdf93SMykyta Yatsenko int err, run_success = 0;
30784cdf93SMykyta Yatsenko 
31784cdf93SMykyta Yatsenko static int validate_file_read(struct file *file);
32784cdf93SMykyta Yatsenko static int task_work_callback(struct bpf_map *map, void *key, void *value);
33784cdf93SMykyta Yatsenko 
34784cdf93SMykyta Yatsenko SEC("lsm/file_open")
35784cdf93SMykyta Yatsenko int on_open_expect_fault(void *c)
36784cdf93SMykyta Yatsenko {
37784cdf93SMykyta Yatsenko 	struct bpf_dynptr dynptr;
38784cdf93SMykyta Yatsenko 	struct file *file;
39784cdf93SMykyta Yatsenko 	int local_err = 1;
40784cdf93SMykyta Yatsenko 	__u32 user_buf_sz = sizeof(user_buf);
41784cdf93SMykyta Yatsenko 
42784cdf93SMykyta Yatsenko 	if (bpf_get_current_pid_tgid() >> 32 != pid)
43784cdf93SMykyta Yatsenko 		return 0;
44784cdf93SMykyta Yatsenko 
45784cdf93SMykyta Yatsenko 	file = bpf_get_task_exe_file(bpf_get_current_task_btf());
46784cdf93SMykyta Yatsenko 	if (!file)
47784cdf93SMykyta Yatsenko 		return 0;
48784cdf93SMykyta Yatsenko 
49784cdf93SMykyta Yatsenko 	if (bpf_dynptr_from_file(file, 0, &dynptr))
50784cdf93SMykyta Yatsenko 		goto out;
51784cdf93SMykyta Yatsenko 
525913e936SMykyta Yatsenko 	local_err = bpf_dynptr_read(tmp_buf, user_buf_sz, &dynptr, user_buf_sz, 0);
53784cdf93SMykyta Yatsenko 	if (local_err == -EFAULT) { /* Expect page fault */
54784cdf93SMykyta Yatsenko 		local_err = 0;
55784cdf93SMykyta Yatsenko 		run_success = 1;
56784cdf93SMykyta Yatsenko 	}
57784cdf93SMykyta Yatsenko out:
58784cdf93SMykyta Yatsenko 	bpf_dynptr_file_discard(&dynptr);
59784cdf93SMykyta Yatsenko 	if (local_err)
60784cdf93SMykyta Yatsenko 		err = local_err;
61784cdf93SMykyta Yatsenko 	bpf_put_file(file);
62784cdf93SMykyta Yatsenko 	return 0;
63784cdf93SMykyta Yatsenko }
64784cdf93SMykyta Yatsenko 
65784cdf93SMykyta Yatsenko SEC("lsm/file_open")
66784cdf93SMykyta Yatsenko int on_open_validate_file_read(void *c)
67784cdf93SMykyta Yatsenko {
68784cdf93SMykyta Yatsenko 	struct task_struct *task = bpf_get_current_task_btf();
69784cdf93SMykyta Yatsenko 	struct elem *work;
70784cdf93SMykyta Yatsenko 	int key = 0;
71784cdf93SMykyta Yatsenko 
72784cdf93SMykyta Yatsenko 	if (bpf_get_current_pid_tgid() >> 32 != pid)
73784cdf93SMykyta Yatsenko 		return 0;
74784cdf93SMykyta Yatsenko 
75784cdf93SMykyta Yatsenko 	work = bpf_map_lookup_elem(&arrmap, &key);
76784cdf93SMykyta Yatsenko 	if (!work) {
77784cdf93SMykyta Yatsenko 		err = 1;
78784cdf93SMykyta Yatsenko 		return 0;
79784cdf93SMykyta Yatsenko 	}
80*6e663ffdSIhor Solodrai 	bpf_task_work_schedule_signal(task, &work->tw, &arrmap, task_work_callback);
81784cdf93SMykyta Yatsenko 	return 0;
82784cdf93SMykyta Yatsenko }
83784cdf93SMykyta Yatsenko 
84784cdf93SMykyta Yatsenko /* Called in a sleepable context, read 256K bytes, cross check with user space read data */
85784cdf93SMykyta Yatsenko static int task_work_callback(struct bpf_map *map, void *key, void *value)
86784cdf93SMykyta Yatsenko {
87784cdf93SMykyta Yatsenko 	struct task_struct *task = bpf_get_current_task_btf();
88784cdf93SMykyta Yatsenko 	struct file *file = bpf_get_task_exe_file(task);
89784cdf93SMykyta Yatsenko 
90784cdf93SMykyta Yatsenko 	if (!file)
91784cdf93SMykyta Yatsenko 		return 0;
92784cdf93SMykyta Yatsenko 
93784cdf93SMykyta Yatsenko 	err = validate_file_read(file);
94784cdf93SMykyta Yatsenko 	if (!err)
95784cdf93SMykyta Yatsenko 		run_success = 1;
96784cdf93SMykyta Yatsenko 	bpf_put_file(file);
97784cdf93SMykyta Yatsenko 	return 0;
98784cdf93SMykyta Yatsenko }
99784cdf93SMykyta Yatsenko 
100784cdf93SMykyta Yatsenko static int verify_dynptr_read(struct bpf_dynptr *ptr, u32 off, char *user_buf, u32 len)
101784cdf93SMykyta Yatsenko {
102784cdf93SMykyta Yatsenko 	int i;
103784cdf93SMykyta Yatsenko 
104784cdf93SMykyta Yatsenko 	if (bpf_dynptr_read(tmp_buf, len, ptr, off, 0))
105784cdf93SMykyta Yatsenko 		return 1;
106784cdf93SMykyta Yatsenko 
107784cdf93SMykyta Yatsenko 	/* Verify file contents read from BPF is the same as the one read from userspace */
108784cdf93SMykyta Yatsenko 	bpf_for(i, 0, len)
109784cdf93SMykyta Yatsenko 	{
110784cdf93SMykyta Yatsenko 		if (tmp_buf[i] != user_buf[i])
111784cdf93SMykyta Yatsenko 			return 1;
112784cdf93SMykyta Yatsenko 	}
113784cdf93SMykyta Yatsenko 	return 0;
114784cdf93SMykyta Yatsenko }
115784cdf93SMykyta Yatsenko 
116784cdf93SMykyta Yatsenko static int validate_file_read(struct file *file)
117784cdf93SMykyta Yatsenko {
118784cdf93SMykyta Yatsenko 	struct bpf_dynptr dynptr;
119784cdf93SMykyta Yatsenko 	int loc_err = 1, off;
120784cdf93SMykyta Yatsenko 	__u32 user_buf_sz = sizeof(user_buf);
121784cdf93SMykyta Yatsenko 
122784cdf93SMykyta Yatsenko 	if (bpf_dynptr_from_file(file, 0, &dynptr))
123784cdf93SMykyta Yatsenko 		goto cleanup;
124784cdf93SMykyta Yatsenko 
125784cdf93SMykyta Yatsenko 	loc_err = verify_dynptr_read(&dynptr, 0, user_buf, user_buf_sz);
126784cdf93SMykyta Yatsenko 	off = 1;
127784cdf93SMykyta Yatsenko 	loc_err = loc_err ?: verify_dynptr_read(&dynptr, off, user_buf + off, user_buf_sz - off);
128784cdf93SMykyta Yatsenko 	off = user_buf_sz - 1;
129784cdf93SMykyta Yatsenko 	loc_err = loc_err ?: verify_dynptr_read(&dynptr, off, user_buf + off, user_buf_sz - off);
130784cdf93SMykyta Yatsenko 	/* Read file with random offset and length */
131784cdf93SMykyta Yatsenko 	off = 4097;
132784cdf93SMykyta Yatsenko 	loc_err = loc_err ?: verify_dynptr_read(&dynptr, off, user_buf + off, 100);
133784cdf93SMykyta Yatsenko 
134784cdf93SMykyta Yatsenko 	/* Adjust dynptr, verify read */
135784cdf93SMykyta Yatsenko 	loc_err = loc_err ?: bpf_dynptr_adjust(&dynptr, off, off + 1);
136784cdf93SMykyta Yatsenko 	loc_err = loc_err ?: verify_dynptr_read(&dynptr, 0, user_buf + off, 1);
137784cdf93SMykyta Yatsenko 	/* Can't read more than 1 byte */
138784cdf93SMykyta Yatsenko 	loc_err = loc_err ?: verify_dynptr_read(&dynptr, 0, user_buf + off, 2) == 0;
139784cdf93SMykyta Yatsenko 	/* Can't read with far offset */
140784cdf93SMykyta Yatsenko 	loc_err = loc_err ?: verify_dynptr_read(&dynptr, 1, user_buf + off, 1) == 0;
141784cdf93SMykyta Yatsenko 
142784cdf93SMykyta Yatsenko cleanup:
143784cdf93SMykyta Yatsenko 	bpf_dynptr_file_discard(&dynptr);
144784cdf93SMykyta Yatsenko 	return loc_err;
145784cdf93SMykyta Yatsenko }
146