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