Skip to content

Commit 410694e

Browse files
committed
Merge tag 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dledford/rdma
Pull infiniband fixes from Doug Ledford: "It's late in the game, I know, but these fixes seemed important enough to warrant a late pull request. They all involve oopses or use after frees or corruptions. Six serious fixes: - Hold the mutex around the find and corresponding update of our gid - The ifa list is rcu protected, copy its contents under rcu to avoid using a freed structure - On error, netdev might be null, so check it before trying to release it - On init, if workqueue alloc fails, fail init - The new demux patches exposed a bug in mlx5 and ipath drivers, we need to use the payload P_Key to determine the P_Key the packet arrived on because the hardware doesn't tell us the truth - Due to a couple convoluted error flows, it is possible for the CM to trigger a use_after_free and a double_free of rb nodes. Add two checks to prevent that. This code has worked for 10+ years. It is likely that some of the recent changes have caused this issue to surface. The current patch will protect us from nasty events for now while we track down why this is just now showing up" * tag 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dledford/rdma: IB/cm: Fix rb-tree duplicate free and use-after-free IB/cma: Use inner P_Key to determine netdev IB/ucma: check workqueue allocation before usage IB/cma: Potential NULL dereference in cma_id_from_event IB/core: Fix use after free of ifa IB/core: Fix memory corruption in ib_cache_gid_set_default_gid
2 parents 35df017 + 0ca81a2 commit 410694e

File tree

5 files changed

+46
-14
lines changed

5 files changed

+46
-14
lines changed

