Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

veth: prevent oops caused by netdev destructor

From: Stephen Hemminger <shemminger@vyatta.com>

The veth driver will oops if sysfs hooks are open while module is removed.

The net device destructor can not point to code in a module; basically
there are only two possible safe values: NULL - no destructor, or
free_netdev - free on last use

Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Stephen Hemminger and committed by
David S. Miller
ae0e8e82 6a783c90

+16 -25
+16 -25
drivers/net/veth.c
··· 210 210 211 211 static struct net_device_stats *veth_get_stats(struct net_device *dev) 212 212 { 213 - struct veth_priv *priv; 214 - struct net_device_stats *dev_stats; 215 - int cpu; 213 + struct veth_priv *priv = netdev_priv(dev); 214 + struct net_device_stats *dev_stats = &dev->stats; 215 + unsigned int cpu; 216 216 struct veth_net_stats *stats; 217 - 218 - priv = netdev_priv(dev); 219 - dev_stats = &dev->stats; 220 217 221 218 dev_stats->rx_packets = 0; 222 219 dev_stats->tx_packets = 0; ··· 222 225 dev_stats->tx_dropped = 0; 223 226 dev_stats->rx_dropped = 0; 224 227 225 - for_each_online_cpu(cpu) { 226 - stats = per_cpu_ptr(priv->stats, cpu); 228 + if (priv->stats) 229 + for_each_online_cpu(cpu) { 230 + stats = per_cpu_ptr(priv->stats, cpu); 227 231 228 - dev_stats->rx_packets += stats->rx_packets; 229 - dev_stats->tx_packets += stats->tx_packets; 230 - dev_stats->rx_bytes += stats->rx_bytes; 231 - dev_stats->tx_bytes += stats->tx_bytes; 232 - dev_stats->tx_dropped += stats->tx_dropped; 233 - dev_stats->rx_dropped += stats->rx_dropped; 234 - } 232 + dev_stats->rx_packets += stats->rx_packets; 233 + dev_stats->tx_packets += stats->tx_packets; 234 + dev_stats->rx_bytes += stats->rx_bytes; 235 + dev_stats->tx_bytes += stats->tx_bytes; 236 + dev_stats->tx_dropped += stats->tx_dropped; 237 + dev_stats->rx_dropped += stats->rx_dropped; 238 + } 235 239 236 240 return dev_stats; 237 241 } ··· 259 261 netif_carrier_off(dev); 260 262 netif_carrier_off(priv->peer); 261 263 264 + free_percpu(priv->stats); 265 + priv->stats = NULL; 262 266 return 0; 263 267 } 264 268 ··· 291 291 return 0; 292 292 } 293 293 294 - static void veth_dev_free(struct net_device *dev) 295 - { 296 - struct veth_priv *priv; 297 - 298 - priv = netdev_priv(dev); 299 - free_percpu(priv->stats); 300 - free_netdev(dev); 301 - } 302 - 303 294 static const struct net_device_ops veth_netdev_ops = { 304 295 .ndo_init = veth_dev_init, 305 296 .ndo_open = veth_open, ··· 308 317 dev->netdev_ops = &veth_netdev_ops; 309 318 dev->ethtool_ops = &veth_ethtool_ops; 310 319 dev->features |= NETIF_F_LLTX; 311 - dev->destructor = veth_dev_free; 320 + dev->destructor = free_netdev; 312 321 } 313 322 314 323 /*