diff mbox series

[07/14] bpf: Implement BPF_LOAD_FD subcommand handler

Message ID 20250109214617.485144-8-bboscaccy@linux.microsoft.com (mailing list archive)
State New
Delegated to: BPF
Headers show
Series [01/14] bpf: Port prerequiste BTF handling functions from userspace | expand

Checks

Context Check Description
netdev/series_format warning Series does not have a cover letter
netdev/tree_selection success Guessed tree name to be net-next, async
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit fail Errors and warnings before: 7 this patch: 9
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers fail 12 maintainers not CCed: jolsa@kernel.org john.fastabend@gmail.com ast@kernel.org daniel@iogearbox.net martin.lau@linux.dev yonghong.song@linux.dev eddyz87@gmail.com andrii@kernel.org song@kernel.org sdf@fomichev.me kpsingh@kernel.org haoluo@google.com
netdev/build_clang success Errors and warnings before: 11 this patch: 11
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn fail Errors and warnings before: 7 this patch: 9
netdev/checkpatch warning CHECK: Please don't use multiple blank lines WARNING: line length of 100 exceeds 80 columns WARNING: line length of 81 exceeds 80 columns WARNING: line length of 84 exceeds 80 columns WARNING: line length of 94 exceeds 80 columns WARNING: line length of 97 exceeds 80 columns WARNING: line length of 99 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-net-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-net-VM_Test-3 success Logs for Validate matrix.py
bpf/vmtest-bpf-net-VM_Test-2 success Logs for Unittests
bpf/vmtest-bpf-net-VM_Test-5 success Logs for aarch64-gcc / build-release
bpf/vmtest-bpf-net-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-net-VM_Test-4 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-net-VM_Test-10 success Logs for aarch64-gcc / veristat-kernel
bpf/vmtest-bpf-net-VM_Test-11 success Logs for aarch64-gcc / veristat-meta
bpf/vmtest-bpf-net-VM_Test-13 success Logs for s390x-gcc / build-release
bpf/vmtest-bpf-net-VM_Test-6 success Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
bpf/vmtest-bpf-net-VM_Test-9 success Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
bpf/vmtest-bpf-net-VM_Test-12 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-net-VM_Test-16 success Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-net-VM_Test-17 success Logs for s390x-gcc / veristat-kernel
bpf/vmtest-bpf-net-VM_Test-18 success Logs for s390x-gcc / veristat-meta
bpf/vmtest-bpf-net-VM_Test-19 success Logs for set-matrix
bpf/vmtest-bpf-net-VM_Test-20 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-net-VM_Test-21 success Logs for x86_64-gcc / build-release
bpf/vmtest-bpf-net-VM_Test-30 success Logs for x86_64-llvm-17 / build / build for x86_64 with llvm-17
bpf/vmtest-bpf-net-VM_Test-31 success Logs for x86_64-llvm-17 / build-release / build for x86_64 with llvm-17-O2
bpf/vmtest-bpf-net-VM_Test-36 success Logs for x86_64-llvm-17 / veristat-kernel
bpf/vmtest-bpf-net-VM_Test-37 success Logs for x86_64-llvm-17 / veristat-meta
bpf/vmtest-bpf-net-VM_Test-38 success Logs for x86_64-llvm-18 / build / build for x86_64 with llvm-18
bpf/vmtest-bpf-net-VM_Test-39 success Logs for x86_64-llvm-18 / build-release / build for x86_64 with llvm-18-O2
bpf/vmtest-bpf-net-VM_Test-45 success Logs for x86_64-llvm-18 / veristat-kernel
bpf/vmtest-bpf-net-VM_Test-46 success Logs for x86_64-llvm-18 / veristat-meta
bpf/vmtest-bpf-net-VM_Test-7 fail Logs for aarch64-gcc / test (test_progs, false, 360) / test_progs on aarch64 with gcc
bpf/vmtest-bpf-net-VM_Test-8 fail Logs for aarch64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-net-VM_Test-15 fail Logs for s390x-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-net-VM_Test-35 success Logs for x86_64-llvm-17 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-17
bpf/vmtest-bpf-net-VM_Test-14 fail Logs for s390x-gcc / test (test_progs, false, 360) / test_progs on s390x with gcc
bpf/vmtest-bpf-net-VM_Test-22 success Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-net-VM_Test-23 fail Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-net-VM_Test-24 fail Logs for x86_64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-net-VM_Test-25 success Logs for x86_64-gcc / test (test_progs_no_alu32_parallel, true, 30) / test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-net-VM_Test-26 success Logs for x86_64-gcc / test (test_progs_parallel, true, 30) / test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-net-VM_Test-27 success Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-net-VM_Test-28 success Logs for x86_64-gcc / veristat-kernel / x86_64-gcc veristat_kernel
bpf/vmtest-bpf-net-VM_Test-29 success Logs for x86_64-gcc / veristat-meta / x86_64-gcc veristat_meta
bpf/vmtest-bpf-net-VM_Test-32 success Logs for x86_64-llvm-17 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-17
bpf/vmtest-bpf-net-VM_Test-33 fail Logs for x86_64-llvm-17 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-17
bpf/vmtest-bpf-net-VM_Test-34 fail Logs for x86_64-llvm-17 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-17
bpf/vmtest-bpf-net-VM_Test-44 success Logs for x86_64-llvm-18 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-18
bpf/vmtest-bpf-net-PR fail PR summary
bpf/vmtest-bpf-net-VM_Test-40 success Logs for x86_64-llvm-18 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-18
bpf/vmtest-bpf-net-VM_Test-41 fail Logs for x86_64-llvm-18 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-18
bpf/vmtest-bpf-net-VM_Test-42 fail Logs for x86_64-llvm-18 / test (test_progs_cpuv4, false, 360) / test_progs_cpuv4 on x86_64 with llvm-18
bpf/vmtest-bpf-net-VM_Test-43 fail Logs for x86_64-llvm-18 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-18

