IPoIB: Fix use-after-free of multicast object
authorPatrick McHardy <kaber@trash.net>
Thu, 30 Aug 2012 07:01:30 +0000 (07:01 +0000)
committerBen Hutchings <ben@decadent.org.uk>
Wed, 17 Oct 2012 02:49:17 +0000 (03:49 +0100)
commit bea1e22df494a729978e7f2c54f7bda328f74bc3 upstream.

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

Commit c8c2afe360b7 ("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: Patrick McHardy <kaber@trash.net>
Signed-off-by: Roland Dreier <roland@purestorage.com>
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
drivers/infiniband/ulp/ipoib/ipoib_main.c
drivers/infiniband/ulp/ipoib/ipoib_multicast.c

index fe2fdbb..1740b82 100644 (file)
@@ -148,7 +148,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)) {
index e5069b4..80799c0 100644 (file)
@@ -190,7 +190,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);
@@ -198,10 +200,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)) {
@@ -590,14 +599,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);