drivers/infiniband/core/cache.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,12 +508,12 @@ void ib_cache_gid_set_default_gid(struct ib_device *ib_dev, u8 port,
508508
memset(&gid_attr, 0, sizeof(gid_attr));
509509
gid_attr.ndev = ndev;
510510

511+
mutex_lock(&table->lock);
511512
ix = find_gid(table, NULL, NULL, true, GID_ATTR_FIND_MASK_DEFAULT);
512513

513514
/* Coudn't find default GID location */
514515
WARN_ON(ix < 0);
515516

516-
mutex_lock(&table->lock);
517517
if (!__ib_cache_gid_get(ib_dev, port, ix,
518518
&current_gid, &current_gid_attr) &&
519519
mode == IB_CACHE_GID_DEFAULT_MODE_SET &&

drivers/infiniband/core/cm.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,11 @@ static void cm_destroy_id(struct ib_cm_id *cm_id, int err)
835835
case IB_CM_SIDR_REQ_RCVD:
836836
spin_unlock_irq(&cm_id_priv->lock);
837837
cm_reject_sidr_req(cm_id_priv, IB_SIDR_REJECT);
838+
spin_lock_irq(&cm.lock);
839+
if (!RB_EMPTY_NODE(&cm_id_priv->sidr_id_node))
840+
rb_erase(&cm_id_priv->sidr_id_node,
841+
&cm.remote_sidr_table);
842+
spin_unlock_irq(&cm.lock);
838843
break;
839844
case IB_CM_REQ_SENT:
840845
case IB_CM_MRA_REQ_RCVD:
@@ -3172,7 +3177,10 @@ int ib_send_cm_sidr_rep(struct ib_cm_id *cm_id,
31723177
spin_unlock_irqrestore(&cm_id_priv->lock, flags);
31733178

31743179
spin_lock_irqsave(&cm.lock, flags);
3175-
rb_erase(&cm_id_priv->sidr_id_node, &cm.remote_sidr_table);
3180+
if (!RB_EMPTY_NODE(&cm_id_priv->sidr_id_node)) {
3181+
rb_erase(&cm_id_priv->sidr_id_node, &cm.remote_sidr_table);
3182+
RB_CLEAR_NODE(&cm_id_priv->sidr_id_node);
3183+
}
31763184
spin_unlock_irqrestore(&cm.lock, flags);
31773185
return 0;
31783186

drivers/infiniband/core/cma.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,14 +1067,14 @@ static int cma_save_req_info(const struct ib_cm_event *ib_event,
10671067
sizeof(req->local_gid));
10681068
req->has_gid = true;
10691069
req->service_id = req_param->primary_path->service_id;
1070-
req->pkey = req_param->bth_pkey;
1070+
req->pkey = be16_to_cpu(req_param->primary_path->pkey);
10711071
break;
10721072
case IB_CM_SIDR_REQ_RECEIVED:
10731073
req->device = sidr_param->listen_id->device;
10741074
req->port = sidr_param->port;
10751075
req->has_gid = false;
10761076
req->service_id = sidr_param->service_id;
1077-
req->pkey = sidr_param->bth_pkey;
1077+
req->pkey = sidr_param->pkey;
10781078
break;
10791079
default:
10801080
return -EINVAL;
@@ -1324,7 +1324,7 @@ static struct rdma_id_private *cma_id_from_event(struct ib_cm_id *cm_id,
13241324
bind_list = cma_ps_find(rdma_ps_from_service_id(req.service_id),
13251325
cma_port_from_service_id(req.service_id));
13261326
id_priv = cma_find_listener(bind_list, cm_id, ib_event, &req, *net_dev);
1327-
if (IS_ERR(id_priv)) {
1327+
if (IS_ERR(id_priv) && *net_dev) {
13281328
dev_put(*net_dev);
13291329
*net_dev = NULL;
13301330
}

drivers/infiniband/core/roce_gid_mgmt.c

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -250,25 +250,44 @@ static void enum_netdev_ipv4_ips(struct ib_device *ib_dev,
250250
u8 port, struct net_device *ndev)
251251
{
252252
struct in_device *in_dev;
253+
struct sin_list {
254+
struct list_head list;
255+
struct sockaddr_in ip;
256+
};
257+
struct sin_list *sin_iter;
258+
struct sin_list *sin_temp;
253259

260+
LIST_HEAD(sin_list);
254261
if (ndev->reg_state >= NETREG_UNREGISTERING)
255262
return;
256263

257-
in_dev = in_dev_get(ndev);
258-
if (!in_dev)
264+
rcu_read_lock();
265+
in_dev = __in_dev_get_rcu(ndev);
266+
if (!in_dev) {
267+
rcu_read_unlock();
259268
return;
269+
}
260270

261271
for_ifa(in_dev) {
262-
struct sockaddr_in ip;
272+
struct sin_list *entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
263273

264-
ip.sin_family = AF_INET;
265-
ip.sin_addr.s_addr = ifa->ifa_address;
266-
update_gid_ip(GID_ADD, ib_dev, port, ndev,
267-
(struct sockaddr *)&ip);
274+
if (!entry) {
275+
pr_warn("roce_gid_mgmt: couldn't allocate entry for IPv4 update\n");
276+
continue;
277+
}
278+
entry->ip.sin_family = AF_INET;
279+
entry->ip.sin_addr.s_addr = ifa->ifa_address;
280+
list_add_tail(&entry->list, &sin_list);
268281
}
269282
endfor_ifa(in_dev);
283+
rcu_read_unlock();
270284

271-
in_dev_put(in_dev);
285+
list_for_each_entry_safe(sin_iter, sin_temp, &sin_list, list) {
286+
update_gid_ip(GID_ADD, ib_dev, port, ndev,
287+
(struct sockaddr *)&sin_iter->ip);
288+
list_del(&sin_iter->list);
289+
kfree(sin_iter);
290+
}
272291
}
273292

274293
static void enum_netdev_ipv6_ips(struct ib_device *ib_dev,

drivers/infiniband/core/ucma.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1624,11 +1624,16 @@ static int ucma_open(struct inode *inode, struct file *filp)
16241624
if (!file)
16251625
return -ENOMEM;
16261626

1627+
file->close_wq = create_singlethread_workqueue("ucma_close_id");
1628+
if (!file->close_wq) {
1629+
kfree(file);
1630+
return -ENOMEM;
1631+
}
1632+
16271633
INIT_LIST_HEAD(&file->event_list);
16281634
INIT_LIST_HEAD(&file->ctx_list);
16291635
init_waitqueue_head(&file->poll_wait);
16301636
mutex_init(&file->mut);
1631-
file->close_wq = create_singlethread_workqueue("ucma_close_id");
16321637

16331638
filp->private_data = file;
16341639
file->filp = filp;

0 commit comments

Comments
 (0)