IPoIB: Fix use-after-free of multicast object
authorPatrick McHardy <kaber@trash.net>
Thu, 30 Aug 2012 07:01:30 +0000 (07:01 +0000)
committerRoland Dreier <roland@purestorage.com>
Mon, 1 Oct 2012 03:32:33 +0000 (20:32 -0700)
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>
Cc: <stable@vger.kernel.org>
Signed-off-by: Roland Dreier <roland@purestorage.com>
drivers/infiniband/ulp/ipoib/ipoib_main.c
drivers/infiniband/ulp/ipoib/ipoib_multicast.c

index 1e19b5ae7c479a5865837ffa4b08f7f5c89bdcdc..ea0dfc77a7f46a98f27037171dddd789e45636ee 100644 (file)
@@ -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)) {
index 75367249f447f497851e76a0a4312b6000989ff2..cecb98a4c662b7d82053f152a3da46d653e4d6ca 100644 (file)
@@ -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);