Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S935197AbaDJDXK (ORCPT ); Wed, 9 Apr 2014 23:23:10 -0400 Received: from mail.linuxfoundation.org ([140.211.169.12]:40936 "EHLO mail.linuxfoundation.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S934660AbaDJDVu (ORCPT ); Wed, 9 Apr 2014 23:21:50 -0400 From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Manoj Chourasia , Peter Wu , Jiri Kosina , Ben Hutchings , Yijing Wang Subject: [PATCH 3.4 028/134] HID: hidraw: correctly deallocate memory on device disconnect Date: Wed, 9 Apr 2014 20:22:24 -0700 Message-Id: <20140410032303.525427527@linuxfoundation.org> X-Mailer: git-send-email 1.9.0 In-Reply-To: <20140410032259.587501440@linuxfoundation.org> References: <20140410032259.587501440@linuxfoundation.org> User-Agent: quilt/0.60-1 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 3.4-stable review patch. If anyone has any objections, please let me know. ------------------ From: Manoj Chourasia commit 212a871a3934beccf43431608c27ed2e05a476ec upstream. This changes puts the commit 4fe9f8e203f back in place with the fixes for slab corruption because of the commit. When a device is unplugged, wait for all processes that have opened the device to close before deallocating the device. This commit was solving kernel crash because of the corruption in rb tree of vmalloc. The rootcause was the device data pointer was geting excessed after the memory associated with hidraw was freed. The commit 4fe9f8e203f was buggy as it was also freeing the hidraw first and then calling delete operation on the list associated with that hidraw leading to slab corruption. Signed-off-by: Manoj Chourasia Tested-by: Peter Wu Signed-off-by: Jiri Kosina Signed-off-by: Ben Hutchings Cc: Yijing Wang Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hidraw.c | 60 +++++++++++++++++++++------------------------------ 1 file changed, 25 insertions(+), 35 deletions(-) --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -113,7 +113,7 @@ static ssize_t hidraw_send_report(struct __u8 *buf; int ret = 0; - if (!hidraw_table[minor]) { + if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { ret = -ENODEV; goto out; } @@ -261,7 +261,7 @@ static int hidraw_open(struct inode *ino } mutex_lock(&minors_lock); - if (!hidraw_table[minor]) { + if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { err = -ENODEV; goto out_unlock; } @@ -295,39 +295,38 @@ out: } +static void drop_ref(struct hidraw *hidraw, int exists_bit) +{ + if (exists_bit) { + hid_hw_close(hidraw->hid); + hidraw->exist = 0; + if (hidraw->open) + wake_up_interruptible(&hidraw->wait); + } else { + --hidraw->open; + } + + if (!hidraw->open && !hidraw->exist) { + device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); + hidraw_table[hidraw->minor] = NULL; + kfree(hidraw); + } +} + static int hidraw_release(struct inode * inode, struct file * file) { unsigned int minor = iminor(inode); - struct hidraw *dev; struct hidraw_list *list = file->private_data; - int ret; - int i; mutex_lock(&minors_lock); - if (!hidraw_table[minor]) { - ret = -ENODEV; - goto unlock; - } list_del(&list->node); - dev = hidraw_table[minor]; - if (!--dev->open) { - if (list->hidraw->exist) { - hid_hw_power(dev->hid, PM_HINT_NORMAL); - hid_hw_close(dev->hid); - } else { - kfree(list->hidraw); - } - } - - for (i = 0; i < HIDRAW_BUFFER_SIZE; ++i) - kfree(list->buffer[i].value); kfree(list); - ret = 0; -unlock: - mutex_unlock(&minors_lock); - return ret; + drop_ref(hidraw_table[minor], 0); + + mutex_unlock(&minors_lock); + return 0; } static long hidraw_ioctl(struct file *file, unsigned int cmd, @@ -531,18 +530,9 @@ void hidraw_disconnect(struct hid_device struct hidraw *hidraw = hid->hidraw; mutex_lock(&minors_lock); - hidraw->exist = 0; - - device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); - hidraw_table[hidraw->minor] = NULL; + drop_ref(hidraw, 1); - if (hidraw->open) { - hid_hw_close(hid); - wake_up_interruptible(&hidraw->wait); - } else { - kfree(hidraw); - } mutex_unlock(&minors_lock); } EXPORT_SYMBOL_GPL(hidraw_disconnect); -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/