linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [patch]fix memleak in error case of hiddev
@ 2008-10-31  9:31 Oliver Neukum
  2008-10-31 14:08 ` Jiri Kosina
  0 siblings, 1 reply; 8+ messages in thread
From: Oliver Neukum @ 2008-10-31  9:31 UTC (permalink / raw)
  To: Jiri Kosina; +Cc: Dmitry Torokhov, linux-input

Hi,

this bug is old, the patch should go into 2.6.28 and stable.

This fixes a memleak in hiddev's open in an error case.

Signed-off-by: Oliver Neukum <oneukum@suse.de>

	Regards
		Oliver

---

diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c
index b218cbc..6834d1f 100644
--- a/drivers/hid/usbhid/hiddev.c
+++ b/drivers/hid/usbhid/hiddev.c
@@ -291,8 +291,13 @@ static int hiddev_open(struct inode *inode, struct file *file)
 		if (list->hiddev->exist) {
 			struct hid_device *hid = hiddev_table[i]->hid;
 			res = usbhid_get_power(hid);
-			if (res < 0)
+			if (res < 0) {
+				spin_lock_irqsave(&list->hiddev->list_lock, flags);
+				list_del(&list->node);
+				spin_unlock_irqrestore(&list->hiddev->list_lock, flags);
+				kfree(list->hiddev);
 				return -EIO;
+			}
 			usbhid_open(hid);
 		}
 

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [patch]fix memleak in error case of hiddev
  2008-10-31  9:31 [patch]fix memleak in error case of hiddev Oliver Neukum
@ 2008-10-31 14:08 ` Jiri Kosina
  2008-10-31 14:42   ` Oliver Neukum
  2008-10-31 15:10   ` Oliver Neukum
  0 siblings, 2 replies; 8+ messages in thread
From: Jiri Kosina @ 2008-10-31 14:08 UTC (permalink / raw)
  To: Oliver Neukum; +Cc: Jiri Kosina, Dmitry Torokhov, linux-input

On Fri, 31 Oct 2008, Oliver Neukum wrote:

> this bug is old, the patch should go into 2.6.28 and stable.
> This fixes a memleak in hiddev's open in an error case.
> ---
> 
> diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c
> index b218cbc..6834d1f 100644
> --- a/drivers/hid/usbhid/hiddev.c
> +++ b/drivers/hid/usbhid/hiddev.c
> @@ -291,8 +291,13 @@ static int hiddev_open(struct inode *inode, struct file *file)
>  		if (list->hiddev->exist) {
>  			struct hid_device *hid = hiddev_table[i]->hid;
>  			res = usbhid_get_power(hid);
> -			if (res < 0)
> +			if (res < 0) {
> +				spin_lock_irqsave(&list->hiddev->list_lock, flags);
> +				list_del(&list->node);
> +				spin_unlock_irqrestore(&list->hiddev->list_lock, flags);
> +				kfree(list->hiddev);
>  				return -EIO;
> +			}
>  			usbhid_open(hid);
>  		}

Hmm, this is probably based on not-yet-merged usbhid autosuspend patchset, 
right?

Now, you are right that hiddev_open() doesn't handle error condition from 
usbhid_open(), and that should be fixed. But that doesn't seem to be 
addressed by your patch at all ... ?

Thanks,

