twist: allow disabling reboot request

From: Tetsuo Handa
Date: Wed Jun 03 2020 - 07:03:38 EST


On 2020/05/29 22:26, Tetsuo Handa wrote:
> By the way, I do worry that people forget to perform these steps when they do
> their tests without asking syzbot...

Here is a draft of boot-time switching. Since kconfig can handle string variable up to
2048 characters, we could hold the content of the "your-config" file inside .config file
in order to avoid relying on external file in "syzkaller tree". But since only one kconfig
option is used, basically the way to temporarily include/exclude specific options (under
automated testing by syzbot) seems to remain directly patching apply_twist_flags(), for
https://github.com/google/syzkaller/blob/master/dashboard/config/util.sh will automatically
overwrite CONFIG_DEFAULT_TWIST_FLAGS settings. If each twist flag were using independent
kconfig option, the way to temporarily include/exclude specific options will become directly
patching Kconfig file.

drivers/tty/vt/keyboard.c | 2 ++
include/linux/kernel.h | 8 ++++++++
init/main.c | 30 ++++++++++++++++++++++++++++++
kernel/reboot.c | 36 ++++++++++++++++++++++++++++++++++++
lib/Kconfig.debug | 5 +++++
5 files changed, 81 insertions(+)

diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 568b2171f335..ae0b7cd69249 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -637,6 +637,8 @@ static void k_spec(struct vc_data *vc, unsigned char value, char up_flag)
kbd->kbdmode == VC_OFF) &&
value != KVAL(K_SAK))
return; /* SAK is allowed even in raw mode */
+ if (twist_flags.disable_kbd_k_spec_handler)
+ return;
fn_handler[value](vc);
}

diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 82d91547d122..78fdbb4f17b1 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -1038,4 +1038,12 @@ static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { }
/* OTHER_WRITABLE? Generally considered a bad idea. */ \
BUILD_BUG_ON_ZERO((perms) & 2) + \
(perms))
+
+/* Flags for twisting kernel behavior. */
+struct twist_flags {
+ bool disable_kbd_k_spec_handler;
+ bool disable_reboot_request;
+};
+extern struct twist_flags twist_flags;
+
#endif
diff --git a/init/main.c b/init/main.c
index 0ead83e86b5a..15eecd253b61 100644
--- a/init/main.c
+++ b/init/main.c
@@ -1531,3 +1531,33 @@ static noinline void __init kernel_init_freeable(void)

