lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <20201106200704.192894-1-mcroce@linux.microsoft.com>
Date:   Fri,  6 Nov 2020 21:07:04 +0100
From:   Matteo Croce <mcroce@...ux.microsoft.com>
To:     linux-kernel@...r.kernel.org
Cc:     Mike Rapoport <rppt@...nel.org>,
        Guenter Roeck <linux@...ck-us.net>,
        Arnd Bergmann <arnd@...db.de>, Petr Mladek <pmladek@...e.com>,
        Pavel Tatashin <pasha.tatashin@...een.com>,
        Kees Cook <keescook@...omium.org>,
        Andrew Morton <akpm@...ux-foundation.org>,
        Tyler Hicks <tyhicks@...ux.microsoft.com>
Subject: [PATCH v2] reboot: allow to specify reboot mode via sysfs

From: Matteo Croce <mcroce@...rosoft.com>

The kernel cmdline reboot= option offers some sort of control
on how the reboot is issued.
Add handles in sysfs to allow setting these reboot options, so they
can be changed when the system is booted, other than at boot time.

The handlers are under <sysfs>/kernel/reboot, can be read to
get the current configuration and written to alter it.

	# cd /sys/kernel/reboot/

	# grep . *
	cpu:0
	force:0
	mode:cold
	type:acpi

	# echo 2 >cpu
	# echo 1 >force
	# echo soft >mode
	# echo bios >type

	# grep . *
	cpu:2
	force:1
	mode:soft
	type:bios

Before setting anything, check for CAP_SYS_BOOT capability, so it's
possible to allow an unpriviledged process to change these settings
simply by relaxing the handles permissions, without opening them to
the world.

Signed-off-by: Matteo Croce <mcroce@...rosoft.com>
---
 Documentation/ABI/testing/sysfs-kernel-reboot |  26 +++
 kernel/reboot.c                               | 193 ++++++++++++++++++
 2 files changed, 219 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-kernel-reboot

diff --git a/Documentation/ABI/testing/sysfs-kernel-reboot b/Documentation/ABI/testing/sysfs-kernel-reboot
new file mode 100644
index 000000000000..3fda90bdc644
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-kernel-reboot
@@ -0,0 +1,26 @@
+What:		/sys/kernel/reboot
+Date:		October 2020
+KernelVersion:	5.11
+Contact:	Matteo Croce <mcroce@...rosoft.com>
+Description:	Interface to set the kernel reboot mode, similarly to
+		what can be done via the reboot= cmdline option.
+		(see Documentation/admin-guide/kernel-parameters.txt)
+
+What:		/sys/kernel/reboot/mode
+What:		/sys/kernel/reboot/type
+What:		/sys/kernel/reboot/cpu
+What:		/sys/kernel/reboot/force
+
+Date:		October 2020
+Contact:	Matteo Croce <mcroce@...rosoft.com>
+Description:	Tune reboot parameters.
+
+		mode: Reboot mode. Valid values are:
+		cold warm hard soft gpio
+
+		type: Reboot type. Valid values are:
+		bios acpi kbd triple efi pci
+
+		cpu: CPU number to use to reboot.
+
+		force: Force an immediate reboot.
diff --git a/kernel/reboot.c b/kernel/reboot.c
index e7b78d5ae1ab..b9e607517ae3 100644
--- a/kernel/reboot.c
+++ b/kernel/reboot.c
@@ -594,3 +594,196 @@ static int __init reboot_setup(char *str)
 	return 1;
 }
 __setup("reboot=", reboot_setup);
