Re: [syzbot] [kernel?] general protection fault in timers_dead_cpu

From: Thomas Gleixner

Date: Tue Aug 18 2026 - 05:09:42 EST


On Mon, Aug 17 2026 at 17:18, Borislav Petkov wrote:
> On Mon, Aug 17, 2026 at 04:55:53PM -0700, Borislav Petkov wrote:
>> On Mon, Aug 17, 2026 at 11:14:35PM +0200, Thomas Gleixner wrote:
>> > timers_dead_cpu()
>> > migrate timer to CPU0
>> >
>> > // Migrates the MCE timer of CPU1, which is a bug in itself
>>
>> Stupid question: can we prevent this?
>>
>> As in, this timer is not migratable, do not migrate it.

We could do that, but that's just papering over the underlying issues.

>> But then what do you do with a timer which is not migratable and its CPU goes
>> offline?
>>
>> Perhaps cancel it...

Yes, but that's not really well defined.

>> It won't matter in the MCE case, that's for sure.

Correct. You still have CMCI ...

>> Anyway, just some musings from reading this...
>
> Hmm, the down path does timer_delete_sync() so I guess I'm missing an aspect
> here about the timer migration.

Care to read my first reply where I described exactly how that happens?

The timer is rearmed by that sysfs muck _after_ the down callback
deleted it. And the same happens to CMCI. The down callback stops it and
the sysfs muck reenables it.

Alternatively we can split the hotplug callbacks and have one in the
late stage of hotplug after the point of no return, which stops the
timer and CMCI. Then let the existing one only care about the device
stuff which requires task context. Something like the untested below.

Thanks,

tglx
---
--- a/arch/x86/kernel/cpu/mce/core.c
+++ b/arch/x86/kernel/cpu/mce/core.c
@@ -2762,23 +2762,33 @@ static int mce_cpu_dead(unsigned int cpu
return 0;
}

-static int mce_cpu_online(unsigned int cpu)
+static int mce_cpu_starting(unsigned int cpu)
{
struct timer_list *t = this_cpu_ptr(&mce_timer);

- mce_device_create(cpu);
- mce_threshold_create_device(cpu);
mce_reenable_cpu();
mce_start_timer(t);
return 0;
}

-static int mce_cpu_pre_down(unsigned int cpu)
+static int mce_cpu_dying(unsigned int cpu)
{
struct timer_list *t = this_cpu_ptr(&mce_timer);

mce_disable_cpu();
timer_delete_sync(t);
+ return 0;
+}
+
+static int mce_cpu_online(unsigned int cpu)
+{
+ mce_device_create(cpu);
+ mce_threshold_create_device(cpu);
+ return 0;
+}
+
+static int mce_cpu_pre_down(unsigned int cpu)
+{
mce_threshold_remove_device(cpu);
mce_device_remove(cpu);
return 0;
@@ -2841,6 +2851,14 @@ static __init int mcheck_init_device(voi
mce_cpu_dead);
if (err)
goto err_out_mem;
+ /*
+ * Invokes mce_cpu_starting() on all CPUs which are online when
+ * the state is installed.
+ */
+ err = cpuhp_setup_state(CPUHP_AP_X86_MCE_STARTING, "x86/mce:starting",
+ mce_cpu_starting, mce_cpu_dying);
+ if (err < 0)
+ goto err_out_starting;

/*
* Invokes mce_cpu_online() on all CPUs which are online when
@@ -2856,6 +2874,9 @@ static __init int mcheck_init_device(voi
return 0;

err_out_online:
+ cpuhp_remove_state(CPUHP_AP_X86_MCE_STARTING);
+
+err_out_starting:
cpuhp_remove_state(CPUHP_X86_MCE_DEAD);

err_out_mem:
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -186,6 +186,7 @@ enum cpuhp_state {
CPUHP_AP_HRTIMERS_DYING,
CPUHP_AP_TICK_DYING,
CPUHP_AP_X86_TBOOT_DYING,
+ CPUHP_AP_X86_MCE_STARTING,
CPUHP_AP_ARM_CACHE_B15_RAC_DYING,
CPUHP_AP_ONLINE,
CPUHP_TEARDOWN_CPU,