-- 
Jiri Kosina
SUSE Labs

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [patch]fix memleak in error case of hiddev
  2008-10-31 14:08 ` Jiri Kosina
@ 2008-10-31 14:42   ` Oliver Neukum
  2008-10-31 15:10   ` Oliver Neukum
  1 sibling, 0 replies; 8+ messages in thread
From: Oliver Neukum @ 2008-10-31 14:42 UTC (permalink / raw)
  To: Jiri Kosina; +Cc: Jiri Kosina, Dmitry Torokhov, linux-input


> Hmm, this is probably based on not-yet-merged usbhid autosuspend patchset, 
> right?

Yes, that's right. I looked at the wrong tree.
 
> Now, you are right that hiddev_open() doesn't handle error condition from 
> usbhid_open(), and that should be fixed. But that doesn't seem to be 
> addressed by your patch at all ... ?

Yes I am looking at hiddev currently and have found several errors.
I'll make patches for the appropriate trees.

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [patch]fix memleak in error case of hiddev
  2008-10-31 14:08 ` Jiri Kosina
  2008-10-31 14:42   ` Oliver Neukum
@ 2008-10-31 15:10   ` Oliver Neukum
  2008-11-12 14:48     ` Jiri Kosina
  1 sibling, 1 reply; 8+ messages in thread
From: Oliver Neukum @ 2008-10-31 15:10 UTC (permalink / raw)
  To: Jiri Kosina; +Cc: Jiri Kosina, Dmitry Torokhov, linux-input

Am Freitag, 31. Oktober 2008 15:08:07 schrieb Jiri Kosina:
> Now, you are right that hiddev_open() doesn't handle error condition from 
> usbhid_open(), and that should be fixed. But that doesn't seem to be 
> addressed by your patch at all ... ?

Very well, this is what I've found in hiddev (with the autosuspend patch).
I am porting this to the vanilla and the stable tree.

- failure to test for lower range of minors
- addition to list before open finishes
- failure to handle errors from usbhid_open()
- possibility to miss a wakeup in hiddev_read
- open() races with hiddev_connect()

Note that this is untested and should be tested.

	Regards
		Oliver

Signed-off-by: Oliver Neukum <oneukum@suse.de>
---

diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c
index 6834d1f..3b3e179 100644
--- a/drivers/hid/usbhid/hiddev.c
+++ b/drivers/hid/usbhid/hiddev.c
@@ -268,40 +268,42 @@ static int hiddev_release(struct inode * inode, struct file * file)
 static int hiddev_open(struct inode *inode, struct file *file)
 {
 	struct hiddev_list *list;
-	unsigned long flags;
 	int res;
 
 	int i = iminor(inode) - HIDDEV_MINOR_BASE;
 
-	if (i >= HIDDEV_MINORS || !hiddev_table[i])
+	if (i >= HIDDEV_MINORS || i < 0 || !hiddev_table[i])
 		return -ENODEV;
 
 	if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL)))
 		return -ENOMEM;
 
 	list->hiddev = hiddev_table[i];
-
-	spin_lock_irqsave(&list->hiddev->list_lock, flags);
-	list_add_tail(&list->node, &hiddev_table[i]->list);
-	spin_unlock_irqrestore(&list->hiddev->list_lock, flags);
-
 	file->private_data = list;
 
 	if (!list->hiddev->open++)
 		if (list->hiddev->exist) {
 			struct hid_device *hid = hiddev_table[i]->hid;
 			res = usbhid_get_power(hid);
-			if (res < 0) {
-				spin_lock_irqsave(&list->hiddev->list_lock, flags);
-				list_del(&list->node);
-				spin_unlock_irqrestore(&list->hiddev->list_lock, flags);
-				kfree(list->hiddev);
-				return -EIO;
-			}
-			usbhid_open(hid);
+			if (res < 0)
+				goto bail;
+			res = usbhid_open(hid);
+			if (res < 0)
+				goto bail_power_put;
 		}
 
+	spin_lock_irq(&list->hiddev->list_lock);
+	list_add_tail(&list->node, &hiddev_table[i]->list);
+	spin_unlock_irq(&list->hiddev->list_lock);
+
 	return 0;
+
+bail_power_put:
+	usbhid_put_power(list->hiddev->hid);
+bail:
+	file->private_data = NULL;
+	kfree(list->hiddev);
+	return -EIO;
 }
 
 /*
@@ -330,8 +332,8 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun
 
 	while (retval == 0) {
 		if (list->head == list->tail) {
-			add_wait_queue(&list->hiddev->wait, &wait);
 			set_current_state(TASK_INTERRUPTIBLE);
+			add_wait_queue(&list->hiddev->wait, &wait);
 
 			while (list->head == list->tail) {
 				if (file->f_flags & O_NONBLOCK) {
@@ -348,7 +350,6 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun
 				}
 
 				schedule();
-				set_current_state(TASK_INTERRUPTIBLE);
 			}
 
 			set_current_state(TASK_RUNNING);
@@ -823,13 +824,6 @@ int hiddev_connect(struct hid_device *hid, unsigned int force)
 	if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL)))
 		return -1;
 
-	retval = usb_register_dev(usbhid->intf, &hiddev_class);
-	if (retval) {
-		err_hid("Not able to get a minor for this device.");
-		kfree(hiddev);
-		return -1;
-	}
-
 	init_waitqueue_head(&hiddev->wait);
 	INIT_LIST_HEAD(&hiddev->list);
 	spin_lock_init(&hiddev->list_lock);
@@ -841,6 +835,14 @@ int hiddev_connect(struct hid_device *hid, unsigned int force)
 
 	hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev;
 
+	retval = usb_register_dev(usbhid->intf, &hiddev_class);
+	if (retval) {
+		err_hid("Not able to get a minor for this device.");
+		hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = NULL;
+		kfree(hiddev);
+		return -1;
+	}
+
 	return 0;
 }
 


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [patch]fix memleak in error case of hiddev
  2008-10-31 15:10   ` Oliver Neukum
