diff options
| author | Dennis Dalessandro <dennis.dalessandro@intel.com> | 2016-05-19 08:26:44 -0400 |
|---|---|---|
| committer | Doug Ledford <dledford@redhat.com> | 2016-05-26 11:35:13 -0400 |
| commit | e11ffbd57520c3832e05f2f5f19e9ff6adbb7cdc (patch) | |
| tree | 6926bb60c32447ffc09e68e909492d85c0044c38 | |
| parent | 8a1882ebd4b593df0e36ba0b72e4e2f632573274 (diff) | |
IB/hfi1: Do not free hfi1 cdev parent structure early
The deletion of a cdev is not a fence for holding off references to the
structure. The driver attempts to delete the cdev and then proceeds to
free the parent structure, the hfi1_devdata, or dd. This can potentially
lead to a kernel panic in situations where a user has an FD for the cdev
open, and the pci device gets removed. If the user then closes the FD
there will be a NULL dereference when trying to do put on the cdev's
kobject.
Fix this by pointing the cdev's kobject.parent at a new kobject embedded
in its parent structure. Also take a reference when the device is opened
and put it back when it is closed.
Reviewed-by: Mitko Haralanov <mitko.haralanov@intel.com>
Signed-off-by: Ira Weiny <ira.weiny@intel.com>
Signed-off-by: Dennis Dalessandro <dennis.dalessandro@intel.com>
Signed-off-by: Doug Ledford <dledford@redhat.com>
| -rw-r--r-- | drivers/staging/rdma/hfi1/device.c | 4 | ||||
| -rw-r--r-- | drivers/staging/rdma/hfi1/device.h | 3 | ||||
| -rw-r--r-- | drivers/staging/rdma/hfi1/file_ops.c | 15 | ||||
| -rw-r--r-- | drivers/staging/rdma/hfi1/hfi.h | 1 | ||||
| -rw-r--r-- | drivers/staging/rdma/hfi1/init.c | 14 |
5 files changed, 31 insertions, 6 deletions
diff --git a/drivers/staging/rdma/hfi1/device.c b/drivers/staging/rdma/hfi1/device.c index 6ee800f0359f..bf64b5a7bfd7 100644 --- a/drivers/staging/rdma/hfi1/device.c +++ b/drivers/staging/rdma/hfi1/device.c | |||
| @@ -60,7 +60,8 @@ static dev_t hfi1_dev; | |||
| 60 | int hfi1_cdev_init(int minor, const char *name, | 60 | int hfi1_cdev_init(int minor, const char *name, |
| 61 | const struct file_operations *fops, | 61 | const struct file_operations *fops, |
| 62 | struct cdev *cdev, struct device **devp, | 62 | struct cdev *cdev, struct device **devp, |
| 63 | bool user_accessible) | 63 | bool user_accessible, |
| 64 | struct kobject *parent) | ||
| 64 | { | 65 | { |
| 65 | const dev_t dev = MKDEV(MAJOR(hfi1_dev), minor); | 66 | const dev_t dev = MKDEV(MAJOR(hfi1_dev), minor); |
| 66 | struct device *device = NULL; | 67 | struct device *device = NULL; |
| @@ -68,6 +69,7 @@ int hfi1_cdev_init(int minor, const char *name, | |||
| 68 | 69 | ||
| 69 | cdev_init(cdev, fops); | 70 | cdev_init(cdev, fops); |
| 70 | cdev->owner = THIS_MODULE; | 71 | cdev->owner = THIS_MODULE; |
| 72 | cdev->kobj.parent = parent; | ||
| 71 | kobject_set_name(&cdev->kobj, name); | 73 | kobject_set_name(&cdev->kobj, name); |
| 72 | 74 | ||
| 73 | ret = cdev_add(cdev, dev, 1); | 75 | ret = cdev_add(cdev, dev, 1); |
diff --git a/drivers/staging/rdma/hfi1/device.h b/drivers/staging/rdma/hfi1/device.h index 5bb3e83cf2da..c3ec19cb0ac9 100644 --- a/drivers/staging/rdma/hfi1/device.h +++ b/drivers/staging/rdma/hfi1/device.h | |||
| @@ -50,7 +50,8 @@ | |||
| 50 | int hfi1_cdev_init(int minor, const char *name, | 50 | int hfi1_cdev_init(int minor, const char *name, |
| 51 | const struct file_operations *fops, | 51 | const struct file_operations *fops, |
| 52 | struct cdev *cdev, struct device **devp, | 52 | struct cdev *cdev, struct device **devp, |
| 53 | bool user_accessible); | 53 | bool user_accessible, |
| 54 | struct kobject *parent); | ||
| 54 | void hfi1_cdev_cleanup(struct cdev *cdev, struct device **devp); | 55 | void hfi1_cdev_cleanup(struct cdev *cdev, struct device **devp); |
| 55 | const char *class_name(void); | 56 | const char *class_name(void); |
| 56 | int __init dev_init(void); | 57 | int __init dev_init(void); |
diff --git a/drivers/staging/rdma/hfi1/file_ops.c b/drivers/staging/rdma/hfi1/file_ops.c index 113917021af8..7a5b0e676cc7 100644 --- a/drivers/staging/rdma/hfi1/file_ops.c +++ b/drivers/staging/rdma/hfi1/file_ops.c | |||
| @@ -168,6 +168,13 @@ static inline int is_valid_mmap(u64 token) | |||
| 168 | 168 | ||
| 169 | static int hfi1_file_open(struct inode *inode, struct file *fp) | 169 | static int hfi1_file_open(struct inode *inode, struct file *fp) |
| 170 | { | 170 | { |
| 171 | struct hfi1_devdata *dd = container_of(inode->i_cdev, | ||
| 172 | struct hfi1_devdata, | ||
| 173 | user_cdev); | ||
| 174 | |||
| 175 | /* Just take a ref now. Not all opens result in a context assign */ | ||
| 176 | kobject_get(&dd->kobj); | ||
| 177 | |||
| 171 | /* The real work is performed later in assign_ctxt() */ | 178 | /* The real work is performed later in assign_ctxt() */ |
| 172 | fp->private_data = kzalloc(sizeof(struct hfi1_filedata), GFP_KERNEL); | 179 | fp->private_data = kzalloc(sizeof(struct hfi1_filedata), GFP_KERNEL); |
| 173 | if (fp->private_data) /* no cpu affinity by default */ | 180 | if (fp->private_data) /* no cpu affinity by default */ |
| @@ -690,7 +697,9 @@ static int hfi1_file_close(struct inode *inode, struct file *fp) | |||
| 690 | { | 697 | { |
| 691 | struct hfi1_filedata *fdata = fp->private_data; | 698 | struct hfi1_filedata *fdata = fp->private_data; |
| 692 | struct hfi1_ctxtdata *uctxt = fdata->uctxt; | 699 | struct hfi1_ctxtdata *uctxt = fdata->uctxt; |
| 693 | struct hfi1_devdata *dd; | 700 | struct hfi1_devdata *dd = container_of(inode->i_cdev, |
| 701 | struct hfi1_devdata, | ||
| 702 | user_cdev); | ||
| 694 | unsigned long flags, *ev; | 703 | unsigned long flags, *ev; |
| 695 | 704 | ||
| 696 | fp->private_data = NULL; | 705 | fp->private_data = NULL; |
| @@ -699,7 +708,6 @@ static int hfi1_file_close(struct inode *inode, struct file *fp) | |||
| 699 | goto done; | 708 | goto done; |
| 700 | 709 | ||
| 701 | hfi1_cdbg(PROC, "freeing ctxt %u:%u", uctxt->ctxt, fdata->subctxt); | 710 | hfi1_cdbg(PROC, "freeing ctxt %u:%u", uctxt->ctxt, fdata->subctxt); |
| 702 | dd = uctxt->dd; | ||
| 703 | mutex_lock(&hfi1_mutex); | 711 | mutex_lock(&hfi1_mutex); |
| 704 | 712 | ||
| 705 | flush_wc(); | 713 | flush_wc(); |
| @@ -765,6 +773,7 @@ static int hfi1_file_close(struct inode *inode, struct file *fp) | |||
| 765 | mutex_unlock(&hfi1_mutex); | 773 | mutex_unlock(&hfi1_mutex); |
| 766 | hfi1_free_ctxtdata(dd, uctxt); | 774 | hfi1_free_ctxtdata(dd, uctxt); |
| 767 | done: | 775 | done: |
| 776 | kobject_put(&dd->kobj); | ||
| 768 | kfree(fdata); | 777 | kfree(fdata); |
| 769 | return 0; | 778 | return 0; |
| 770 | } | 779 | } |
| @@ -1464,7 +1473,7 @@ static int user_add(struct hfi1_devdata *dd) | |||
| 1464 | snprintf(name, sizeof(name), "%s_%d", class_name(), dd->unit); | 1473 | snprintf(name, sizeof(name), "%s_%d", class_name(), dd->unit); |
| 1465 | ret = hfi1_cdev_init(dd->unit, name, &hfi1_file_ops, | 1474 | ret = hfi1_cdev_init(dd->unit, name, &hfi1_file_ops, |
| 1466 | &dd->user_cdev, &dd->user_device, | 1475 | &dd->user_cdev, &dd->user_device, |
| 1467 | true); | 1476 | true, &dd->kobj); |
| 1468 | if (ret) | 1477 | if (ret) |
| 1469 | user_remove(dd); | 1478 | user_remove(dd); |
| 1470 | 1479 | ||
diff --git a/drivers/staging/rdma/hfi1/hfi.h b/drivers/staging/rdma/hfi1/hfi.h index 337bd2dc2dbf..4417a0fd3ef9 100644 --- a/drivers/staging/rdma/hfi1/hfi.h +++ b/drivers/staging/rdma/hfi1/hfi.h | |||
| @@ -1169,6 +1169,7 @@ struct hfi1_devdata { | |||
| 1169 | atomic_t aspm_disabled_cnt; | 1169 | atomic_t aspm_disabled_cnt; |
| 1170 | 1170 | ||
| 1171 | struct hfi1_affinity *affinity; | 1171 | struct hfi1_affinity *affinity; |
| 1172 | struct kobject kobj; | ||
| 1172 | }; | 1173 | }; |
| 1173 | 1174 | ||
| 1174 | /* 8051 firmware version helper */ | 1175 | /* 8051 firmware version helper */ |
diff --git a/drivers/staging/rdma/hfi1/init.c b/drivers/staging/rdma/hfi1/init.c index b9beaae5953d..5cc492e5776d 100644 --- a/drivers/staging/rdma/hfi1/init.c +++ b/drivers/staging/rdma/hfi1/init.c | |||
| @@ -989,8 +989,10 @@ static void release_asic_data(struct hfi1_devdata *dd) | |||
| 989 | dd->asic_data = NULL; | 989 | dd->asic_data = NULL; |
| 990 | } | 990 | } |
| 991 | 991 | ||
| 992 | void hfi1_free_devdata(struct hfi1_devdata *dd) | 992 | static void __hfi1_free_devdata(struct kobject *kobj) |
| 993 | { | 993 | { |
| 994 | struct hfi1_devdata *dd = | ||
| 995 | container_of(kobj, struct hfi1_devdata, kobj); | ||
| 994 | unsigned long flags; | 996 | unsigned long flags; |
| 995 | 997 | ||
| 996 | spin_lock_irqsave(&hfi1_devs_lock, flags); | 998 | spin_lock_irqsave(&hfi1_devs_lock, flags); |
| @@ -1007,6 +1009,15 @@ void hfi1_free_devdata(struct hfi1_devdata *dd) | |||
| 1007 | rvt_dealloc_device(&dd->verbs_dev.rdi); | 1009 | rvt_dealloc_device(&dd->verbs_dev.rdi); |
| 1008 | } | 1010 | } |
| 1009 | 1011 | ||
| 1012 | static struct kobj_type hfi1_devdata_type = { | ||
| 1013 | .release = __hfi1_free_devdata, | ||
| 1014 | }; | ||
| 1015 | |||
| 1016 | void hfi1_free_devdata(struct hfi1_devdata *dd) | ||
| 1017 | { | ||
| 1018 | kobject_put(&dd->kobj); | ||
| 1019 | } | ||
| 1020 | |||
| 1010 | /* | 1021 | /* |
| 1011 | * Allocate our primary per-unit data structure. Must be done via verbs | 1022 | * Allocate our primary per-unit data structure. Must be done via verbs |
| 1012 | * allocator, because the verbs cleanup process both does cleanup and | 1023 | * allocator, because the verbs cleanup process both does cleanup and |
| @@ -1102,6 +1113,7 @@ struct hfi1_devdata *hfi1_alloc_devdata(struct pci_dev *pdev, size_t extra) | |||
| 1102 | &pdev->dev, | 1113 | &pdev->dev, |
| 1103 | "Could not alloc cpulist info, cpu affinity might be wrong\n"); | 1114 | "Could not alloc cpulist info, cpu affinity might be wrong\n"); |
| 1104 | } | 1115 | } |
| 1116 | kobject_init(&dd->kobj, &hfi1_devdata_type); | ||
| 1105 | return dd; | 1117 | return dd; |
| 1106 | 1118 | ||
| 1107 | bail: | 1119 | bail: |
