From: Roberto Sassu <roberto.sassu@xxxxxxxxxx> Introduce a simple UMD driver using the management library, for testing purposes. UMD Manager: sample_mgr.c UMD Loader: sample_loader.c UMD Handler: sample_handler.c The UMD Manager exposes /sys/kernel/security/sample_umd and accepts an offset between 0-128K. It invokes the UMD Loader to start the UMD Handler and passes to the latter the offset at which it sets the byte of the response buffer to 1. The UMD Manager verifies that and returns the number of bytes written on success, a negative value on failure. Signed-off-by: Roberto Sassu <roberto.sassu@xxxxxxxxxx> --- MAINTAINERS | 1 + tools/testing/selftests/Makefile | 1 + tools/testing/selftests/umd_mgmt/.gitignore | 1 + tools/testing/selftests/umd_mgmt/Makefile | 14 ++ tools/testing/selftests/umd_mgmt/config | 1 + .../selftests/umd_mgmt/sample_umd/Makefile | 22 ++++ .../selftests/umd_mgmt/sample_umd/msgfmt.h | 13 ++ .../umd_mgmt/sample_umd/sample_binary_blob.S | 7 + .../umd_mgmt/sample_umd/sample_handler.c | 81 ++++++++++++ .../umd_mgmt/sample_umd/sample_loader.c | 28 ++++ .../umd_mgmt/sample_umd/sample_mgr.c | 124 ++++++++++++++++++ tools/testing/selftests/umd_mgmt/umd_mgmt.sh | 40 ++++++ 12 files changed, 333 insertions(+) create mode 100644 tools/testing/selftests/umd_mgmt/.gitignore create mode 100644 tools/testing/selftests/umd_mgmt/Makefile create mode 100644 tools/testing/selftests/umd_mgmt/config create mode 100644 tools/testing/selftests/umd_mgmt/sample_umd/Makefile create mode 100644 tools/testing/selftests/umd_mgmt/sample_umd/msgfmt.h create mode 100644 tools/testing/selftests/umd_mgmt/sample_umd/sample_binary_blob.S create mode 100644 tools/testing/selftests/umd_mgmt/sample_umd/sample_handler.c create mode 100644 tools/testing/selftests/umd_mgmt/sample_umd/sample_loader.c create mode 100644 tools/testing/selftests/umd_mgmt/sample_umd/sample_mgr.c create mode 100755 tools/testing/selftests/umd_mgmt/umd_mgmt.sh diff --git a/MAINTAINERS b/MAINTAINERS index 7b27435fd20..a0cd161843e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11251,6 +11251,7 @@ L: linux-kernel@xxxxxxxxxxxxxxx S: Maintained F: include/linux/usermode_driver_mgmt.h F: kernel/usermode_driver_mgmt.c +F: tools/testing/selftests/umd_mgmt/* KERNEL VIRTUAL MACHINE (KVM) M: Paolo Bonzini <pbonzini@xxxxxxxxxx> diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 13a6837a0c6..84202d5b4fb 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -84,6 +84,7 @@ TARGETS += timers endif TARGETS += tmpfs TARGETS += tpm2 +TARGETS += umd_mgmt TARGETS += user TARGETS += vDSO TARGETS += mm diff --git a/tools/testing/selftests/umd_mgmt/.gitignore b/tools/testing/selftests/umd_mgmt/.gitignore new file mode 100644 index 00000000000..215c17d13e9 --- /dev/null +++ b/tools/testing/selftests/umd_mgmt/.gitignore @@ -0,0 +1 @@ +/sample_umd/sample_umh diff --git a/tools/testing/selftests/umd_mgmt/Makefile b/tools/testing/selftests/umd_mgmt/Makefile new file mode 100644 index 00000000000..f1d47eec04e --- /dev/null +++ b/tools/testing/selftests/umd_mgmt/Makefile @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0 +TEST_GEN_PROGS_EXTENDED = sample_umd.ko + +$(OUTPUT)/%.ko: $(wildcard sample_umd/Makefile sample_umd/*.[ch]) + $(call msg,MOD,,$@) + $(Q)$(MAKE) -C sample_umd install + +OVERRIDE_TARGETS := 1 +override define CLEAN + $(call msg,CLEAN) + $(Q)$(MAKE) -C sample_umd clean +endef + +include ../lib.mk diff --git a/tools/testing/selftests/umd_mgmt/config b/tools/testing/selftests/umd_mgmt/config new file mode 100644 index 00000000000..71a078a3ac0 --- /dev/null +++ b/tools/testing/selftests/umd_mgmt/config @@ -0,0 +1 @@ +CONFIG_BPFILTER=y diff --git a/tools/testing/selftests/umd_mgmt/sample_umd/Makefile b/tools/testing/selftests/umd_mgmt/sample_umd/Makefile new file mode 100644 index 00000000000..6d950e05f3d --- /dev/null +++ b/tools/testing/selftests/umd_mgmt/sample_umd/Makefile @@ -0,0 +1,22 @@ +KDIR ?= ../../../../.. + +userprogs := sample_umh +sample_umh-objs := sample_handler.o +userccflags += -I $(srctree) + +$(obj)/sample_binary_blob.o: $(obj)/sample_umh + +MODULES = sample_loader_kmod.ko sample_mgr.ko + +obj-m += sample_loader_kmod.o sample_mgr.o + +sample_loader_kmod-objs = sample_loader.o sample_binary_blob.o + +all: + +$(Q)$(MAKE) -C $(KDIR) M=$$PWD modules + +clean: + +$(Q)$(MAKE) -C $(KDIR) M=$$PWD clean + +install: all + +$(Q)$(MAKE) -C $(KDIR) M=$$PWD modules_install diff --git a/tools/testing/selftests/umd_mgmt/sample_umd/msgfmt.h b/tools/testing/selftests/umd_mgmt/sample_umd/msgfmt.h new file mode 100644 index 00000000000..34a62d72cde --- /dev/null +++ b/tools/testing/selftests/umd_mgmt/sample_umd/msgfmt.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _SAMPLE_UMH_MSGFMT_H +#define _SAMPLE_UMH_MSGFMT_H + +struct sample_request { + uint32_t offset; +}; + +struct sample_reply { + uint8_t data[128 * 1024]; +}; + +#endif diff --git a/tools/testing/selftests/umd_mgmt/sample_umd/sample_binary_blob.S b/tools/testing/selftests/umd_mgmt/sample_umd/sample_binary_blob.S new file mode 100644 index 00000000000..3687dd13973 --- /dev/null +++ b/tools/testing/selftests/umd_mgmt/sample_umd/sample_binary_blob.S @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + .section .init.rodata, "a" + .global sample_umh_start +sample_umh_start: + .incbin "tools/testing/selftests/umd_mgmt/sample_umd/sample_umh" + .global sample_umh_end +sample_umh_end: diff --git a/tools/testing/selftests/umd_mgmt/sample_umd/sample_handler.c b/tools/testing/selftests/umd_mgmt/sample_umd/sample_handler.c new file mode 100644 index 00000000000..94ea6d99bbc --- /dev/null +++ b/tools/testing/selftests/umd_mgmt/sample_umd/sample_handler.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Huawei Technologies Duesseldorf GmbH + * + * Author: Roberto Sassu <roberto.sassu@xxxxxxxxxx> + * + * Implement the UMD Handler. + */ +#include <unistd.h> +#include <malloc.h> +#include <stdint.h> + +#include "msgfmt.h" + +FILE *debug_f; + +static void loop(void) +{ + struct sample_request *req = NULL; + struct sample_reply *reply = NULL; + + req = calloc(1, sizeof(*req)); + if (!req) + return; + + reply = calloc(1, sizeof(*reply)); + if (!reply) + goto out; + + while (1) { + int n, len, offset; + + offset = 0; + len = sizeof(*req); + + while (len) { + n = read(0, ((void *)req) + offset, len); + if (n <= 0) { + fprintf(debug_f, "invalid request %d\n", n); + goto out; + } + + len -= n; + offset += n; + } + + if (req->offset < sizeof(reply->data)) + reply->data[req->offset] = 1; + + offset = 0; + len = sizeof(*reply); + + while (len) { + n = write(1, ((void *)reply) + offset, len); + if (n <= 0) { + fprintf(debug_f, "reply failed %d\n", n); + goto out; + } + + len -= n; + offset += n; + } + + if (req->offset < sizeof(reply->data)) + reply->data[req->offset] = 0; + } +out: + free(req); + free(reply); +} + +int main(void) +{ + debug_f = fopen("/dev/kmsg", "w"); + setvbuf(debug_f, 0, _IOLBF, 0); + fprintf(debug_f, "<5>Started sample_umh\n"); + loop(); + fclose(debug_f); + + return 0; +} diff --git a/tools/testing/selftests/umd_mgmt/sample_umd/sample_loader.c b/tools/testing/selftests/umd_mgmt/sample_umd/sample_loader.c new file mode 100644 index 00000000000..36c0e69e3f7 --- /dev/null +++ b/tools/testing/selftests/umd_mgmt/sample_umd/sample_loader.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Huawei Technologies Duesseldorf GmbH + * + * Author: Roberto Sassu <roberto.sassu@xxxxxxxxxx> + * + * Implement the UMD Loader (credits: bpfilter). + */ +#include <linux/module.h> +#include <linux/usermode_driver_mgmt.h> + +extern char sample_umh_start; +extern char sample_umh_end; +extern struct umd_mgmt sample_mgmt_ops; + +static int __init load_umh(void) +{ + return umd_mgmt_load(&sample_mgmt_ops, &sample_umh_start, + &sample_umh_end); +} + +static void __exit fini_umh(void) +{ + umd_mgmt_unload(&sample_mgmt_ops); +} +module_init(load_umh); +module_exit(fini_umh); +MODULE_LICENSE("GPL"); diff --git a/tools/testing/selftests/umd_mgmt/sample_umd/sample_mgr.c b/tools/testing/selftests/umd_mgmt/sample_umd/sample_mgr.c new file mode 100644 index 00000000000..75c572f9849 --- /dev/null +++ b/tools/testing/selftests/umd_mgmt/sample_umd/sample_mgr.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Huawei Technologies Duesseldorf GmbH + * + * Author: Roberto Sassu <roberto.sassu@xxxxxxxxxx> + * + * Implement the UMD Manager. + */ +#include <linux/module.h> +#include <linux/security.h> +#include <linux/seq_file.h> +#include <linux/usermode_driver_mgmt.h> + +#include "msgfmt.h" + +struct umd_mgmt sample_mgmt_ops; +EXPORT_SYMBOL_GPL(sample_mgmt_ops); + +struct dentry *sample_umd_dentry; + +static int sample_write_common(u32 offset, bool test) +{ + struct sample_request *req; + struct sample_reply *reply; + int ret; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) { + ret = -ENOMEM; + goto out; + } + + reply = kzalloc(sizeof(*reply), GFP_KERNEL); + if (!reply) { + ret = -ENOMEM; + goto out; + } + + req->offset = offset; + + if (test) + /* Lock is already taken. */ + ret = umd_send_recv(&sample_mgmt_ops.info, req, sizeof(*req), + reply, sizeof(*reply)); + else + ret = umd_mgmt_send_recv(&sample_mgmt_ops, req, sizeof(*req), + reply, sizeof(*reply)); + if (ret < 0) + goto out; + + if (reply->data[req->offset] != 1) { + ret = -EINVAL; + goto out; + } +out: + kfree(req); + kfree(reply); + + return ret; +} + +static ssize_t sample_umd_write(struct file *file, const char __user *buf, + size_t datalen, loff_t *ppos) +{ + char offset_str[8]; + u32 offset; + int ret; + + if (datalen >= sizeof(offset_str)) + return -EINVAL; + + ret = copy_from_user(offset_str, buf, datalen); + if (ret < 0) + return ret; + + offset_str[datalen] = '\0'; + + ret = kstrtou32(offset_str, 10, &offset); + if (ret < 0) + return ret; + + if (offset >= sizeof(((struct sample_reply *)0)->data)) + return -EINVAL; + + ret = sample_write_common(offset, false); + if (ret < 0) + return ret; + + return datalen; +} + +static const struct file_operations sample_umd_file_ops = { + .write = sample_umd_write, +}; + +static int sample_post_start_umh(struct umd_mgmt *mgmt) +{ + return sample_write_common(0, true); +} + +static int __init load_umh(void) +{ + mutex_init(&sample_mgmt_ops.lock); + sample_mgmt_ops.info.tgid = NULL; + sample_mgmt_ops.info.driver_name = "sample_umh"; + sample_mgmt_ops.post_start = sample_post_start_umh; + sample_mgmt_ops.kmod = "sample_loader_kmod"; + sample_mgmt_ops.kmod_loaded = false; + + sample_umd_dentry = securityfs_create_file("sample_umd", 0200, NULL, + NULL, &sample_umd_file_ops); + if (IS_ERR(sample_umd_dentry)) + return PTR_ERR(sample_umd_dentry); + + return 0; +} + +static void __exit fini_umh(void) +{ + securityfs_remove(sample_umd_dentry); +} +module_init(load_umh); +module_exit(fini_umh); +MODULE_LICENSE("GPL"); diff --git a/tools/testing/selftests/umd_mgmt/umd_mgmt.sh b/tools/testing/selftests/umd_mgmt/umd_mgmt.sh new file mode 100755 index 00000000000..9b90d737fec --- /dev/null +++ b/tools/testing/selftests/umd_mgmt/umd_mgmt.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2023 Huawei Technologies Duesseldorf GmbH +# +# Author: Roberto Sassu <roberto.sassu@xxxxxxxxxx> +# +# Script to test the UMD management library. + +# Kselftest framework defines: ksft_pass=0, ksft_fail=1, ksft_skip=4 +ksft_pass=0 +ksft_fail=1 +ksft_skip=4 + +if ! /sbin/modprobe -q sample_mgr; then + echo "umd_mgmt: module sample_mgr is not found [SKIP]" + exit $ksft_skip +fi + +if [ ! -f /sys/kernel/security/sample_umd ]; then + echo "umd_mgmt: kernel interface is not found [SKIP]" + exit $ksft_skip +fi + +i=0 + +while [ $i -lt 500 ]; do + if ! echo $(( RANDOM % 128 * 1024 )) > /sys/kernel/security/sample_umd; then + echo "umd_mgmt: test failed" + exit $ksft_fail + fi + + if [ $(( i % 50 )) -eq 0 ]; then + rmmod sample_loader_kmod + fi + + (( i++ )) +done + +exit $ksft_pass -- 2.25.1