File Index
Files and directories
Sign in
FILE

file_reader.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/bpf/progs/file_reader.c C
  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_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 */
 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}
~/ Read-only file preview.