aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-10-23 18:28:05 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2015-10-23 18:28:05 -0400
commit410694e214d5b479bc8b035ded0bb832f1c469b4 (patch)
treefc1cf8c2b4416e73aaf82e73333bc4aaaed8c643
parent35df017c4d5571ee6f3061964d1445aae250219c (diff)
parent0ca81a2840f77855bbad1b9f172c545c4dc9e6a4 (diff)
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
-rw-r--r--drivers/infiniband/core/cache.c2
-rw-r--r--drivers/infiniband/core/cm.c10
-rw-r--r--drivers/infiniband/core/cma.c6
-rw-r--r--drivers/infiniband/core/roce_gid_mgmt.c35
-rw-r--r--drivers/infiniband/core/ucma.c7
5 files changed, 46 insertions, 14 deletions
diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c
index 8f66c67ff0df..87471ef37198 100644
--- a/drivers/infiniband/core/cache.c
+++ b/drivers/infiniband/core/cache.c
@@ -508,12 +508,12 @@ void ib_cache_gid_set_default_gid(struct ib_device *ib_dev, u8 port,
508 memset(&gid_attr, 0, sizeof(gid_attr)); 508 memset(&gid_attr, 0, sizeof(gid_attr));
509 gid_attr.ndev = ndev; 509 gid_attr.ndev = ndev;
510 510
511 mutex_lock(&table->lock);
511 ix = find_gid(table, NULL, NULL, true, GID_ATTR_FIND_MASK_DEFAULT); 512 ix = find_gid(table, NULL, NULL, true, GID_ATTR_FIND_MASK_DEFAULT);
512 513
513 /* Coudn't find default GID location */ 514 /* Coudn't find default GID location */
514 WARN_ON(ix < 0); 515 WARN_ON(ix < 0);
515 516
516 mutex_lock(&table->lock);
517 if (!__ib_cache_gid_get(ib_dev, port, ix, 517 if (!__ib_cache_gid_get(ib_dev, port, ix,
518 &current_gid, &current_gid_attr) && 518 &current_gid, &current_gid_attr) &&
519 mode == IB_CACHE_GID_DEFAULT_MODE_SET && 519 mode == IB_CACHE_GID_DEFAULT_MODE_SET &&
diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c
index ea4db9c1d44f..4f918b929eca 100644
--- a/drivers/infiniband/core/cm.c
+++ b/drivers/infiniband/core/cm.c
@@ -835,6 +835,11 @@ retest:
835 case IB_CM_SIDR_REQ_RCVD: 835 case IB_CM_SIDR_REQ_RCVD:
836 spin_unlock_irq(&cm_id_priv->lock); 836 spin_unlock_irq(&cm_id_priv->lock);
837 cm_reject_sidr_req(cm_id_priv, IB_SIDR_REJECT); 837 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);
838 break; 843 break;
839 case IB_CM_REQ_SENT: 844 case IB_CM_REQ_SENT:
840 case IB_CM_MRA_REQ_RCVD: 845 case IB_CM_MRA_REQ_RCVD:
@@ -3172,7 +3177,10 @@ int ib_send_cm_sidr_rep(struct ib_cm_id *cm_id,
3172 spin_unlock_irqrestore(&cm_id_priv->lock, flags); 3177 spin_unlock_irqrestore(&cm_id_priv->lock, flags);
3173 3178
3174 spin_lock_irqsave(&cm.lock, flags); 3179 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 }
3176 spin_unlock_irqrestore(&cm.lock, flags); 3184 spin_unlock_irqrestore(&cm.lock, flags);
3177 return 0; 3185 return 0;
3178 3186
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 59a2dafc8c57..36b12d560e17 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -1067,14 +1067,14 @@ static int cma_save_req_info(const struct ib_cm_event *ib_event,
1067 sizeof(req->local_gid)); 1067 sizeof(req->local_gid));
1068 req->has_gid = true; 1068 req->has_gid = true;
1069 req->service_id = req_param->primary_path->service_id; 1069 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);
1071 break; 1071 break;
1072 case IB_CM_SIDR_REQ_RECEIVED: 1072 case IB_CM_SIDR_REQ_RECEIVED:
1073 req->device = sidr_param->listen_id->device; 1073 req->device = sidr_param->listen_id->device;
1074 req->port = sidr_param->port; 1074 req->port = sidr_param->port;
1075 req->has_gid = false; 1075 req->has_gid = false;
1076 req->service_id = sidr_param->service_id; 1076 req->service_id = sidr_param->service_id;
1077 req->pkey = sidr_param->bth_pkey; 1077 req->pkey = sidr_param->pkey;
1078 break; 1078 break;
1079 default: 1079 default:
1080 return -EINVAL; 1080 return -EINVAL;
@@ -1324,7 +1324,7 @@ static struct rdma_id_private *cma_id_from_event(struct ib_cm_id *cm_id,
1324 bind_list = cma_ps_find(rdma_ps_from_service_id(req.service_id), 1324 bind_list = cma_ps_find(rdma_ps_from_service_id(req.service_id),
1325 cma_port_from_service_id(req.service_id)); 1325 cma_port_from_service_id(req.service_id));
1326 id_priv = cma_find_listener(bind_list, cm_id, ib_event, &req, *net_dev); 1326 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) {
1328 dev_put(*net_dev); 1328 dev_put(*net_dev);
1329 *net_dev = NULL; 1329 *net_dev = NULL;
1330 } 1330 }
diff --git a/drivers/infiniband/core/roce_gid_mgmt.c b/drivers/infiniband/core/roce_gid_mgmt.c
index 6b24cba1e474..178f98482e13 100644
--- a/drivers/infiniband/core/roce_gid_mgmt.c
+++ b/drivers/infiniband/core/roce_gid_mgmt.c
@@ -250,25 +250,44 @@ static void enum_netdev_ipv4_ips(struct ib_device *ib_dev,
250 u8 port, struct net_device *ndev) 250 u8 port, struct net_device *ndev)
251{ 251{
252 struct in_device *in_dev; 252 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;
253 259
260 LIST_HEAD(sin_list);
254 if (ndev->reg_state >= NETREG_UNREGISTERING) 261 if (ndev->reg_state >= NETREG_UNREGISTERING)
255 return; 262 return;
256 263
257 in_dev = in_dev_get(ndev); 264 rcu_read_lock();
258 if (!in_dev) 265 in_dev = __in_dev_get_rcu(ndev);
266 if (!in_dev) {
267 rcu_read_unlock();
259 return; 268 return;
269 }
260 270
261 for_ifa(in_dev) { 271 for_ifa(in_dev) {
262 struct sockaddr_in ip; 272 struct sin_list *entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
263 273
264 ip.sin_family = AF_INET; 274 if (!entry) {
265 ip.sin_addr.s_addr = ifa->ifa_address; 275 pr_warn("roce_gid_mgmt: couldn't allocate entry for IPv4 update\n");
266 update_gid_ip(GID_ADD, ib_dev, port, ndev, 276 continue;
267 (struct sockaddr *)&ip); 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);
268 } 281 }
269 endfor_ifa(in_dev); 282 endfor_ifa(in_dev);
283 rcu_read_unlock();
270 284
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 }
272} 291}
273 292
274static void enum_netdev_ipv6_ips(struct ib_device *ib_dev, 293static void enum_netdev_ipv6_ips(struct ib_device *ib_dev,
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index a53fc9b01c69..30467d10df91 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -1624,11 +1624,16 @@ static int ucma_open(struct inode *inode, struct file *filp)
1624 if (!file) 1624 if (!file)
1625 return -ENOMEM; 1625 return -ENOMEM;
1626 1626
1627 file->close_wq = create_singlethread_workqueue("ucma_close_id");
1628 if (!file->close_wq) {
1629 kfree(file);
1630 return -ENOMEM;
1631 }
1632
1627 INIT_LIST_HEAD(&file->event_list); 1633 INIT_LIST_HEAD(&file->event_list);
1628 INIT_LIST_HEAD(&file->ctx_list); 1634 INIT_LIST_HEAD(&file->ctx_list);
1629 init_waitqueue_head(&file->poll_wait); 1635 init_waitqueue_head(&file->poll_wait);
1630 mutex_init(&file->mut); 1636 mutex_init(&file->mut);
1631 file->close_wq = create_singlethread_workqueue("ucma_close_id");
1632 1637
1633 filp->private_data = file; 1638 filp->private_data = file;
1634 file->filp = filp; 1639 file->filp = filp;