Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
11char _license[] SEC("license") = "GPL";
12
13struct {
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
20struct elem {
21 struct file *file;
22 struct bpf_task_work tw;
23};
24
25char user_buf[256000];
26char tmp_buf[256000];
27
28int pid = 0;
29int err, run_success = 0;
30
31static int validate_file_read(struct file *file);
32static int task_work_callback(struct bpf_map *map, void *key, void *value);
33
34SEC("lsm/file_open")
35int 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 }
57out:
58 bpf_dynptr_file_discard(&dynptr);
59 if (local_err)
60 err = local_err;
61 bpf_put_file(file);
62 return 0;
63}
64
65SEC("lsm/file_open")
66int 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(task, &work->tw, &arrmap, task_work_callback);
81 return 0;
82}
83
84/* Called in a sleepable context, read 256K bytes, cross check with user space read data */
85static 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
100static 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
116static 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
142cleanup:
143 bpf_dynptr_file_discard(&dynptr);
144 return loc_err;
145}