Skip to content

Commit

Permalink
net/bonding: Fix potential bad memory access during bonding events
Browse files Browse the repository at this point in the history
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: 69e6113 ('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>
  • Loading branch information
monis410 authored and davem330 committed Feb 9, 2015
1 parent 9dce285 commit 92e584f
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 18 deletions.
28 changes: 11 additions & 17 deletions drivers/net/bonding/bond_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand All @@ -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> */
Expand Down
2 changes: 1 addition & 1 deletion include/net/bonding.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 92e584f

Please sign in to comment.