[PATCH] driver: uio: fix possible memory leak and use-after-free in __uio_register_device

From: liujian
Date: Wed Jan 02 2019 - 00:47:26 EST


Before device_register, if something goes wrong, we need to manually
free idev.

In the error handling path, after device_unregister, idev maybe have been
released, we should not use it anymore.

Signed-off-by: liujian <liujian56@xxxxxxxxxx>
---
drivers/uio/uio.c | 25 +++++++++++++++++++------
1 file changed, 19 insertions(+), 6 deletions(-)

diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c
index 1313422..5c10fc7 100644
--- a/drivers/uio/uio.c
+++ b/drivers/uio/uio.c
@@ -413,13 +413,18 @@ static int uio_get_minor(struct uio_device *idev)
return retval;
}

-static void uio_free_minor(struct uio_device *idev)
+static void __uio_free_minor(int id)
{
mutex_lock(&minor_lock);
- idr_remove(&uio_idr, idev->minor);
+ idr_remove(&uio_idr, id);
mutex_unlock(&minor_lock);
}

+static void uio_free_minor(struct uio_device *idev)
+{
+ __uio_free_minor(idev->minor);
+}
+
/**
* uio_event_notify - trigger an interrupt event
* @info: UIO device capabilities
@@ -919,6 +924,7 @@ int __uio_register_device(struct module *owner,
{
struct uio_device *idev;
int ret = 0;
+ int uio_minor;

if (!uio_class_registered)
return -EPROBE_DEFER;
@@ -940,8 +946,12 @@ int __uio_register_device(struct module *owner,
atomic_set(&idev->event, 0);

ret = uio_get_minor(idev);
- if (ret)
+ if (ret) {
+ kfree(idev);
return ret;
+ }
+
+ uio_minor = idev->minor;

idev->dev.devt = MKDEV(uio_major, idev->minor);
idev->dev.class = &uio_class;
@@ -950,8 +960,11 @@ int __uio_register_device(struct module *owner,
dev_set_drvdata(&idev->dev, idev);

ret = dev_set_name(&idev->dev, "uio%d", idev->minor);
- if (ret)
- goto err_device_create;
+ if (ret) {
+ __uio_free_minor(uio_minor);
+ kfree(idev);
+ return ret;
+ }

ret = device_register(&idev->dev);
if (ret)
@@ -987,7 +1000,7 @@ int __uio_register_device(struct module *owner,
err_uio_dev_add_attributes:
device_unregister(&idev->dev);
err_device_create:
- uio_free_minor(idev);
+ __uio_free_minor(uio_minor);
return ret;
}
EXPORT_SYMBOL_GPL(__uio_register_device);
--
2.7.4