+
+#ifdef CONFIG_SYSFS
+
+#define STARTS_WITH(s, sc) (!strncmp(s, sc, sizeof(sc)-1))
+
+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	const char *val;
+
+	switch (reboot_mode) {
+	case REBOOT_COLD:
+		val = "cold\n";
+		break;
+	case REBOOT_WARM:
+		val = "warm\n";
+		break;
+	case REBOOT_HARD:
+		val = "hard\n";
+		break;
+	case REBOOT_SOFT:
+		val = "soft\n";
+		break;
+	case REBOOT_GPIO:
+		val = "gpio\n";
+		break;
+	default:
+		val = "undefined\n";
+	}
+
+	return strscpy(buf, val, 10);
+}
+static ssize_t mode_store(struct kobject *kobj, struct kobj_attribute *attr,
+			  const char *buf, size_t count)
+{
+	if (!capable(CAP_SYS_BOOT))
+		return -EPERM;
+
+	if (STARTS_WITH(buf, "cold"))
+		reboot_mode = REBOOT_COLD;
+	else if (STARTS_WITH(buf, "warm"))
+		reboot_mode = REBOOT_WARM;
+	else if (STARTS_WITH(buf, "hard"))
+		reboot_mode = REBOOT_HARD;
+	else if (STARTS_WITH(buf, "soft"))
+		reboot_mode = REBOOT_SOFT;
+	else if (STARTS_WITH(buf, "gpio"))
+		reboot_mode = REBOOT_GPIO;
+	else
+		return -EINVAL;
+
+	return count;
+}
+static struct kobj_attribute reboot_mode_attr = __ATTR_RW(mode);
+
+static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	const char *val;
+
+	switch (reboot_type) {
+	case BOOT_TRIPLE:
+		val = "triple\n";
+		break;
+	case BOOT_KBD:
+		val = "kbd\n";
+		break;
+	case BOOT_BIOS:
+		val = "bios\n";
+		break;
+	case BOOT_ACPI:
+		val = "acpi\n";
+		break;
+	case BOOT_EFI:
+		val = "efi\n";
+		break;
+	case BOOT_CF9_FORCE:
+		val = "cf9_force\n";
+		break;
+	case BOOT_CF9_SAFE:
+		val = "cf9_safe\n";
+		break;
+	default:
+		val = "undefined\n";
+	}
+
+	return strscpy(buf, val, 10);
+}
+static ssize_t type_store(struct kobject *kobj, struct kobj_attribute *attr,
+			  const char *buf, size_t count)
+{
+	if (!capable(CAP_SYS_BOOT))
+		return -EPERM;
+
+	if (STARTS_WITH(buf, "triple"))
+		reboot_type = BOOT_TRIPLE;
+	else if (STARTS_WITH(buf, "kbd"))
+		reboot_type = BOOT_KBD;
+	else if (STARTS_WITH(buf, "bios"))
+		reboot_type = BOOT_BIOS;
+	else if (STARTS_WITH(buf, "acpi"))
+		reboot_type = BOOT_ACPI;
+	else if (STARTS_WITH(buf, "efi"))
+		reboot_type = BOOT_EFI;
+	else if (STARTS_WITH(buf, "cf9_force"))
+		reboot_type = BOOT_CF9_FORCE;
+	else if (STARTS_WITH(buf, "cf9_safe"))
+		reboot_type = BOOT_CF9_SAFE;
+	else
+		return -EINVAL;
+
+	return count;
+}
+static struct kobj_attribute reboot_type_attr = __ATTR_RW(type);
+
+#undef STARTS_WITH
+
+static ssize_t cpu_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", reboot_cpu);
+}
+static ssize_t cpu_store(struct kobject *kobj, struct kobj_attribute *attr,
+			  const char *buf, size_t count)
+{
+	unsigned int cpunum;
+	int rc;
+
+	if (!capable(CAP_SYS_BOOT))
+		return -EPERM;
+
+	rc = kstrtouint(buf, 0, &cpunum);
+
+	if (rc)
+		return rc;
+
+	if (cpunum >= num_possible_cpus())
+		return -ERANGE;
+
+	reboot_cpu = cpunum;
+
+	return count;
+}
+static struct kobj_attribute reboot_cpu_attr = __ATTR_RW(cpu);
+
+static ssize_t force_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", reboot_force);
+}
+static ssize_t force_store(struct kobject *kobj, struct kobj_attribute *attr,
+			  const char *buf, size_t count)
+{
+	if (!capable(CAP_SYS_BOOT))
+		return -EPERM;
+
+	if (buf[0] != '0' && buf[0] != '1')
+		return -EINVAL;
+
+	reboot_force = buf[0] - '0';
+
+	return count;
+}
+static struct kobj_attribute reboot_force_attr = __ATTR_RW(force);
+
+static struct attribute *reboot_attrs[] = {
+	&reboot_mode_attr.attr,
+	&reboot_type_attr.attr,
+	&reboot_cpu_attr.attr,
+	&reboot_force_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group reboot_attr_group = {
+	.attrs = reboot_attrs,
+};
+
+static int __init reboot_ksysfs_init(void)
+{
+	struct kobject *reboot_kobj;
+	int ret;
+
+	reboot_kobj = kobject_create_and_add("reboot", kernel_kobj);
+	if (!reboot_kobj)
+		return -ENOMEM;
+
+	ret = sysfs_create_group(reboot_kobj, &reboot_attr_group);
+	if (ret) {
+		kobject_put(reboot_kobj);
+		return ret;
+	}
+
+	return 0;
+}
+core_initcall(reboot_ksysfs_init);
+
+#endif
-- 
2.28.0

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