aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/infiniband/ulp
diff options
context:
space:
mode:
authorErez Shitrit <erezsh@mellanox.com>2016-08-28 03:58:31 -0400
committerDoug Ledford <dledford@redhat.com>2016-09-02 14:07:38 -0400
commit546481c2816ea3c061ee9d5658eb48070f69212e (patch)
treed321553fff5061af2a87523a7d69126d746a77d1 /drivers/infiniband/ulp
parent68c6bcdd8bd00394c234b915ab9b97c74104130c (diff)
IB/ipoib: Fix memory corruption in ipoib cm mode connect flow
When a new CM connection is being requested, ipoib driver copies data from the path pointer in the CM/tx object, the path object might be invalid at the point and memory corruption will happened later when now the CM driver will try using that data. The next scenario demonstrates it: neigh_add_path --> ipoib_cm_create_tx --> queue_work (pointer to path is in the cm/tx struct) #while the work is still in the queue, #the port goes down and causes the ipoib_flush_paths: ipoib_flush_paths --> path_free --> kfree(path) #at this point the work scheduled starts. ipoib_cm_tx_start --> copy from the (invalid)path pointer: (memcpy(&pathrec, &p->path->pathrec, sizeof pathrec);) -> memory corruption. To fix that the driver now starts the CM/tx connection only if that specific path exists in the general paths database. This check is protected with the relevant locks, and uses the gid from the neigh member in the CM/tx object which is valid according to the ref count that was taken by the CM/tx. Fixes: 839fcaba35 ('IPoIB: Connected mode experimental support') Signed-off-by: Erez Shitrit <erezsh@mellanox.com> Signed-off-by: Leon Romanovsky <leon@kernel.org> Signed-off-by: Doug Ledford <dledford@redhat.com>
Diffstat (limited to 'drivers/infiniband/ulp')
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib.h1
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_cm.c16
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c2
3 files changed, 18 insertions, 1 deletions
diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h
index 4f7d9b48df64..9dbfcc0ab577 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib.h
+++ b/drivers/infiniband/ulp/ipoib/ipoib.h
@@ -478,6 +478,7 @@ void ipoib_send(struct net_device *dev, struct sk_buff *skb,
478 struct ipoib_ah *address, u32 qpn); 478 struct ipoib_ah *address, u32 qpn);
479void ipoib_reap_ah(struct work_struct *work); 479void ipoib_reap_ah(struct work_struct *work);
480 480
481struct ipoib_path *__path_find(struct net_device *dev, void *gid);
481void ipoib_mark_paths_invalid(struct net_device *dev); 482void ipoib_mark_paths_invalid(struct net_device *dev);
482void ipoib_flush_paths(struct net_device *dev); 483void ipoib_flush_paths(struct net_device *dev);
483int ipoib_check_sm_sendonly_fullmember_support(struct ipoib_dev_priv *priv); 484int ipoib_check_sm_sendonly_fullmember_support(struct ipoib_dev_priv *priv);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
index 951d9abcca8b..4ad297d3de89 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
@@ -1318,6 +1318,8 @@ void ipoib_cm_destroy_tx(struct ipoib_cm_tx *tx)
1318 } 1318 }
1319} 1319}
1320 1320
1321#define QPN_AND_OPTIONS_OFFSET 4
1322
1321static void ipoib_cm_tx_start(struct work_struct *work) 1323static void ipoib_cm_tx_start(struct work_struct *work)
1322{ 1324{
1323 struct ipoib_dev_priv *priv = container_of(work, struct ipoib_dev_priv, 1325 struct ipoib_dev_priv *priv = container_of(work, struct ipoib_dev_priv,
@@ -1326,6 +1328,7 @@ static void ipoib_cm_tx_start(struct work_struct *work)
1326 struct ipoib_neigh *neigh; 1328 struct ipoib_neigh *neigh;
1327 struct ipoib_cm_tx *p; 1329 struct ipoib_cm_tx *p;
1328 unsigned long flags; 1330 unsigned long flags;
1331 struct ipoib_path *path;
1329 int ret; 1332 int ret;
1330 1333
1331 struct ib_sa_path_rec pathrec; 1334 struct ib_sa_path_rec pathrec;
@@ -1338,7 +1341,19 @@ static void ipoib_cm_tx_start(struct work_struct *work)
1338 p = list_entry(priv->cm.start_list.next, typeof(*p), list); 1341 p = list_entry(priv->cm.start_list.next, typeof(*p), list);
1339 list_del_init(&p->list); 1342 list_del_init(&p->list);
1340 neigh = p->neigh; 1343 neigh = p->neigh;
1344
1341 qpn = IPOIB_QPN(neigh->daddr); 1345 qpn = IPOIB_QPN(neigh->daddr);
1346 /*
1347 * As long as the search is with these 2 locks,
1348 * path existence indicates its validity.
1349 */
1350 path = __path_find(dev, neigh->daddr + QPN_AND_OPTIONS_OFFSET);
1351 if (!path) {
1352 pr_info("%s ignore not valid path %pI6\n",
1353 __func__,
1354 neigh->daddr + QPN_AND_OPTIONS_OFFSET);
1355 goto free_neigh;
1356 }
1342 memcpy(&pathrec, &p->path->pathrec, sizeof pathrec); 1357 memcpy(&pathrec, &p->path->pathrec, sizeof pathrec);
1343 1358
1344 spin_unlock_irqrestore(&priv->lock, flags); 1359 spin_unlock_irqrestore(&priv->lock, flags);
@@ -1350,6 +1365,7 @@ static void ipoib_cm_tx_start(struct work_struct *work)
1350 spin_lock_irqsave(&priv->lock, flags); 1365 spin_lock_irqsave(&priv->lock, flags);
1351 1366
1352 if (ret) { 1367 if (ret) {
1368free_neigh:
1353 neigh = p->neigh; 1369 neigh = p->neigh;
1354 if (neigh) { 1370 if (neigh) {
1355 neigh->cm = NULL; 1371 neigh->cm = NULL;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 74bcaa064226..cc1c1b062ea5 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -485,7 +485,7 @@ int ipoib_set_mode(struct net_device *dev, const char *buf)
485 return -EINVAL; 485 return -EINVAL;
486} 486}
487 487
488static struct ipoib_path *__path_find(struct net_device *dev, void *gid) 488struct ipoib_path *__path_find(struct net_device *dev, void *gid)
489{ 489{
490 struct ipoib_dev_priv *priv = netdev_priv(dev); 490 struct ipoib_dev_priv *priv = netdev_priv(dev);
491 struct rb_node *n = priv->path_tree.rb_node; 491 struct rb_node *n = priv->path_tree.rb_node;