diff options
author | James Hogan <james.hogan@imgtec.com> | 2011-09-20 09:23:46 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2011-09-20 09:23:46 -0400 |
commit | 65b01bd561dc995aab116aa784f97a37f7c49a65 (patch) | |
tree | cebcfb510b789147e185eba651d383d61c071ca9 /drivers/hid/hidraw.c | |
parent | e4e436e0bd480668834fe6849a52c5397b7be4fb (diff) |
HID: hidraw: protect hidraw_disconnect() better
The function hidraw_disconnect() only acquires the hidraw minors_lock
when clearing the entry in hidraw_table. However the device_destroy()
call can cause a userland read/write to return with an error. It may
cause the program to release the file descripter before the disconnect
is finished. hidraw_disconnect() has already set hidraw->exist to 0,
which makes hidraw_release() kfree the hidraw structure, which
hidraw_disconnect() continues to access and even tries to kfree again.
Similarly if a hidraw_release() occurs after setting hidraw->exist to 0,
the same thing can happen.
This is fixed by expanding the mutex critical section to cover the whole
function from setting hidraw->exist to 0 to freeing the hidraw
structure, preventing a hidraw_release() from interfering.
Signed-off-by: James Hogan <james.hogan@imgtec.com>
Tested-by: David Herrmann <dh.herrmann@googlemail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/hidraw.c')
-rw-r--r-- | drivers/hid/hidraw.c | 4 |
1 files changed, 2 insertions, 2 deletions
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index c79578b5a788..a8c2b7b6220a 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c | |||
@@ -510,13 +510,12 @@ void hidraw_disconnect(struct hid_device *hid) | |||
510 | { | 510 | { |
511 | struct hidraw *hidraw = hid->hidraw; | 511 | struct hidraw *hidraw = hid->hidraw; |
512 | 512 | ||
513 | mutex_lock(&minors_lock); | ||
513 | hidraw->exist = 0; | 514 | hidraw->exist = 0; |
514 | 515 | ||
515 | device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); | 516 | device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); |
516 | 517 | ||
517 | mutex_lock(&minors_lock); | ||
518 | hidraw_table[hidraw->minor] = NULL; | 518 | hidraw_table[hidraw->minor] = NULL; |
519 | mutex_unlock(&minors_lock); | ||
520 | 519 | ||
521 | if (hidraw->open) { | 520 | if (hidraw->open) { |
522 | hid_hw_close(hid); | 521 | hid_hw_close(hid); |
@@ -524,6 +523,7 @@ void hidraw_disconnect(struct hid_device *hid) | |||
524 | } else { | 523 | } else { |
525 | kfree(hidraw); | 524 | kfree(hidraw); |
526 | } | 525 | } |
526 | mutex_unlock(&minors_lock); | ||
527 | } | 527 | } |
528 | EXPORT_SYMBOL_GPL(hidraw_disconnect); | 528 | EXPORT_SYMBOL_GPL(hidraw_disconnect); |
529 | 529 | ||