net/bonding: Fix potential bad memory access during bonding events
authorMoni Shoua <monis@mellanox.com>
Sun, 8 Feb 2015 09:49:32 +0000 (11:49 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 9 Feb 2015 22:03:53 +0000 (14:03 -0800)
When queuing work to send the NETDEV_BONDING_INFO netdev event, it's
possible that when the work is executed, the pointer to the slave
becomes invalid. This can happen if between queuing the event and the
execution of the work, the net-device was un-ensvaled and re-enslaved.

Fix that by queuing a work with the data of the slave instead of the
slave structure.

Fixes: 69e6113343cf ('net/bonding: Notify state change on slaves')
Reported-by: Nikolay Aleksandrov <nikolay@redhat.com>
Signed-off-by: Moni Shoua <monis@mellanox.com>
Signed-off-by: Or Gerlitz <ogerlitz@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/bonding/bond_main.c
include/net/bonding.h

index 679ef00d6b16a102dd9681121b6ae91cc3714a67..b979c265fc51d0a09c48ff4216a8b055ebcd1c26 100644 (file)
@@ -1196,18 +1196,11 @@ static void bond_fill_ifslave(struct slave *slave, struct ifslave *info)
        info->link_failure_count = slave->link_failure_count;
 }
 
-static void bond_netdev_notify(struct slave *slave, struct net_device *dev)
+static void bond_netdev_notify(struct net_device *dev,
+                              struct netdev_bonding_info *info)
 {
-       struct bonding *bond = slave->bond;
-       struct netdev_bonding_info bonding_info;
-
        rtnl_lock();
-       /* make sure that slave is still valid */
-       if (dev->priv_flags & IFF_BONDING) {
-               bond_fill_ifslave(slave, &bonding_info.slave);
-               bond_fill_ifbond(bond, &bonding_info.master);
-               netdev_bonding_info_change(slave->dev, &bonding_info);
-       }
+       netdev_bonding_info_change(dev, info);
        rtnl_unlock();
 }
 
@@ -1216,25 +1209,26 @@ static void bond_netdev_notify_work(struct work_struct *_work)
        struct netdev_notify_work *w =
                container_of(_work, struct netdev_notify_work, work.work);
 
-       bond_netdev_notify(w->slave, w->dev);
+       bond_netdev_notify(w->dev, &w->bonding_info);
        dev_put(w->dev);
+       kfree(w);
 }
 
 void bond_queue_slave_event(struct slave *slave)
 {
+       struct bonding *bond = slave->bond;
        struct netdev_notify_work *nnw = kzalloc(sizeof(*nnw), GFP_ATOMIC);
 
        if (!nnw)
                return;
 
-       INIT_DELAYED_WORK(&nnw->work, bond_netdev_notify_work);
-       nnw->slave = slave;
+       dev_hold(slave->dev);
        nnw->dev = slave->dev;
+       bond_fill_ifslave(slave, &nnw->bonding_info.slave);
+       bond_fill_ifbond(bond, &nnw->bonding_info.master);
+       INIT_DELAYED_WORK(&nnw->work, bond_netdev_notify_work);
 
-       if (queue_delayed_work(slave->bond->wq, &nnw->work, 0))
-               dev_hold(slave->dev);
-       else
-               kfree(nnw);
+       queue_delayed_work(slave->bond->wq, &nnw->work, 0);
 }
 
 /* enslave device <slave> to bond device <master> */
index 4e17095ad46a1fb196ac3724e2e0c33603e4b3fa..fda6feeb6c1f3fc3ae7c4929935901e31982166b 100644 (file)
@@ -152,8 +152,8 @@ struct bond_parm_tbl {
 
 struct netdev_notify_work {
        struct delayed_work     work;
-       struct slave            *slave;
        struct net_device       *dev;
+       struct netdev_bonding_info bonding_info;
 };
 
 struct slave {