xref: /linux/tools/testing/selftests/bpf/progs/file_reader.c (revision db6b35cffe59c619ea3772b21d7c7c8a7b885dc1)
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 
31 static int validate_file_read(struct file *file);
32 static int task_work_callback(struct bpf_map *map, void *key, void *value);
33 
34 SEC("lsm/file_open")
35 int on_open_expect_fault(void *c)
36 {
37 	struct bpf_dynptr dynptr;
38 	struct file *file;
39 	int local_err = 1;
40 	__u32 user_buf_sz = sizeof(user_buf);
41 
42 	if (bpf_get_current_pid_tgid() >> 32 != pid)
43 		return 0;
44 
45 	file = bpf_get_task_exe_file(bpf_get_current_task_btf());
46 	if (!file)
47 		return 0;
48 
49 	if (bpf_dynptr_from_file(file, 0, &dynptr))
50 		goto out;
51 
52 	local_err = bpf_dynptr_read(tmp_buf, user_buf_sz, &dynptr, user_buf_sz, 0);
53 	if (local_err == -EFAULT) { /* Expect page fault */
54 		local_err = 0;
55 		run_success = 1;
56 	}
57 out:
58 	bpf_dynptr_file_discard(&dynptr);
59 	if (local_err)
60 		err = local_err;
61 	bpf_put_file(file);
62 	return 0;
63 }
64 
65 SEC("lsm/file_open")
66 int on_open_validate_file_read(void *c)
67 {
68 	struct task_struct *task = bpf_get_current_task_btf();
69 	struct elem *work;
70 	int key = 0;
71 
72 	if (bpf_get_current_pid_tgid() >> 32 != pid)
73 		return 0;
74 
75 	work = bpf_map_lookup_elem(&arrmap, &key);
76 	if (!work) {
77 		err = 1;
78 		return 0;
79 	}
80 	bpf_task_work_schedule_signal_impl(task, &work->tw, &arrmap, task_work_callback, NULL);
81 	return 0;
82 }
83 
84 /* Called in a sleepable context, read 256K bytes, cross check with user space read data */
85 static int task_work_callback(struct bpf_map *map, void *key, void *value)
86 {
87 	struct task_struct *task = bpf_get_current_task_btf();
88 	struct file *file = bpf_get_task_exe_file(task);
89 
90 	if (!file)
91 		return 0;
92 
93 	err = validate_file_read(file);
94 	if (!err)
95 		run_success = 1;
96 	bpf_put_file(file);
97 	return 0;
98 }
99 
100 static int verify_dynptr_read(struct bpf_dynptr *ptr, u32 off, char *user_buf, u32 len)
101 {
102 	int i;
103 
104 	if (bpf_dynptr_read(tmp_buf, len, ptr, off, 0))
105 		return 1;
106 
107 	/* Verify file contents read from BPF is the same as the one read from userspace */
108 	bpf_for(i, 0, len)
109 	{
110 		if (tmp_buf[i] != user_buf[i])
111 			return 1;
112 	}
113 	return 0;
114 }
115 
116 static int validate_file_read(struct file *file)
117 {
118 	struct bpf_dynptr dynptr;
119 	int loc_err = 1, off;
120 	__u32 user_buf_sz = sizeof(user_buf);
121 
122 	if (bpf_dynptr_from_file(file, 0, &dynptr))
123 		goto cleanup;
124 
125 	loc_err = verify_dynptr_read(&dynptr, 0, user_buf, user_buf_sz);
126 	off = 1;
127 	loc_err = loc_err ?: verify_dynptr_read(&dynptr, off, user_buf + off, user_buf_sz - off);
128 	off = user_buf_sz - 1;
129 	loc_err = loc_err ?: verify_dynptr_read(&dynptr, off, user_buf + off, user_buf_sz - off);
130 	/* Read file with random offset and length */
131 	off = 4097;
132 	loc_err = loc_err ?: verify_dynptr_read(&dynptr, off, user_buf + off, 100);
133 
134 	/* Adjust dynptr, verify read */
135 	loc_err = loc_err ?: bpf_dynptr_adjust(&dynptr, off, off + 1);
136 	loc_err = loc_err ?: verify_dynptr_read(&dynptr, 0, user_buf + off, 1);
137 	/* Can't read more than 1 byte */
138 	loc_err = loc_err ?: verify_dynptr_read(&dynptr, 0, user_buf + off, 2) == 0;
139 	/* Can't read with far offset */
140 	loc_err = loc_err ?: verify_dynptr_read(&dynptr, 1, user_buf + off, 1) == 0;
141 
142 cleanup:
143 	bpf_dynptr_file_discard(&dynptr);
144 	return loc_err;
145 }
146