Skip to content

Commit 546481c

Browse files
Erez Shitritdledford
authored andcommitted
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: 839fcab ('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>
1 parent 68c6bcd commit 546481c

File tree

3 files changed

+18
-1
lines changed

3 files changed

+18
-1
lines changed

drivers/infiniband/ulp/ipoib/ipoib.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,7 @@ void ipoib_send(struct net_device *dev, struct sk_buff *skb,
478478
struct ipoib_ah *address, u32 qpn);
479479
void ipoib_reap_ah(struct work_struct *work);
480480

481+
struct ipoib_path *__path_find(struct net_device *dev, void *gid);
481482
void ipoib_mark_paths_invalid(struct net_device *dev);
482483
void ipoib_flush_paths(struct net_device *dev);
483484
int ipoib_check_sm_sendonly_fullmember_support(struct ipoib_dev_priv *priv);

drivers/infiniband/ulp/ipoib/ipoib_cm.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1318,6 +1318,8 @@ void ipoib_cm_destroy_tx(struct ipoib_cm_tx *tx)
13181318
}
13191319
}
13201320

1321+
#define QPN_AND_OPTIONS_OFFSET 4
1322+
13211323
static void ipoib_cm_tx_start(struct work_struct *work)
13221324
{
13231325
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)
13261328
struct ipoib_neigh *neigh;
13271329
struct ipoib_cm_tx *p;
13281330
unsigned long flags;
1331+
struct ipoib_path *path;
13291332
int ret;
13301333

13311334
struct ib_sa_path_rec pathrec;
@@ -1338,7 +1341,19 @@ static void ipoib_cm_tx_start(struct work_struct *work)
13381341
p = list_entry(priv->cm.start_list.next, typeof(*p), list);
13391342
list_del_init(&p->list);
13401343
neigh = p->neigh;
1344+
13411345
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+
}
13421357
memcpy(&pathrec, &p->path->pathrec, sizeof pathrec);
13431358

13441359
spin_unlock_irqrestore(&priv->lock, flags);
@@ -1350,6 +1365,7 @@ static void ipoib_cm_tx_start(struct work_struct *work)
13501365
spin_lock_irqsave(&priv->lock, flags);
13511366

13521367
if (ret) {
1368+
free_neigh:
13531369
neigh = p->neigh;
13541370
if (neigh) {
13551371
neigh->cm = NULL;

drivers/infiniband/ulp/ipoib/ipoib_main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ int ipoib_set_mode(struct net_device *dev, const char *buf)
485485
return -EINVAL;
486486
}
487487

488-
static struct ipoib_path *__path_find(struct net_device *dev, void *gid)
488+
struct ipoib_path *__path_find(struct net_device *dev, void *gid)
489489
{
490490
struct ipoib_dev_priv *priv = netdev_priv(dev);
491491
struct rb_node *n = priv->path_tree.rb_node;

0 commit comments

Comments
 (0)