Re: [PATCH v3] IB/ipoib: fix dangling pointer references to ipoib_neigh and ipoib_path

2010-03-29 Thread Roland Dreier
 > > > @@ -249,44 +252,26 @@ static int __path_add(struct net_device *dev, 
 > > > struct ipoib_path *path)
 > > >  n = &pn->rb_left;
 > > >  else if (ret > 0)
 > > >  n = &pn->rb_right;
 > > > -else
 > > > -return -EEXIST;
 > > > +else /* Should never happen since we always search 
 > > > first */
 > > > +return;
 > > >  }
 > > 
 > > Why not remove the last else and change the "else if" into else?
 > 
 > I don't understand. This is left, right, or return.
 > I'm only changing the return value to void since it is
 > never used.

It would probably be better to split that cleanup out into a separate
patch.  And since we have a "should never happen" condition in the code,
I guess we should either trust our code and just assume that it really
never happens (ie have just an if-else as Eli suggests), or add
something like a WARN_ONCE() if it actually does happen (probably safer)
-- 
Roland Dreier  || For corporate legal information go to:
http://www.cisco.com/web/about/doing_business/legal/cri/index.html
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v3] IB/ipoib: fix dangling pointer references to ipoib_neigh and ipoib_path

2010-03-29 Thread Ralph Campbell
On Sun, 2010-03-28 at 09:02 -0700, Eli Cohen wrote:
> On Thu, Mar 04, 2010 at 10:58:27AM -0800, Ralph Campbell wrote:
> > Subject: [PATCH v3] IB/ipoib: fix dangling pointer references to 
> > ipoib_neigh and ipoib_path
> > 
> > When using connected mode, ipoib_cm_create_tx() kmallocs a
> > struct ipoib_cm_tx which contains pointers to ipoib_neigh and
> > ipoib_path. If the paths are flushed or the struct neighbour is
> > destroyed, the pointers held by struct ipoib_cm_tx can reference
> > freed memory.
> > 
> I could use some more content here as this is quite a large patch.
> Anyway below are some comments. I think besides reviewing this patch
> we need to make sure it has been checked in real life.

Do you mean more details about how ipoib_cm_tx can be used after
being freed or more about how the changes fix the problem?

I have been waiting for our internal regression tests to finish
and to collect review feedback before sending out the final version
of the patch.

> > +/*
> > + * Search the list of connections to be started and remove any entries
> > + * which match the path being destroyed.
> > + *
> > + * This should be called with netif_tx_lock_bh() and priv->lock held.
> > + */
> > +void ipoib_cm_flush_path(struct net_device *dev, struct ipoib_path *path)
> > +{
> > +   struct ipoib_dev_priv *priv = netdev_priv(dev);
> > +   struct ipoib_cm_tx *tx;
> > +
> > +   list_for_each_entry(tx, &priv->cm.start_list, list) {
> > +   tx = list_entry(priv->cm.start_list.next, typeof(*tx), list);
> > +   if (tx->path == path) {
> > +   tx->path = NULL;
> > +   list_del_init(&tx->list);
> > +   break;
> > +   }
> > }
> >  }
> 
> Looks to me like we may find struct ipoib_cm_tx objects hanging and
> not freed. This could happen if they were just added to start_list and
> removed by ipoib_cm_flush_path() before being processed by
> ipoib_cm_tx_start().

Quite right. I should also use list_for_each_entry_safe().
I will fix this.

> > -static int __path_add(struct net_device *dev, struct ipoib_path *path)
> > +static void __path_add(struct net_device *dev, struct ipoib_path *path)
> >  {
> > struct ipoib_dev_priv *priv = netdev_priv(dev);
> > struct rb_node **n = &priv->path_tree.rb_node;
> > @@ -249,44 +252,26 @@ static int __path_add(struct net_device *dev, struct 
> > ipoib_path *path)
> > n = &pn->rb_left;
> > else if (ret > 0)
> > n = &pn->rb_right;
> > -   else
> > -   return -EEXIST;
> > +   else /* Should never happen since we always search first */
> > +   return;
> > }
> 
> Why not remove the last else and change the "else if" into else?

I don't understand. This is left, right, or return.
I'm only changing the return value to void since it is
never used.