Commit Message

Blaise Boscaccy Jan. 9, 2025, 9:43 p.m. UTC
The new LOAD_FD subcommand keys off of a sysfs entry file descriptor
and a file descriptor pointing to a raw elf object file.

After performing some sysfs bookkeeping, the object file is copied
into the kernel, and with map and module metadata arrays.  Userspace
is expected to provide an array of file descriptors that correspond to
maps, along with module information, and offsets into the map array
that correspond with the arena allocator and the kconfig map if
applicable.

Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
 kernel/bpf/syscall.c | 242 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 242 insertions(+)

Comments

Greg KH Jan. 10, 2025, 6:05 a.m. UTC | #1
On Thu, Jan 09, 2025 at 01:43:49PM -0800, Blaise Boscaccy wrote:
> The new LOAD_FD subcommand keys off of a sysfs entry file descriptor
> and a file descriptor pointing to a raw elf object file.

A sysfs file descriptor?  That feels very odd and is not how sysfs
should be used, as it's only for text files and binary pass-through
stuff.

> +static void bpf_loader_show_fdinfo(struct seq_file *m, struct file *filp)
> +{
> +	int i;
> +	struct bpf_obj *obj = filp->private_data;
> +
> +	for (i = 0; i < obj->nr_programs; i++)
> +		seq_printf(m, "program: %s\n", obj->progs[i].name);

So what file is printing this out in sysfs?  Where is the
Documentation/ABI/ entry for it?

confused,

greg k-h
diff mbox series

Patch

diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 37e45145e113b..3cfb497e1b236 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -6074,6 +6074,245 @@  skip_mods_and_typedefs(const struct btf *btf, u32 id, u32 *res_id)
 	return t;
 }
 