@ 2008-11-12 14:48     ` Jiri Kosina
  2008-11-12 14:55       ` Oliver Neukum
  0 siblings, 1 reply; 8+ messages in thread
From: Jiri Kosina @ 2008-11-12 14:48 UTC (permalink / raw)
  To: Oliver Neukum; +Cc: Jiri Kosina, Dmitry Torokhov, linux-input

On Fri, 31 Oct 2008, Oliver Neukum wrote:

> > Now, you are right that hiddev_open() doesn't handle error condition from 
> > usbhid_open(), and that should be fixed. But that doesn't seem to be 
> > addressed by your patch at all ... ?
> Very well, this is what I've found in hiddev (with the autosuspend patch).
> I am porting this to the vanilla and the stable tree.
> - failure to test for lower range of minors
> - addition to list before open finishes
> - failure to handle errors from usbhid_open()
> - possibility to miss a wakeup in hiddev_read
> - open() races with hiddev_connect()
> Note that this is untested and should be tested.

Does the patch below look OK to you? I have removed the dependency on 
autosuspend code for now.

Thanks.


 drivers/hid/usbhid/hiddev.c |   38 ++++++++++++++++++++++----------------
 1 files changed, 22 insertions(+), 16 deletions(-)

diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c
index 3ac3207..99b6c65 100644
--- a/drivers/hid/usbhid/hiddev.c
+++ b/drivers/hid/usbhid/hiddev.c
@@ -270,7 +270,7 @@ static int hiddev_open(struct inode *inode, struct file *file)
 
 	int i = iminor(inode) - HIDDEV_MINOR_BASE;
 
-	if (i >= HIDDEV_MINORS || !hiddev_table[i])
+	if (i >= HIDDEV_MINORS || i < 0 || !hiddev_table[i])
 		return -ENODEV;
 
 	if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL)))
@@ -278,16 +278,22 @@ static int hiddev_open(struct inode *inode, struct file *file)
 
 	list->hiddev = hiddev_table[i];
 
+	file->private_data = list;
+
+	if (!list->hiddev->open++) {
+		if (list->hiddev->exist) {
+			if (usbhid_open(hiddev_table[i]->hid) < 0) {
+				file->private_data = NULL;
+				kfree(list->hiddev);
+				return -EIO;
+			}
+		}
+	}
+
 	spin_lock_irqsave(&list->hiddev->list_lock, flags);
 	list_add_tail(&list->node, &hiddev_table[i]->list);
 	spin_unlock_irqrestore(&list->hiddev->list_lock, flags);
 
-	file->private_data = list;
-
-	if (!list->hiddev->open++)
-		if (list->hiddev->exist)
-			usbhid_open(hiddev_table[i]->hid);
-
 	return 0;
 }
 
@@ -317,8 +323,8 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun
 
 	while (retval == 0) {
 		if (list->head == list->tail) {
-			add_wait_queue(&list->hiddev->wait, &wait);
 			set_current_state(TASK_INTERRUPTIBLE);
+			add_wait_queue(&list->hiddev->wait, &wait);
 
 			while (list->head == list->tail) {
 				if (file->f_flags & O_NONBLOCK) {
@@ -335,7 +341,6 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun
 				}
 
 				schedule();
-				set_current_state(TASK_INTERRUPTIBLE);
 			}
 
 			set_current_state(TASK_RUNNING);
@@ -810,13 +815,6 @@ int hiddev_connect(struct hid_device *hid, unsigned int force)
 	if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL)))
 		return -1;
 
-	retval = usb_register_dev(usbhid->intf, &hiddev_class);
-	if (retval) {
-		err_hid("Not able to get a minor for this device.");
-		kfree(hiddev);
-		return -1;
-	}
-
 	init_waitqueue_head(&hiddev->wait);
 	INIT_LIST_HEAD(&hiddev->list);
 	spin_lock_init(&hiddev->list_lock);
@@ -828,6 +826,14 @@ int hiddev_connect(struct hid_device *hid, unsigned int force)
 
 	hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev;
 
