smp/hotplug: Handle removal correctly in cpuhp_store_callbacks()

If cpuhp_store_callbacks() is called for CPUHP_AP_ONLINE_DYN or
CPUHP_BP_PREPARE_DYN, which are the indicators for dynamically allocated
states, then cpuhp_store_callbacks() allocates a new dynamic state. The
first allocation in each range returns CPUHP_AP_ONLINE_DYN or
CPUHP_BP_PREPARE_DYN.

If cpuhp_remove_state() is invoked for one of these states, then there is
no protection against the allocation mechanism. So the removal, which
should clear the callbacks and the name, gets a new state assigned and
clears that one.

As a consequence the state which should be cleared stays initialized. A
consecutive CPU hotplug operation dereferences the state callbacks and
accesses either freed or reused memory, resulting in crashes.

Add a protection against this by checking the name argument for NULL. If
it's NULL it's a removal. If not, it's an allocation.

[ tglx: Added a comment and massaged changelog ]

Fixes: 5b7aa87e0482 ("cpu/hotplug: Implement setup/removal interface")
Signed-off-by: Ethan Barnes <ethan.barnes@sandisk.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@kernel.or>
Cc: "Srivatsa S. Bhat" <srivatsa@mit.edu>
Cc: Sebastian Siewior <bigeasy@linutronix.d>
Cc: Paul McKenney <paulmck@linux.vnet.ibm.com>
Cc: stable@vger.kernel.org
Link: http://lkml.kernel.org/r/DM2PR04MB398242FC7776D603D9F99C894A60@DM2PR04MB398.namprd04.prod.outlook.com

authored by

Ethan Barnes and committed by
Thomas Gleixner
0c96b273 935acd3f

+11 -1
+11 -1
kernel/cpu.c
··· 1252 1252 struct cpuhp_step *sp; 1253 1253 int ret = 0; 1254 1254 1255 - if (state == CPUHP_AP_ONLINE_DYN || state == CPUHP_BP_PREPARE_DYN) { 1255 + /* 1256 + * If name is NULL, then the state gets removed. 1257 + * 1258 + * CPUHP_AP_ONLINE_DYN and CPUHP_BP_PREPARE_DYN are handed out on 1259 + * the first allocation from these dynamic ranges, so the removal 1260 + * would trigger a new allocation and clear the wrong (already 1261 + * empty) state, leaving the callbacks of the to be cleared state 1262 + * dangling, which causes wreckage on the next hotplug operation. 1263 + */ 1264 + if (name && (state == CPUHP_AP_ONLINE_DYN || 1265 + state == CPUHP_BP_PREPARE_DYN)) { 1256 1266 ret = cpuhp_reserve_state(state); 1257 1267 if (ret < 0) 1258 1268 return ret;