+static void free_bpf_obj(struct bpf_obj *obj)
+{
+	int i;
+
+	if (!obj)
+		return;
+
+	for (i = 0; i < obj->nr_programs; i++) {
+		kfree(obj->progs[i].insn);
+		kfree(obj->progs[i].reloc_desc);
+	}
+
+	kfree(obj->progs);
+	vfree(obj->hdr);
+
+	btf_put(obj->btf);
+	btf_put(obj->btf_vmlinux);
+	btf_ext__free(obj->btf_ext);
+
+	for (i = 0; i < obj->btf_modules_cnt; i++)
+		btf_put(obj->btf_modules[i].btf);
+
+	kfree(obj->btf_modules);
+	kfree(obj->externs);
+	kfree(obj->maps);
+}
+
+#define BPF_LOADER_INODE_NAME "bpf-loader"
+
+static const struct inode_operations bpf_loader_iops = { };
+
+static int bpf_loader_release(struct inode *inode, struct file *filp)
+{
+	struct bpf_obj *obj = filp->private_data;
+
+	free_bpf_obj(obj);
+	return 0;
+}
+
+static void bpf_loader_show_fdinfo(struct seq_file *m, struct file *filp)
+{
+	int i;
+	struct bpf_obj *obj = filp->private_data;
+
+	for (i = 0; i < obj->nr_programs; i++)
+		seq_printf(m, "program: %s\n", obj->progs[i].name);
+}
+
+static const struct file_operations bpf_loader_fops = {
+	.release	= bpf_loader_release,
+	.show_fdinfo	= bpf_loader_show_fdinfo,
+};
+
+static int loader_create(unsigned int bpffs_fd)
+{
+	struct inode *inode;
+	struct bpf_obj *obj = NULL;
+	struct file *file;
+	struct path path;
+	struct fd f;
+	umode_t mode;
+	int err, fd;
+
+	f = fdget(bpffs_fd);
+	if (!fd_file(f))
+		return -EBADF;
+
+	path = fd_file(f)->f_path;
+	path_get(&path);
+	fdput(f);
+
+	if (path.dentry != path.mnt->mnt_sb->s_root) {
+		err = -EINVAL;
+		goto out_path;
+	}
+	if (path.mnt->mnt_sb->s_op != &bpf_super_ops) {
+		err = -EINVAL;
+		goto out_path;
+	}
+	err = path_permission(&path, MAY_ACCESS);
+	if (err)
+		goto out_path;
+
+	mode = S_IFREG | (0600 & ~current_umask());
+	inode = bpf_get_inode(path.mnt->mnt_sb, NULL, mode);
+	if (IS_ERR(inode)) {
+		err = PTR_ERR(inode);
+		goto out_path;
+	}
+
+	inode->i_op = &bpf_loader_iops;
+	inode->i_fop = &bpf_loader_fops;
+	clear_nlink(inode);
+
+	file = alloc_file_pseudo(inode, path.mnt, BPF_LOADER_INODE_NAME, O_RDWR, &bpf_loader_fops);
+	if (IS_ERR(file)) {
+		err = PTR_ERR(file);
+		goto out_inode;
+	}
+
+	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+	if (!obj) {
+		err = -ENOMEM;
+		goto out_inode;
+	}
+
+	fd = get_unused_fd_flags(O_CLOEXEC);
+	if (fd < 0) {
+		err = fd;
+		kfree(obj);
+		goto out_inode;
+	}
+
+	file->private_data = obj;
+	fd_install(fd, file);
+	path_put(&path);
+	return fd;
+
+out_inode:
+	iput(inode);
+	fput(file);
+out_path:
+	path_put(&path);
+	return err;
+}
+
+static int load_fd(union bpf_attr *attr)
+{
+	void *buf = NULL;
+	int len;
+	int i;
+	int obj_f;
+	struct fd obj_fd;
+	struct bpf_module_obj *modules;
+	struct bpf_obj *obj;
+	int err;
+
+	struct fd f;
+	struct fd bpffs_fd;
+
+	f = fdget(attr->load_fd.obj_fd);
+	if (!fd_file(f)) {
+		err = -EBADF;
+		goto out;
+	}
+
+	bpffs_fd = fdget(attr->load_fd.bpffs_fd);
+	if (!fd_file(bpffs_fd)) {
+		fdput(f);
+		err = -EBADF;
+		goto out;
+	}
+
+	obj_f = loader_create(attr->load_fd.bpffs_fd);
+	if (obj_f < 0) {
+		err = obj_f;
+		fdput(f);
+		fdput(bpffs_fd);
+		goto out;
+	}
+
+	obj_fd = fdget(obj_f);
+	obj = fd_file(obj_fd)->private_data;
+
+	len = kernel_read_file(fd_file(f), 0, &buf, INT_MAX, NULL, READING_EBPF);
+	if (len < 0) {
+		fdput(obj_fd);
+		err = len;
+		goto out;
+	}
+
+	obj->hdr = buf;
+	obj->len = len;
+	obj->nr_maps = attr->load_fd.map_cnt;
+	obj->maps = kmalloc_array(attr->load_fd.map_cnt, sizeof(struct bpf_map_obj), GFP_KERNEL);
+
+	if (!obj->maps) {
+		err = -ENOMEM;
+		goto free;
+	}
+
+	if (attr->load_fd.map_cnt) {
+		if (copy_from_user(obj->maps, (const void *)attr->load_fd.maps,
+				   sizeof(struct bpf_map_obj) * attr->load_fd.map_cnt) != 0) {
+			err = -EFAULT;
+			goto free;
+		}
+	}
+
+	obj->kconfig_map_idx = attr->load_fd.kconfig_map_idx;
+	obj->arena_map_idx = attr->load_fd.arena_map_idx;
+	obj->btf_vmlinux = bpf_get_btf_vmlinux();
+	modules = kmalloc_array(attr->load_fd.module_cnt,
+				sizeof(struct bpf_module_obj), GFP_KERNEL);
+
+	if (!modules) {
+		err = -ENOMEM;
+		goto free;
+	}
+
+
+	if (attr->load_fd.module_cnt) {
+		if (copy_from_user(modules, (const void *)attr->load_fd.modules,
+				   sizeof(struct bpf_module_obj) * attr->load_fd.module_cnt) != 0) {
+			err = -EFAULT;
+			goto free;
+		}
+	}
+
+	obj->btf_modules_cnt = attr->load_fd.module_cnt;
+	obj->btf_modules = kmalloc_array(attr->load_fd.module_cnt,
+					 sizeof(struct bpf_module_btf), GFP_KERNEL);
+
+	if (!obj->btf_modules) {
+		err = -ENOMEM;
+		goto free;
+	}
+
+	for (i = 0; i < obj->btf_modules_cnt; i++) {
+		obj->btf_modules[i].fd = modules[i].fd;
+		obj->btf_modules[i].id = modules[i].id;
+		obj->btf_modules[i].fd_array_idx = modules[i].fd_array_idx;
+		obj->btf_modules[i].btf = btf_get_by_fd(obj->btf_modules[i].fd);
+		if (IS_ERR(obj->btf_modules[i].btf)) {
+			err = PTR_ERR(obj->btf_modules[i].btf);
+			kfree(modules);
+			goto free;
+		}
+	}
+	kfree(modules);
+
+	return obj_f;
+free:
+	free_bpf_obj(obj);
+	fd_file(obj_fd)->private_data = NULL;
+out:
+	return err;
+}
+
 static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size)
 {
 	union bpf_attr attr;
@@ -6210,6 +6449,9 @@  static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size)
 	case BPF_TOKEN_CREATE:
 		err = token_create(&attr);
 		break;
+	case BPF_LOAD_FD:
+		err = load_fd(&attr);
+		break;
 	default:
 		err = -EINVAL;
 		break;