+	retval = usb_register_dev(usbhid->intf, &hiddev_class);
+	if (retval) {
+		err_hid("Not able to get a minor for this device.");
+		hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = NULL;
+		kfree(hiddev);
+		return -1;
+	}
+
 	return 0;
 }
 

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [patch]fix memleak in error case of hiddev
  2008-11-12 14:48     ` Jiri Kosina
@ 2008-11-12 14:55       ` Oliver Neukum
  2008-11-12 14:56         ` Jiri Kosina
  2008-11-12 15:11         ` Jiri Slaby
  0 siblings, 2 replies; 8+ messages in thread
From: Oliver Neukum @ 2008-11-12 14:55 UTC (permalink / raw)
  To: Jiri Kosina; +Cc: Jiri Kosina, Dmitry Torokhov, linux-input

Am Mittwoch, 12. November 2008 15:48:38 schrieb Jiri Kosina:
> On Fri, 31 Oct 2008, Oliver Neukum wrote:
> 
> > > Now, you are right that hiddev_open() doesn't handle error condition from 
> > > usbhid_open(), and that should be fixed. But that doesn't seem to be 
> > > addressed by your patch at all ... ?
> > Very well, this is what I've found in hiddev (with the autosuspend patch).
> > I am porting this to the vanilla and the stable tree.
> > - failure to test for lower range of minors
> > - addition to list before open finishes
> > - failure to handle errors from usbhid_open()
> > - possibility to miss a wakeup in hiddev_read
> > - open() races with hiddev_connect()
> > Note that this is untested and should be tested.
> 
> Does the patch below look OK to you? I have removed the dependency on 
> autosuspend code for now.

No, it doesn't look good.

> +	if (!list->hiddev->open++) {
> +		if (list->hiddev->exist) {

The order of checks is inverted. If the device no longer exists
opening should fail, even if the device is already opened.
Didn't I post a patch that does this? My memory is a bit clouded.

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [patch]fix memleak in error case of hiddev
  2008-11-12 14:55       ` Oliver Neukum
@ 2008-11-12 14:56         ` Jiri Kosina
  2008-11-12 15:11         ` Jiri Slaby
  1 sibling, 0 replies; 8+ messages in thread
From: Jiri Kosina @ 2008-11-12 14:56 UTC (permalink / raw)
  To: Oliver Neukum; +Cc: Dmitry Torokhov, linux-input

On Wed, 12 Nov 2008, Oliver Neukum wrote:

> > +	if (!list->hiddev->open++) {
> > +		if (list->hiddev->exist) {
> The order of checks is inverted. If the device no longer exists opening 
> should fail, even if the device is already opened. 

Right.

> Didn't I post a patch that does this? My memory is a bit clouded.

I don't seem to have it, but I have been buried in hundreds of pending 
e-mails after coming back from vacation. So if you have it handy with 
changelog and Signed-off-by, I'd appreciate if you resend it to me. 
Otherwise I'll dig for it a little bit harder.

Thanks,

-- 
Jiri Kosina

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [patch]fix memleak in error case of hiddev
  2008-11-12 14:55       ` Oliver Neukum
  2008-11-12 14:56         ` Jiri Kosina
@ 2008-11-12 15:11         ` Jiri Slaby
  1 sibling, 0 replies; 8+ messages in thread
From: Jiri Slaby @ 2008-11-12 15:11 UTC (permalink / raw)
  To: Oliver Neukum; +Cc: Jiri Kosina, Jiri Kosina, Dmitry Torokhov, linux-input

On 11/12/2008 03:55 PM, Oliver Neukum wrote:
> Am Mittwoch, 12. November 2008 15:48:38 schrieb Jiri Kosina:
>> +	if (!list->hiddev->open++) {
>> +		if (list->hiddev->exist) {
> 
> The order of checks is inverted. If the device no longer exists
> opening should fail, even if the device is already opened.
> Didn't I post a patch that does this? My memory is a bit clouded.

This is very old (and broken) version of the patch. I remember I already
commented some issues contained in this one and you did fix them later ;).

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2008-11-12 15:11 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-10-31  9:31 [patch]fix memleak in error case of hiddev Oliver Neukum
2008-10-31 14:08 ` Jiri Kosina
2008-10-31 14:42   ` Oliver Neukum
2008-10-31 15:10   ` Oliver Neukum
2008-11-12 14:48     ` Jiri Kosina
2008-11-12 14:55       ` Oliver Neukum
2008-11-12 14:56         ` Jiri Kosina
2008-11-12 15:11         ` Jiri Slaby

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).