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

HID: hiddev: fix race between hiddev_disconnect and hiddev_release

When hiddev_disconnect() runs with chardev open, it will proceed with
usbhid_close(). When userspace in parallel runs the hiddev_release(),
it sees !hiddev->exists (as it has been already set so by
hiddev_disconnect()) and kfrees hiddev while hiddev_disconnect() hasn't
finished yet.

Serialize the access to hiddev->exists and hiddev->open by existancelock.

Reported-by: mike-@cinci.rr.com
Signed-off-by: Jiri Kosina <jkosina@suse.cz>

+8 -2
+8 -2
drivers/hid/usbhid/hiddev.c
··· 242 242 list_del(&list->node); 243 243 spin_unlock_irqrestore(&list->hiddev->list_lock, flags); 244 244 245 + mutex_lock(&list->hiddev->existancelock); 245 246 if (!--list->hiddev->open) { 246 247 if (list->hiddev->exist) { 247 248 usbhid_close(list->hiddev->hid); ··· 253 252 } 254 253 255 254 kfree(list); 255 + mutex_unlock(&list->hiddev->existancelock); 256 256 257 257 return 0; 258 258 } ··· 302 300 list_add_tail(&list->node, &hiddev->list); 303 301 spin_unlock_irq(&list->hiddev->list_lock); 304 302 303 + mutex_lock(&hiddev->existancelock); 305 304 if (!list->hiddev->open++) 306 305 if (list->hiddev->exist) { 307 306 struct hid_device *hid = hiddev->hid; 308 307 res = usbhid_get_power(hid); 309 308 if (res < 0) { 310 309 res = -EIO; 311 - goto bail; 310 + goto bail_unlock; 312 311 } 313 312 usbhid_open(hid); 314 313 } 314 + mutex_unlock(&hiddev->existancelock); 315 315 return 0; 316 + bail_unlock: 317 + mutex_unlock(&hiddev->existancelock); 316 318 bail: 317 319 file->private_data = NULL; 318 320 kfree(list); ··· 917 911 918 912 mutex_lock(&hiddev->existancelock); 919 913 hiddev->exist = 0; 920 - mutex_unlock(&hiddev->existancelock); 921 914 922 915 usb_deregister_dev(usbhid->intf, &hiddev_class); 923 916 ··· 926 921 } else { 927 922 kfree(hiddev); 928 923 } 924 + mutex_unlock(&hiddev->existancelock); 929 925 }