integrity_load_keys();
}
+
+/* Flags for twisting kernel behavior. */
+struct twist_flags twist_flags __ro_after_init;
+EXPORT_SYMBOL(twist_flags);
+static __initdata char default_twist_flags[] __initdata = CONFIG_DEFAULT_TWIST_FLAGS;
+static __initdata char *chosen_twist_flags = default_twist_flags;
+
+static int __init overwrite_twist_flags(char *str)
+{
+ chosen_twist_flags = str;
+ return 1;
+}
+__setup("twist_flags=", overwrite_twist_flags);
+
+static int __init apply_twist_flags(void)
+{
+ char *flags = chosen_twist_flags;
+ char *name;
+
+ while ((name = strsep(&flags, ",")) != NULL) {
+ if (!strcmp(name, "kbd-disable-hotkeys"))
+ twist_flags.disable_kbd_k_spec_handler = true;
+ else if (!strcmp(name, "disable-reboot-request"))
+ twist_flags.disable_reboot_request = true;
+ else
+ printk(KERN_INFO "Ignoring unknown twist option '%s'.\n", name);
+ }
+ return 0;
+}
+late_initcall(apply_twist_flags);
diff --git a/kernel/reboot.c b/kernel/reboot.c
index 491f1347bf43..63cec97a9e59 100644
--- a/kernel/reboot.c
+++ b/kernel/reboot.c
@@ -63,6 +63,8 @@ EXPORT_SYMBOL_GPL(pm_power_off_prepare);
*/
void emergency_restart(void)
{
+ if (twist_flags.disable_reboot_request)
+ panic("reboot request is disabled");
kmsg_dump(KMSG_DUMP_EMERG);
machine_emergency_restart();
}
@@ -243,6 +245,8 @@ void migrate_to_reboot_cpu(void)
*/
void kernel_restart(char *cmd)
{
+ if (twist_flags.disable_reboot_request)
+ panic("reboot request is disabled");
kernel_restart_prepare(cmd);
migrate_to_reboot_cpu();
syscore_shutdown();
@@ -270,6 +274,8 @@ static void kernel_shutdown_prepare(enum system_states state)
*/
void kernel_halt(void)
{
+ if (twist_flags.disable_reboot_request)
+ panic("reboot request is disabled");
kernel_shutdown_prepare(SYSTEM_HALT);
migrate_to_reboot_cpu();
syscore_shutdown();
@@ -286,6 +292,8 @@ EXPORT_SYMBOL_GPL(kernel_halt);
*/
void kernel_power_off(void)
{
+ if (twist_flags.disable_reboot_request)
+ panic("reboot request is disabled");
kernel_shutdown_prepare(SYSTEM_POWER_OFF);
if (pm_power_off_prepare)
pm_power_off_prepare();
@@ -344,6 +352,10 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
mutex_lock(&system_transition_mutex);
switch (cmd) {
case LINUX_REBOOT_CMD_RESTART:
+ if (twist_flags.disable_reboot_request) {
+ ret = -EPERM;
+ break;
+ }
kernel_restart(NULL);
break;

@@ -356,11 +368,19 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
break;

case LINUX_REBOOT_CMD_HALT:
+ if (twist_flags.disable_reboot_request) {
+ ret = -EPERM;
+ break;
+ }
kernel_halt();
do_exit(0);
panic("cannot halt");

case LINUX_REBOOT_CMD_POWER_OFF:
+ if (twist_flags.disable_reboot_request) {
+ ret = -EPERM;
+ break;
+ }
kernel_power_off();
do_exit(0);
break;
@@ -373,17 +393,29 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
}
buffer[sizeof(buffer) - 1] = '\0';

+ if (twist_flags.disable_reboot_request) {
+ ret = -EPERM;
+ break;
+ }
kernel_restart(buffer);
break;

#ifdef CONFIG_KEXEC_CORE
case LINUX_REBOOT_CMD_KEXEC:
+ if (twist_flags.disable_reboot_request) {
+ ret = -EPERM;
+ break;
+ }
ret = kernel_kexec();
break;
#endif

#ifdef CONFIG_HIBERNATION
case LINUX_REBOOT_CMD_SW_SUSPEND:
+ if (twist_flags.disable_reboot_request) {
+ ret = -EPERM;
+ break;
+ }
ret = hibernate();
break;
#endif
@@ -493,6 +525,8 @@ static DECLARE_WORK(poweroff_work, poweroff_work_func);
*/
void orderly_poweroff(bool force)
{
+ if (twist_flags.disable_reboot_request)
+ panic("reboot request is disabled");
if (force) /* do not override the pending "true" */
poweroff_force = true;
schedule_work(&poweroff_work);
@@ -514,6 +548,8 @@ static DECLARE_WORK(reboot_work, reboot_work_func);
*/
void orderly_reboot(void)
{
+ if (twist_flags.disable_reboot_request)
+ panic("reboot request is disabled");
schedule_work(&reboot_work);
}
EXPORT_SYMBOL_GPL(orderly_reboot);
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 498d344ea53a..41cfabc74ad7 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -2338,4 +2338,9 @@ config HYPERV_TESTING

endmenu # "Kernel Testing and Coverage"

+menuconfig DEFAULT_TWIST_FLAGS
+ string "Default twist options (DANGEROUS)"
+ help
+ Don't specify anything unless you know what you are doing.
+
endmenu # Kernel hacking