mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-10-31 08:44:41 +00:00 
			
		
		
		
	padata: add separate cpuhp node for CPUHP_PADATA_DEAD
Removing the pcrypt module triggers this:
  general protection fault, probably for non-canonical
    address 0xdead000000000122
  CPU: 5 PID: 264 Comm: modprobe Not tainted 5.6.0+ #2
  Hardware name: QEMU Standard PC
  RIP: 0010:__cpuhp_state_remove_instance+0xcc/0x120
  Call Trace:
   padata_sysfs_release+0x74/0xce
   kobject_put+0x81/0xd0
   padata_free+0x12/0x20
   pcrypt_exit+0x43/0x8ee [pcrypt]
padata instances wrongly use the same hlist node for the online and dead
states, so __padata_free()'s second cpuhp remove call chokes on the node
that the first poisoned.
cpuhp multi-instance callbacks only walk forward in cpuhp_step->list and
the same node is linked in both the online and dead lists, so the list
corruption that results from padata_alloc() adding the node to a second
list without removing it from the first doesn't cause problems as long
as no instances are freed.
Avoid the issue by giving each state its own node.
Fixes: 894c9ef978 ("padata: validate cpumask without removed CPU during offline")
Signed-off-by: Daniel Jordan <daniel.m.jordan@oracle.com>
Cc: Herbert Xu <herbert@gondor.apana.org.au>
Cc: Steffen Klassert <steffen.klassert@secunet.com>
Cc: linux-crypto@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: stable@vger.kernel.org # v5.4+
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
			
			
This commit is contained in:
		
							parent
							
								
									b2d7e8bcaa
								
							
						
					
					
						commit
						3c2214b602
					
				
					 2 changed files with 12 additions and 8 deletions
				
			
		|  | @ -139,7 +139,8 @@ struct padata_shell { | ||||||
| /**
 | /**
 | ||||||
|  * struct padata_instance - The overall control structure. |  * struct padata_instance - The overall control structure. | ||||||
|  * |  * | ||||||
|  * @node: Used by CPU hotplug. |  * @cpu_online_node: Linkage for CPU online callback. | ||||||
|  |  * @cpu_dead_node: Linkage for CPU offline callback. | ||||||
|  * @parallel_wq: The workqueue used for parallel work. |  * @parallel_wq: The workqueue used for parallel work. | ||||||
|  * @serial_wq: The workqueue used for serial work. |  * @serial_wq: The workqueue used for serial work. | ||||||
|  * @pslist: List of padata_shell objects attached to this instance. |  * @pslist: List of padata_shell objects attached to this instance. | ||||||
|  | @ -150,7 +151,8 @@ struct padata_shell { | ||||||
|  * @flags: padata flags. |  * @flags: padata flags. | ||||||
|  */ |  */ | ||||||
| struct padata_instance { | struct padata_instance { | ||||||
| 	struct hlist_node		 node; | 	struct hlist_node		cpu_online_node; | ||||||
|  | 	struct hlist_node		cpu_dead_node; | ||||||
| 	struct workqueue_struct		*parallel_wq; | 	struct workqueue_struct		*parallel_wq; | ||||||
| 	struct workqueue_struct		*serial_wq; | 	struct workqueue_struct		*serial_wq; | ||||||
| 	struct list_head		pslist; | 	struct list_head		pslist; | ||||||
|  |  | ||||||
|  | @ -703,7 +703,7 @@ static int padata_cpu_online(unsigned int cpu, struct hlist_node *node) | ||||||
| 	struct padata_instance *pinst; | 	struct padata_instance *pinst; | ||||||
| 	int ret; | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	pinst = hlist_entry_safe(node, struct padata_instance, node); | 	pinst = hlist_entry_safe(node, struct padata_instance, cpu_online_node); | ||||||
| 	if (!pinst_has_cpu(pinst, cpu)) | 	if (!pinst_has_cpu(pinst, cpu)) | ||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
|  | @ -718,7 +718,7 @@ static int padata_cpu_dead(unsigned int cpu, struct hlist_node *node) | ||||||
| 	struct padata_instance *pinst; | 	struct padata_instance *pinst; | ||||||
| 	int ret; | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	pinst = hlist_entry_safe(node, struct padata_instance, node); | 	pinst = hlist_entry_safe(node, struct padata_instance, cpu_dead_node); | ||||||
| 	if (!pinst_has_cpu(pinst, cpu)) | 	if (!pinst_has_cpu(pinst, cpu)) | ||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
|  | @ -734,8 +734,9 @@ static enum cpuhp_state hp_online; | ||||||
| static void __padata_free(struct padata_instance *pinst) | static void __padata_free(struct padata_instance *pinst) | ||||||
| { | { | ||||||
| #ifdef CONFIG_HOTPLUG_CPU | #ifdef CONFIG_HOTPLUG_CPU | ||||||
| 	cpuhp_state_remove_instance_nocalls(CPUHP_PADATA_DEAD, &pinst->node); | 	cpuhp_state_remove_instance_nocalls(CPUHP_PADATA_DEAD, | ||||||
| 	cpuhp_state_remove_instance_nocalls(hp_online, &pinst->node); | 					    &pinst->cpu_dead_node); | ||||||
|  | 	cpuhp_state_remove_instance_nocalls(hp_online, &pinst->cpu_online_node); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| 	WARN_ON(!list_empty(&pinst->pslist)); | 	WARN_ON(!list_empty(&pinst->pslist)); | ||||||
|  | @ -939,9 +940,10 @@ static struct padata_instance *padata_alloc(const char *name, | ||||||
| 	mutex_init(&pinst->lock); | 	mutex_init(&pinst->lock); | ||||||
| 
 | 
 | ||||||
| #ifdef CONFIG_HOTPLUG_CPU | #ifdef CONFIG_HOTPLUG_CPU | ||||||
| 	cpuhp_state_add_instance_nocalls_cpuslocked(hp_online, &pinst->node); | 	cpuhp_state_add_instance_nocalls_cpuslocked(hp_online, | ||||||
|  | 						    &pinst->cpu_online_node); | ||||||
| 	cpuhp_state_add_instance_nocalls_cpuslocked(CPUHP_PADATA_DEAD, | 	cpuhp_state_add_instance_nocalls_cpuslocked(CPUHP_PADATA_DEAD, | ||||||
| 						    &pinst->node); | 						    &pinst->cpu_dead_node); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| 	put_online_cpus(); | 	put_online_cpus(); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Daniel Jordan
						Daniel Jordan