Re: [PATCH] reboot: fail run_cmd() before init so hw protection can force the action

From: Bradley Morgan

Date: Sat Nov 07 2026 - 11:11:45 EST


On 7 November 2026 14:31:29 GMT, Zak Brighton Knight
<zakbrightonknight@xxxxxxxxx> wrote:
>__hw_protection_trigger() asks userspace to power off through
>orderly_poweroff(). It only forces the power-off when running
>poweroff_cmd fails. run_cmd() waits for the exec only (UMH_WAIT_EXEC)
>so a successful exec counts as a started shutdown.
>
>Before init has started that exec can succeed and do nothing. Usermode
>helpers are enabled at rootfs_initcall and wait for the initramfs to
>be unpacked. So a built-in driver that triggers during device
>initcalls runs the initramfs copy of /sbin/poweroff. On a
>dracut/systemd initramfs that is systemctl. It finds /proc not mounted
>and decides it is in a chroot. It logs "Running in chroot, ignoring
>request." and exits 0. Nobody sees the message because a usermode
>helper starts with no stdout or stderr. The default
>CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0 arms no backup timer. The
>one-shot latch in __hw_protection_trigger() then makes the kernel
>ignore every later request. The machine runs without its critical
>temperature protection until the next boot.
>
>I hit this on a ThinkPad T520 running Fedora's 7.2.15 kernel, booted
>while still hot from a long memtest run. The ACPI zone read 101 C
>against a 100 C _CRT 0.29 s into boot. The kernel logged "HARDWARE
>PROTECTION shutdown (Temperature too high)" and the machine kept
>running for 90 minutes.
>
>It reproduces in QEMU with Fedora's 7.2.13 kernel and with current
>tip. The test uses an SSDT thermal zone whose _TMP is above its _CRT
>and an initramfs with Fedora's systemctl as /sbin/poweroff.
>hw_protection=reboot fails the same way. Setting
>sysctl.kernel.poweroff_cmd on the command line does not help either
>because do_sysctl_args() runs after the initcalls.
>
>Make run_cmd() fail with -EBUSY until system_state reaches
>SYSTEM_RUNNING. Both callers then take the fallback they already have.
>__orderly_poweroff(true) syncs and powers off. __orderly_reboot()
>syncs and restarts.
>
>Firmware that reports a bogus critical temperature only during early
>boot used to get away with it. Such a machine will now power off at
>boot, as it already does when the same reading comes after init has
>started.
>
>A short window remains between SYSTEM_RUNNING and init being ready to
>handle the request. A non-zero CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS
>still covers that.
>
>An AI coding assistant (Claude by Anthropic) helped find the cause,
>build the reproducer and draft this patch.
>
>Fixes: 10a0a8d4e3f6 ("Add common orderly_poweroff()")
>Signed-off-by: Zak Brighton Knight <zakbrightonknight@xxxxxxxxx>
>---
>I haven't tested this on the T520 itself, only in QEMU (x86_64
>defconfig with ACPI_THERMAL=y). Without the patch the guest keeps
>running after the trip. With it the guest powers off within a
>millisecond and logs "Failed to start orderly shutdown: forcing the
>issue". With hw_protection=reboot it restarts instead. A trip after
>init still runs /sbin/poweroff as before.
>
>The reproducer is an SSDT thermal zone whose _TMP is above its _CRT,
>passed to QEMU with -acpitable, and an initramfs with Fedora's
>systemctl as /sbin/poweroff. I can send it if that helps.
>
>Commit ef1d87e06ab4 ("thermal: core: Add a back up thermal shutdown
>mechanism") noted in 2017 that /sbin/poweroff can return success during
>boot without doing anything. It added
>CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS, which defaults to 0. This
>patch covers the early boot case whatever that option is set to.
>
> kernel/reboot.c | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
>
>diff --git a/kernel/reboot.c b/kernel/reboot.c
>index 9a84401adde5..14c2bbbc5a3e 100644
>--- a/kernel/reboot.c
>+++ b/kernel/reboot.c
>@@ -858,6 +858,18 @@ static int run_cmd(const char *cmd)
> NULL
> };
> int ret;
>+
>+ /*
>+ * Until init has started, no userspace can carry out an orderly
>+ * poweroff or reboot. Running the command can still succeed: an
>+ * initramfs usually has /sbin/poweroff, and it does nothing when
>+ * there is no init to talk to. As we only wait for the exec, that
>+ * would look like success and the caller would never force the
>+ * action. Fail instead.
>+ */

Way way too long! I'm sure we can simplify this.

>+ if (system_state < SYSTEM_RUNNING)
>+ return -EBUSY;

This is as easy as it can get!

Reviewed-by: Bradley Morgan <brads@xxxxxxxxxxxxxx>


>+
> argv = argv_split(GFP_KERNEL, cmd, NULL);
> if (argv) {
> ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
>
>base-commit: d48723711bae9b8cdc56719c448ba42fef28cbee
>

--- Thanks!