HID: hidraw: protect hidraw_disconnect() better
authorJames Hogan <james.hogan@imgtec.com>
Tue, 20 Sep 2011 13:23:46 +0000 (15:23 +0200)
committerJiri Kosina <jkosina@suse.cz>
Tue, 20 Sep 2011 13:23:46 +0000 (15:23 +0200)
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>
drivers/hid/hidraw.c

index c79578b5a788f48ba74ec9aa2df5c82b8385b882..a8c2b7b6220a8316a159a263a426ee6360fd58a3 100644 (file)
@@ -510,13 +510,12 @@ void hidraw_disconnect(struct hid_device *hid)
 {
        struct hidraw *hidraw = hid->hidraw;
 
+       mutex_lock(&minors_lock);
        hidraw->exist = 0;
 
        device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
 
-       mutex_lock(&minors_lock);
        hidraw_table[hidraw->minor] = NULL;
-       mutex_unlock(&minors_lock);
 
        if (hidraw->open) {
                hid_hw_close(hid);
@@ -524,6 +523,7 @@ void hidraw_disconnect(struct hid_device *hid)
        } else {
                kfree(hidraw);
        }
+       mutex_unlock(&minors_lock);
 }
 EXPORT_SYMBOL_GPL(hidraw_disconnect);