[ofa-general] [PATCH] cma: fix access to freed memory

Eli Cohen eli at mellanox.co.il
Mon Aug 3 02:25:29 PDT 2009


rdma_join_multicast() allocates struct cma_multicast and then proceeds to join
to a multicast address. However, the join operation completes in another
context and the allocated struct could be released if the user destroys either
the rdma_id object or decides to leave the multicast group while the join is in
progress. This patch uses reference counting to to avoid such situation. It
also protects removal from id_priv->mc_list in cma_leave_mc_groups().

Signed-off-by: Eli Cohen <eli at mellanox.co.il>
---
 drivers/infiniband/core/cma.c |   23 +++++++++++++++++++----
 1 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 851de83..8fee477 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -157,6 +157,7 @@ struct cma_multicast {
 	struct list_head	list;
 	void			*context;
 	struct sockaddr_storage	addr;
+	atomic_t		refcount;
 };
 
 struct cma_work {
@@ -290,6 +291,12 @@ static inline void cma_deref_dev(struct cma_device *cma_dev)
 		complete(&cma_dev->comp);
 }
 
+void cma_deref_mc(struct cma_multicast *mc)
+{
+	if (atomic_dec_and_test(&mc->refcount))
+		kfree(mc);
+}
+
 static void cma_detach_from_dev(struct rdma_id_private *id_priv)
 {
 	list_del(&id_priv->list);
@@ -822,13 +829,17 @@ static void cma_leave_mc_groups(struct rdma_id_private *id_priv)
 {
 	struct cma_multicast *mc;
 
+	spin_lock_irq(&id_priv->lock);
 	while (!list_empty(&id_priv->mc_list)) {
 		mc = container_of(id_priv->mc_list.next,
 				  struct cma_multicast, list);
 		list_del(&mc->list);
+		spin_unlock_irq(&id_priv->lock);
 		ib_sa_free_multicast(mc->multicast.ib);
-		kfree(mc);
+		cma_deref_mc(mc);
+		spin_lock_irq(&id_priv->lock);
 	}
+	spin_unlock_irq(&id_priv->lock);
 }
 
 void rdma_destroy_id(struct rdma_cm_id *id)
@@ -2643,7 +2654,7 @@ static int cma_ib_mc_handler(int status, struct ib_sa_multicast *multicast)
 	id_priv = mc->id_priv;
 	if (cma_disable_callback(id_priv, CMA_ADDR_BOUND) &&
 	    cma_disable_callback(id_priv, CMA_ADDR_RESOLVED))
-		return 0;
+		goto out;
 
 	mutex_lock(&id_priv->qp_mutex);
 	if (!status && id_priv->id.qp)
@@ -2669,10 +2680,12 @@ static int cma_ib_mc_handler(int status, struct ib_sa_multicast *multicast)
 		cma_exch(id_priv, CMA_DESTROYING);
 		mutex_unlock(&id_priv->handler_mutex);
 		rdma_destroy_id(&id_priv->id);
-		return 0;
+		goto out;
 	}
 
 	mutex_unlock(&id_priv->handler_mutex);
+out:
+	cma_deref_mc(mc);
 	return 0;
 }
 
@@ -2759,11 +2772,13 @@ int rdma_join_multicast(struct rdma_cm_id *id, struct sockaddr *addr,
 	memcpy(&mc->addr, addr, ip_addr_size(addr));
 	mc->context = context;
 	mc->id_priv = id_priv;
+	atomic_set(&mc->refcount, 1);
 
 	spin_lock(&id_priv->lock);
 	list_add(&mc->list, &id_priv->mc_list);
 	spin_unlock(&id_priv->lock);
 
+	atomic_inc(&mc->refcount);
 	switch (rdma_node_get_transport(id->device->node_type)) {
 	case RDMA_TRANSPORT_IB:
 		ret = cma_join_ib_multicast(id_priv, mc);
@@ -2800,7 +2815,7 @@ void rdma_leave_multicast(struct rdma_cm_id *id, struct sockaddr *addr)
 						&mc->multicast.ib->rec.mgid,
 						mc->multicast.ib->rec.mlid);
 			ib_sa_free_multicast(mc->multicast.ib);
-			kfree(mc);
+			cma_deref_mc(mc);
 			return;
 		}
 	}
-- 
1.6.3.3




More information about the general mailing list