Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

IB/umad: Fix use-after-free on close

Avoid that closing /dev/infiniband/umad<n> or /dev/infiniband/issm<n>
triggers a use-after-free. __fput() invokes f_op->release() before it
invokes cdev_put(). Make sure that the ib_umad_device structure is
freed by the cdev_put() call instead of f_op->release(). This avoids
that changing the port mode from IB into Ethernet and back to IB
followed by restarting opensmd triggers the following kernel oops:

general protection fault: 0000 [#1] PREEMPT SMP
RIP: 0010:[<ffffffff810cc65c>] [<ffffffff810cc65c>] module_put+0x2c/0x170
Call Trace:
[<ffffffff81190f20>] cdev_put+0x20/0x30
[<ffffffff8118e2ce>] __fput+0x1ae/0x1f0
[<ffffffff8118e35e>] ____fput+0xe/0x10
[<ffffffff810723bc>] task_work_run+0xac/0xe0
[<ffffffff81002a9f>] do_notify_resume+0x9f/0xc0
[<ffffffff814b8398>] int_signal+0x12/0x17

Reference: https://bugzilla.kernel.org/show_bug.cgi?id=75051
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Yann Droneaud <ydroneaud@opteya.com>
Cc: <stable@vger.kernel.org> # 3.x: 8ec0a0e6b58: IB/umad: Fix error handling
Signed-off-by: Roland Dreier <roland@purestorage.com>

authored by

Bart Van Assche and committed by
Roland Dreier
60e1751c 584482ac

+19 -11
+19 -11
drivers/infiniband/core/user_mad.c
··· 98 98 99 99 struct ib_umad_device { 100 100 int start_port, end_port; 101 - struct kref ref; 101 + struct kobject kobj; 102 102 struct ib_umad_port port[0]; 103 103 }; 104 104 ··· 134 134 static void ib_umad_add_one(struct ib_device *device); 135 135 static void ib_umad_remove_one(struct ib_device *device); 136 136 137 - static void ib_umad_release_dev(struct kref *ref) 137 + static void ib_umad_release_dev(struct kobject *kobj) 138 138 { 139 139 struct ib_umad_device *dev = 140 - container_of(ref, struct ib_umad_device, ref); 140 + container_of(kobj, struct ib_umad_device, kobj); 141 141 142 142 kfree(dev); 143 143 } 144 + 145 + static struct kobj_type ib_umad_dev_ktype = { 146 + .release = ib_umad_release_dev, 147 + }; 144 148 145 149 static int hdr_size(struct ib_umad_file *file) 146 150 { ··· 816 812 goto out; 817 813 } 818 814 819 - kref_get(&port->umad_dev->ref); 815 + kobject_get(&port->umad_dev->kobj); 820 816 821 817 out: 822 818 mutex_unlock(&port->file_mutex); ··· 855 851 mutex_unlock(&file->port->file_mutex); 856 852 857 853 kfree(file); 858 - kref_put(&dev->ref, ib_umad_release_dev); 854 + kobject_put(&dev->kobj); 859 855 860 856 return 0; 861 857 } ··· 906 902 if (ret) 907 903 goto err_clr_sm_cap; 908 904 909 - kref_get(&port->umad_dev->ref); 905 + kobject_get(&port->umad_dev->kobj); 910 906 911 907 return 0; 912 908 ··· 936 932 937 933 up(&port->sm_sem); 938 934 939 - kref_put(&port->umad_dev->ref, ib_umad_release_dev); 935 + kobject_put(&port->umad_dev->kobj); 940 936 941 937 return ret; 942 938 } ··· 1004 1000 } 1005 1001 1006 1002 static int ib_umad_init_port(struct ib_device *device, int port_num, 1003 + struct ib_umad_device *umad_dev, 1007 1004 struct ib_umad_port *port) 1008 1005 { 1009 1006 int devnum; ··· 1037 1032 1038 1033 cdev_init(&port->cdev, &umad_fops); 1039 1034 port->cdev.owner = THIS_MODULE; 1035 + port->cdev.kobj.parent = &umad_dev->kobj; 1040 1036 kobject_set_name(&port->cdev.kobj, "umad%d", port->dev_num); 1041 1037 if (cdev_add(&port->cdev, base, 1)) 1042 1038 goto err_cdev; ··· 1056 1050 base += IB_UMAD_MAX_PORTS; 1057 1051 cdev_init(&port->sm_cdev, &umad_sm_fops); 1058 1052 port->sm_cdev.owner = THIS_MODULE; 1053 + port->sm_cdev.kobj.parent = &umad_dev->kobj; 1059 1054 kobject_set_name(&port->sm_cdev.kobj, "issm%d", port->dev_num); 1060 1055 if (cdev_add(&port->sm_cdev, base, 1)) 1061 1056 goto err_sm_cdev; ··· 1150 1143 if (!umad_dev) 1151 1144 return; 1152 1145 1153 - kref_init(&umad_dev->ref); 1146 + kobject_init(&umad_dev->kobj, &ib_umad_dev_ktype); 1154 1147 1155 1148 umad_dev->start_port = s; 1156 1149 umad_dev->end_port = e; ··· 1158 1151 for (i = s; i <= e; ++i) { 1159 1152 umad_dev->port[i - s].umad_dev = umad_dev; 1160 1153 1161 - if (ib_umad_init_port(device, i, &umad_dev->port[i - s])) 1154 + if (ib_umad_init_port(device, i, umad_dev, 1155 + &umad_dev->port[i - s])) 1162 1156 goto err; 1163 1157 } 1164 1158 ··· 1171 1163 while (--i >= s) 1172 1164 ib_umad_kill_port(&umad_dev->port[i - s]); 1173 1165 1174 - kref_put(&umad_dev->ref, ib_umad_release_dev); 1166 + kobject_put(&umad_dev->kobj); 1175 1167 } 1176 1168 1177 1169 static void ib_umad_remove_one(struct ib_device *device) ··· 1185 1177 for (i = 0; i <= umad_dev->end_port - umad_dev->start_port; ++i) 1186 1178 ib_umad_kill_port(&umad_dev->port[i]); 1187 1179 1188 - kref_put(&umad_dev->ref, ib_umad_release_dev); 1180 + kobject_put(&umad_dev->kobj); 1189 1181 } 1190 1182 1191 1183 static char *umad_devnode(struct device *dev, umode_t *mode)