> > }
> > @@ -460,19 +446,13 @@ static void path_rec_completion(int status,
> > memcpy(&neigh->dgid.raw, &path->pathrec.dgid.raw,
> >sizeof(union ib_gid));
> >  
> > -   if (ipoib_cm_enabled(dev, neigh->neighbour)) {
> > -   if (!ipoib_cm_get(neigh))
> > -   ipoib_cm_set(neigh, 
> > ipoib_cm_create_tx(dev,
> > -  
> > path,
> > -  
> > neigh));
> > -   if (!ipoib_cm_get(neigh)) {
> > -   list_del(&neigh->list);
> > -   if (neigh->ah)
> > -   ipoib_put_ah(neigh->ah);
> > -   ipoib_neigh_free(dev, neigh);
> > -   continue;
> > -   }
> > -   }
> ipoib_cm_set() is called only once in neigh_alloc(), setting neigh->cm
> to  NULL, and never again so all calls to ipoib_cm_get() will return
> NULL.

ipoib_cm_set() is only called once since I changed
ipoib_cm_create_tx() to initialize neigh->cm and return void.
To me, it is more clear to let ipoib_cm.c do as much of
the CM specific work as possible instead of defining two
versions of a function for when CM is compiled in or not.
I guess I could change neigh_alloc() to call kzmalloc()
and remove ip

Re: [PATCH v3] IB/ipoib: fix dangling pointer references to ipoib_neigh and ipoib_path

2010-03-28 Thread Eli Cohen
On Thu, Mar 04, 2010 at 10:58:27AM -0800, Ralph Campbell wrote:
> Subject: [PATCH v3] IB/ipoib: fix dangling pointer references to ipoib_neigh 
> and ipoib_path
> 
> When using connected mode, ipoib_cm_create_tx() kmallocs a
> struct ipoib_cm_tx which contains pointers to ipoib_neigh and
> ipoib_path. If the paths are flushed or the struct neighbour is
> destroyed, the pointers held by struct ipoib_cm_tx can reference
> freed memory.
> 
I could use some more content here as this is quite a large patch.
Anyway below are some comments. I think besides reviewing this patch
we need to make sure it has been checked in real life.

> +/*
> + * Search the list of connections to be started and remove any entries
> + * which match the path being destroyed.
> + *
> + * This should be called with netif_tx_lock_bh() and priv->lock held.
> + */
> +void ipoib_cm_flush_path(struct net_device *dev, struct ipoib_path *path)
> +{
> + struct ipoib_dev_priv *priv = netdev_priv(dev);
> + struct ipoib_cm_tx *tx;
> +
> + list_for_each_entry(tx, &priv->cm.start_list, list) {
> + tx = list_entry(priv->cm.start_list.next, typeof(*tx), list);
> + if (tx->path == path) {
> + tx->path = NULL;
> + list_del_init(&tx->list);
> + break;
> + }
>   }
>  }

Looks to me like we may find struct ipoib_cm_tx objects hanging and
not freed. This could happen if they were just added to start_list and
removed by ipoib_cm_flush_path() before being processed by
ipoib_cm_tx_start().


> -static int __path_add(struct net_device *dev, struct ipoib_path *path)
> +static void __path_add(struct net_device *dev, struct ipoib_path *path)
>  {
>   struct ipoib_dev_priv *priv = netdev_priv(dev);
>   struct rb_node **n = &priv->path_tree.rb_node;
> @@ -249,44 +252,26 @@ static int __path_add(struct net_device *dev, struct 
> ipoib_path *path)
>   n = &pn->rb_left;
>   else if (ret > 0)
>   n = &pn->rb_right;
> - else
> - return -EEXIST;
> + else /* Should never happen since we always search first */
> + return;
>   }

Why not remove the last else and change the "else if" into else?

>   }
> @@ -460,19 +446,13 @@ static void path_rec_completion(int status,
>   memcpy(&neigh->dgid.raw, &path->pathrec.dgid.raw,
>  sizeof(union ib_gid));
>  
> - if (ipoib_cm_enabled(dev, neigh->neighbour)) {
> - if (!ipoib_cm_get(neigh))
> - ipoib_cm_set(neigh, 
> ipoib_cm_create_tx(dev,
> -
> path,
> -
> neigh));
> - if (!ipoib_cm_get(neigh)) {
> - list_del(&neigh->list);
> - if (neigh->ah)
> - ipoib_put_ah(neigh->ah);
> - ipoib_neigh_free(dev, neigh);
> - continue;
> - }
> - }
ipoib_cm_set() is called only once in neigh_alloc(), setting neigh->cm
to  NULL, and never again so all calls to ipoib_cm_get() will return
NULL.

> + /*
> +  * If connected mode is enabled but not started,
> +  * start getting a connection.
> +  */
> + if (ipoib_cm_enabled(dev, neigh->neighbour) &&
> + !ipoib_cm_get(neigh))
> + ipoib_cm_create_tx(dev, path, neigh);
>  
>   while ((skb = __skb_dequeue(&neigh->queue)))
>   __skb_queue_tail(&skqueue, skb);
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v3] IB/ipoib: fix dangling pointer references to ipoib_neigh and ipoib_path

2010-03-04 Thread Ralph Campbell
Subject: [PATCH v3] IB/ipoib: fix dangling pointer references to ipoib_neigh 
and ipoib_path

When using connected mode, ipoib_cm_create_tx() kmallocs a
struct ipoib_cm_tx which contains pointers to ipoib_neigh and
ipoib_path. If the paths are flushed or the struct neighbour is
destroyed, the pointers held by struct ipoib_cm_tx can reference
freed memory.

Signed-off-by: Ralph Campbell 
---

Changes from V1 were to remove the kref_t and avoid the extra locking.
Changes from V2 were to fix a race where a call to ipoib_cm_destroy_tx()
while the struct ipoib_cm_tx was being processed by ipoib_cm_tx_start()
could cause a use after free.

 drivers/infiniband/ulp/ipoib/ipoib.h   |   24 ++-
 drivers/infiniband/ulp/ipoib/ipoib_cm.c|  108 +-
 drivers/infiniband/ulp/ipoib/ipoib_main.c  |  267 +++-
 drivers/infiniband/ulp/ipoib/ipoib_multicast.c |   95 +++--
 4 files changed, 225 insertions(+), 269 deletions(-)

diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h 
b/drivers/infiniband/ulp/ipoib/ipoib.h
index 753a983..84bb561 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib.h
+++ b/drivers/infiniband/ulp/ipoib/ipoib.h
@@ -415,9 +415,7 @@ static inline struct ipoib_neigh **to_ipoib_neigh(struct 
neighbour *neigh)
 INFINIBAND_ALEN, sizeof(void *));
 }
 
-struct ipoib_neigh *ipoib_neigh_alloc(struct neighbour *neigh,
- struct net_device *dev);
-void ipoib_neigh_free(struct net_device *dev, struct ipoib_neigh *neigh);
+void ipoib_neigh_flush(struct ipoib_neigh *neigh);
 
 extern struct workqueue_struct *ipoib_workqueue;
 
@@ -464,7 +462,8 @@ void ipoib_dev_cleanup(struct net_device *dev);
 
 void ipoib_mcast_join_task(struct work_struct *work);
 void ipoib_mcast_carrier_on_task(struct work_struct *work);
-void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb);
+void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb,
+ struct ipoib_neigh *neigh);
 
 void ipoib_mcast_restart_task(struct work_struct *work);
 int ipoib_mcast_start_thread(struct net_device *dev);
@@ -567,9 +566,10 @@ void ipoib_cm_dev_stop(struct net_device *dev);
 int ipoib_cm_dev_init(struct net_device *dev);
 int ipoib_cm_add_mode_attr(struct net_device *dev);
 void ipoib_cm_dev_cleanup(struct net_device *dev);
-struct ipoib_cm_tx *ipoib_cm_create_tx(struct net_device *dev, struct 
ipoib_path *path,
-   struct ipoib_neigh *neigh);
+void ipoib_cm_create_tx(struct net_device *dev, struct ipoib_path *path,
+   struct ipoib_neigh *neigh);
 void ipoib_cm_destroy_tx(struct ipoib_cm_tx *tx);
+void ipoib_cm_flush_path(struct net_device *dev, struct ipoib_path *path);
 void ipoib_cm_skb_too_long(struct net_device *dev, struct sk_buff *skb,
   unsigned int mtu);
 void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc);
@@ -646,10 +646,10 @@ void ipoib_cm_dev_cleanup(struct net_device *dev)
 }
 
 static inline
-struct ipoib_cm_tx *ipoib_cm_create_tx(struct net_device *dev, struct 
ipoib_path *path,
-   struct ipoib_neigh *neigh)
+void ipoib_cm_create_tx(struct net_device *dev, struct ipoib_path *path,
+   struct ipoib_neigh *neigh)
 {
-   return NULL;
+   return;
 }
 
 static inline
@@ -659,6 +659,12 @@ void ipoib_cm_destroy_tx(struct ipoib_cm_tx *tx)
 }
 
 static inline
+void ipoib_cm_flush_path(struct net_device *dev, struct ipoib_path *path)
+{
+   return;
+}
+
+static inline
 int ipoib_cm_add_mode_attr(struct net_device *dev)
 {
return 0;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c 
b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
index 30bdf42..27f494d 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
@@ -794,31 +794,14 @@ void ipoib_cm_handle_tx_wc(struct net_device *dev, struct 
ib_wc *wc)
 
if (wc->status != IB_WC_SUCCESS &&
wc->status != IB_WC_WR_FLUSH_ERR) {
-   struct ipoib_neigh *neigh;
-
ipoib_dbg(priv, "failed cm send event "
   "(status=%d, wrid=%d vend_err %x)\n",
   wc->status, wr_id, wc->vendor_err);
 
spin_lock_irqsave(&priv->lock, flags);
-   neigh = tx->neigh;
-
-   if (neigh) {
-   neigh->cm = NULL;
-   list_del(&neigh->list);
-   if (neigh->ah)
-   ipoib_put_ah(neigh->ah);
-   ipoib_neigh_free(dev, neigh);
-
-   tx->neigh = NULL;
-   }
-
-   if (test_and_clear_bit(IPOIB_FLAG_INITIALIZED, &tx->flags)) {
-   list_move(&tx->list, &priv-