forked from mirrors/linux
		
	[NET]: Fix free_netdev on register_netdev failure.
Point 1:
The unregistering of a network device schedule a netdev_run_todo.
This function calls dev->destructor when it is set and the
destructor calls free_netdev.
Point 2:
In the case of an initialization of a network device the usual code
is:
 * alloc_netdev
 * register_netdev
    -> if this one fails, call free_netdev and exit with error.
Point 3:
In the register_netdevice function at the later state, when the device
is at the registered state, a call to the netdevice_notifiers is made.
If one of the notification falls into an error, a rollback to the
registered state is done using unregister_netdevice.
Conclusion:
When a network device fails to register during initialization because
one network subsystem returned an error during a notification call
chain, the network device is freed twice because of fact 1 and fact 2.
The second free_netdev will be done with an invalid pointer.
Proposed solution:
The following patch move all the code of unregister_netdevice *except*
the call to net_set_todo, to a new function "rollback_registered".
The following functions are changed in this way:
 * register_netdevice: calls rollback_registered when a notification fails
 * unregister_netdevice: calls rollback_register + net_set_todo, the call
                         order to net_set_todo is changed because it is the
                         latest now. Since it justs add an element to a list
                         that should not break anything.
Signed-off-by: Daniel Lezcano <dlezcano@fr.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
			
			
This commit is contained in:
		
							parent
							
								
									5c41542bde
								
							
						
					
					
						commit
						93ee31f14f
					
				
					 1 changed files with 59 additions and 53 deletions
				
			
		
							
								
								
									
										112
									
								
								net/core/dev.c
									
									
									
									
									
								
							
							
						
						
									
										112
									
								
								net/core/dev.c
									
									
									
									
									
								
							| 
						 | 
					@ -3496,6 +3496,60 @@ static void net_set_todo(struct net_device *dev)
 | 
				
			||||||
	spin_unlock(&net_todo_list_lock);
 | 
						spin_unlock(&net_todo_list_lock);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void rollback_registered(struct net_device *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						BUG_ON(dev_boot_phase);
 | 
				
			||||||
 | 
						ASSERT_RTNL();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Some devices call without registering for initialization unwind. */
 | 
				
			||||||
 | 
						if (dev->reg_state == NETREG_UNINITIALIZED) {
 | 
				
			||||||
 | 
							printk(KERN_DEBUG "unregister_netdevice: device %s/%p never "
 | 
				
			||||||
 | 
									  "was registered\n", dev->name, dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							WARN_ON(1);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						BUG_ON(dev->reg_state != NETREG_REGISTERED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* If device is running, close it first. */
 | 
				
			||||||
 | 
						dev_close(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* And unlink it from device chain. */
 | 
				
			||||||
 | 
						unlist_netdevice(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dev->reg_state = NETREG_UNREGISTERING;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						synchronize_net();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Shutdown queueing discipline. */
 | 
				
			||||||
 | 
						dev_shutdown(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Notify protocols, that we are about to destroy
 | 
				
			||||||
 | 
						   this device. They should clean all the things.
 | 
				
			||||||
 | 
						*/
 | 
				
			||||||
 | 
						call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 *	Flush the unicast and multicast chains
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						dev_addr_discard(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (dev->uninit)
 | 
				
			||||||
 | 
							dev->uninit(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Notifier chain MUST detach us from master device. */
 | 
				
			||||||
 | 
						BUG_TRAP(!dev->master);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Remove entries from kobject tree */
 | 
				
			||||||
 | 
						netdev_unregister_kobject(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						synchronize_net();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dev_put(dev);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 *	register_netdevice	- register a network device
 | 
					 *	register_netdevice	- register a network device
 | 
				
			||||||
 *	@dev: device to register
 | 
					 *	@dev: device to register
 | 
				
			||||||
| 
						 | 
					@ -3633,8 +3687,10 @@ int register_netdevice(struct net_device *dev)
 | 
				
			||||||
	/* Notify protocols, that a new device appeared. */
 | 
						/* Notify protocols, that a new device appeared. */
 | 
				
			||||||
	ret = call_netdevice_notifiers(NETDEV_REGISTER, dev);
 | 
						ret = call_netdevice_notifiers(NETDEV_REGISTER, dev);
 | 
				
			||||||
	ret = notifier_to_errno(ret);
 | 
						ret = notifier_to_errno(ret);
 | 
				
			||||||
	if (ret)
 | 
						if (ret) {
 | 
				
			||||||
		unregister_netdevice(dev);
 | 
							rollback_registered(dev);
 | 
				
			||||||
 | 
							dev->reg_state = NETREG_UNREGISTERED;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
| 
						 | 
					@ -3911,59 +3967,9 @@ void synchronize_net(void)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void unregister_netdevice(struct net_device *dev)
 | 
					void unregister_netdevice(struct net_device *dev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	BUG_ON(dev_boot_phase);
 | 
						rollback_registered(dev);
 | 
				
			||||||
	ASSERT_RTNL();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Some devices call without registering for initialization unwind. */
 | 
					 | 
				
			||||||
	if (dev->reg_state == NETREG_UNINITIALIZED) {
 | 
					 | 
				
			||||||
		printk(KERN_DEBUG "unregister_netdevice: device %s/%p never "
 | 
					 | 
				
			||||||
				  "was registered\n", dev->name, dev);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		WARN_ON(1);
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	BUG_ON(dev->reg_state != NETREG_REGISTERED);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* If device is running, close it first. */
 | 
					 | 
				
			||||||
	dev_close(dev);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* And unlink it from device chain. */
 | 
					 | 
				
			||||||
	unlist_netdevice(dev);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	dev->reg_state = NETREG_UNREGISTERING;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	synchronize_net();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Shutdown queueing discipline. */
 | 
					 | 
				
			||||||
	dev_shutdown(dev);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Notify protocols, that we are about to destroy
 | 
					 | 
				
			||||||
	   this device. They should clean all the things.
 | 
					 | 
				
			||||||
	*/
 | 
					 | 
				
			||||||
	call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
	 *	Flush the unicast and multicast chains
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	dev_addr_discard(dev);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (dev->uninit)
 | 
					 | 
				
			||||||
		dev->uninit(dev);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Notifier chain MUST detach us from master device. */
 | 
					 | 
				
			||||||
	BUG_TRAP(!dev->master);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Remove entries from kobject tree */
 | 
					 | 
				
			||||||
	netdev_unregister_kobject(dev);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Finish processing unregister after unlock */
 | 
						/* Finish processing unregister after unlock */
 | 
				
			||||||
	net_set_todo(dev);
 | 
						net_set_todo(dev);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	synchronize_net();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	dev_put(dev);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue