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