aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenson Leung <bleung@chromium.org>2013-09-24 23:05:11 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-10-05 10:13:11 -0400
commit78421afdbf0ac17baee98a9caed80c8141a121d3 (patch)
treedaedc2b5f5159dec651b4bc72895c0cac1c940ca
parent0ce8614959e1630ed3ce1fd36a689e80ca945f94 (diff)
driver core : Fix use after free of dev->parent in device_shutdown
commit f123db8e9d6c84c863cb3c44d17e61995dc984fb upstream. The put_device(dev) at the bottom of the loop of device_shutdown may result in the dev being cleaned up. In device_create_release, the dev is kfreed. However, device_shutdown attempts to use the dev pointer again after put_device by referring to dev->parent. Copy the parent pointer instead to avoid this condition. This bug was found on Chromium OS's chromeos-3.8, which is based on v3.8.11. See bug report : https://code.google.com/p/chromium/issues/detail?id=297842 This can easily be reproduced when shutting down with hidraw devices that report battery condition. Two examples are the HP Bluetooth Mouse X4000b and the Apple Magic Mouse. For example, with the magic mouse : The dev in question is "hidraw0" dev->parent is "magicmouse" In the course of the shutdown for this device, the input event cleanup calls a put on hidraw0, decrementing its reference count. When we finally get to put_device(dev) in device_shutdown, kobject_cleanup is called and device_create_release does kfree(dev). dev->parent is no longer valid, and we may crash in put_device(dev->parent). This change should be applied on any kernel with this change : d1c6c030fcec6f860d9bb6c632a3ebe62e28440b Signed-off-by: Benson Leung <bleung@chromium.org> Reviewed-by: Ming Lei <ming.lei@canonical.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/base/core.c14
1 files changed, 7 insertions, 7 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 2499cefdcdf2..ca4bcb8b3938 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -1839,7 +1839,7 @@ EXPORT_SYMBOL_GPL(device_move);
1839 */ 1839 */
1840void device_shutdown(void) 1840void device_shutdown(void)
1841{ 1841{
1842 struct device *dev; 1842 struct device *dev, *parent;
1843 1843
1844 spin_lock(&devices_kset->list_lock); 1844 spin_lock(&devices_kset->list_lock);
1845 /* 1845 /*
@@ -1856,7 +1856,7 @@ void device_shutdown(void)
1856 * prevent it from being freed because parent's 1856 * prevent it from being freed because parent's
1857 * lock is to be held 1857 * lock is to be held
1858 */ 1858 */
1859 get_device(dev->parent); 1859 parent = get_device(dev->parent);
1860 get_device(dev); 1860 get_device(dev);
1861 /* 1861 /*
1862 * Make sure the device is off the kset list, in the 1862 * Make sure the device is off the kset list, in the
@@ -1866,8 +1866,8 @@ void device_shutdown(void)
1866 spin_unlock(&devices_kset->list_lock); 1866 spin_unlock(&devices_kset->list_lock);
1867 1867
1868 /* hold lock to avoid race with probe/release */ 1868 /* hold lock to avoid race with probe/release */
1869 if (dev->parent) 1869 if (parent)
1870 device_lock(dev->parent); 1870 device_lock(parent);
1871 device_lock(dev); 1871 device_lock(dev);
1872 1872
1873 /* Don't allow any more runtime suspends */ 1873 /* Don't allow any more runtime suspends */
@@ -1885,11 +1885,11 @@ void device_shutdown(void)
1885 } 1885 }
1886 1886
1887 device_unlock(dev); 1887 device_unlock(dev);
1888 if (dev->parent) 1888 if (parent)
1889 device_unlock(dev->parent); 1889 device_unlock(parent);
1890 1890
1891 put_device(dev); 1891 put_device(dev);
1892 put_device(dev->parent); 1892 put_device(parent);
1893 1893
1894 spin_lock(&devices_kset->list_lock); 1894 spin_lock(&devices_kset->list_lock);
1895 } 1895 }