Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bea1e22d authored by Patrick McHardy's avatar Patrick McHardy Committed by Roland Dreier
Browse files

IPoIB: Fix use-after-free of multicast object



Fix a crash in ipoib_mcast_join_task().  (with help from Or Gerlitz)

Commit c8c2afe3 ("IPoIB: Use rtnl lock/unlock when changing device
flags") added a call to rtnl_lock() in ipoib_mcast_join_task(), which
is run from the ipoib_workqueue, and hence the workqueue can't be
flushed from the context of ipoib_stop().

In the current code, ipoib_stop() (which doesn't flush the workqueue)
calls ipoib_mcast_dev_flush(), which goes and deletes all the
multicast entries.  This takes place without any synchronization with
a possible running instance of ipoib_mcast_join_task() for the same
ipoib device, leading to a crash due to NULL pointer dereference.

Fix this by making sure that the workqueue is flushed before
ipoib_mcast_dev_flush() is called.  To make that possible, we move the
RTNL-lock wrapped code to ipoib_mcast_join_finish().

Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarRoland Dreier <roland@purestorage.com>
parent 979570e0
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -150,7 +150,7 @@ static int ipoib_stop(struct net_device *dev)

	netif_stop_queue(dev);

	ipoib_ib_dev_down(dev, 0);
	ipoib_ib_dev_down(dev, 1);
	ipoib_ib_dev_stop(dev, 0);

	if (!test_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags)) {
+10 −9
Original line number Diff line number Diff line
@@ -175,7 +175,9 @@ static int ipoib_mcast_join_finish(struct ipoib_mcast *mcast,

	mcast->mcmember = *mcmember;

	/* Set the cached Q_Key before we attach if it's the broadcast group */
	/* Set the multicast MTU and cached Q_Key before we attach if it's
	 * the broadcast group.
	 */
	if (!memcmp(mcast->mcmember.mgid.raw, priv->dev->broadcast + 4,
		    sizeof (union ib_gid))) {
		spin_lock_irq(&priv->lock);
@@ -183,10 +185,17 @@ static int ipoib_mcast_join_finish(struct ipoib_mcast *mcast,
			spin_unlock_irq(&priv->lock);
			return -EAGAIN;
		}
		priv->mcast_mtu = IPOIB_UD_MTU(ib_mtu_enum_to_int(priv->broadcast->mcmember.mtu));
		priv->qkey = be32_to_cpu(priv->broadcast->mcmember.qkey);
		spin_unlock_irq(&priv->lock);
		priv->tx_wr.wr.ud.remote_qkey = priv->qkey;
		set_qkey = 1;

		if (!ipoib_cm_admin_enabled(dev)) {
			rtnl_lock();
			dev_set_mtu(dev, min(priv->mcast_mtu, priv->admin_mtu));
			rtnl_unlock();
		}
	}

	if (!test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags)) {
@@ -574,14 +583,6 @@ void ipoib_mcast_join_task(struct work_struct *work)
		return;
	}

	priv->mcast_mtu = IPOIB_UD_MTU(ib_mtu_enum_to_int(priv->broadcast->mcmember.mtu));

	if (!ipoib_cm_admin_enabled(dev)) {
		rtnl_lock();
		dev_set_mtu(dev, min(priv->mcast_mtu, priv->admin_mtu));
		rtnl_unlock();
	}

	ipoib_dbg_mcast(priv, "successfully joined all multicast groups\n");

	clear_bit(IPOIB_MCAST_RUN, &priv->flags);