|
Jean Delvare |
a097ae |
From cc67482c9e5f2c80d62f623bcc347c29f9f648e1 Mon Sep 17 00:00:00 2001
|
|
Jean Delvare |
a097ae |
From: Hyunwoo Kim <imv4bel@gmail.com>
|
|
Jean Delvare |
a097ae |
Date: Thu, 20 Oct 2022 18:15:44 -0700
|
|
Jean Delvare |
a097ae |
Subject: [PATCH] fbdev: smscufx: Fix several use-after-free bugs
|
|
Jean Delvare |
a097ae |
Git-commit: cc67482c9e5f2c80d62f623bcc347c29f9f648e1
|
|
Jean Delvare |
a097ae |
Patch-mainline: v6.1-rc3
|
|
Jean Delvare |
a097ae |
References: git-fixes
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
Several types of UAFs can occur when physically removing a USB device.
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
Adds ufx_ops_destroy() function to .fb_destroy of fb_ops, and
|
|
Jean Delvare |
a097ae |
in this function, there is kref_put() that finally calls ufx_free().
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
This fix prevents multiple UAFs.
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
|
|
Jean Delvare |
a097ae |
Link: https://lore.kernel.org/linux-fbdev/20221011153436.GA4446@ubuntu/
|
|
Jean Delvare |
a097ae |
Cc: <stable@vger.kernel.org>
|
|
Jean Delvare |
a097ae |
Signed-off-by: Helge Deller <deller@gmx.de>
|
|
Jean Delvare |
a097ae |
Acked-by: Takashi Iwai <tiwai@suse.de>
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
---
|
|
Jean Delvare |
a097ae |
drivers/video/fbdev/smscufx.c | 55 +++++++++++++++++++----------------
|
|
Jean Delvare |
a097ae |
1 file changed, 30 insertions(+), 25 deletions(-)
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
diff --git a/drivers/video/fbdev/smscufx.c b/drivers/video/fbdev/smscufx.c
|
|
Jean Delvare |
a097ae |
index e65bdc499c23..9343b7a4ac89 100644
|
|
Jean Delvare |
a097ae |
--- a/drivers/video/fbdev/smscufx.c
|
|
Jean Delvare |
a097ae |
+++ b/drivers/video/fbdev/smscufx.c
|
|
Jean Delvare |
a097ae |
@@ -97,7 +97,6 @@ struct ufx_data {
|
|
Jean Delvare |
a097ae |
struct kref kref;
|
|
Jean Delvare |
a097ae |
int fb_count;
|
|
Jean Delvare |
a097ae |
bool virtualized; /* true when physical usb device not present */
|
|
Jean Delvare |
a097ae |
- struct delayed_work free_framebuffer_work;
|
|
Jean Delvare |
a097ae |
atomic_t usb_active; /* 0 = update virtual buffer, but no usb traffic */
|
|
Jean Delvare |
a097ae |
atomic_t lost_pixels; /* 1 = a render op failed. Need screen refresh */
|
|
Jean Delvare |
a097ae |
u8 *edid; /* null until we read edid from hw or get from sysfs */
|
|
Jean Delvare |
a097ae |
@@ -1117,15 +1116,24 @@ static void ufx_free(struct kref *kref)
|
|
Jean Delvare |
a097ae |
{
|
|
Jean Delvare |
a097ae |
struct ufx_data *dev = container_of(kref, struct ufx_data, kref);
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
- /* this function will wait for all in-flight urbs to complete */
|
|
Jean Delvare |
a097ae |
- if (dev->urbs.count > 0)
|
|
Jean Delvare |
a097ae |
- ufx_free_urb_list(dev);
|
|
Jean Delvare |
a097ae |
+ kfree(dev);
|
|
Jean Delvare |
a097ae |
+}
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
- pr_debug("freeing ufx_data %p", dev);
|
|
Jean Delvare |
a097ae |
+static void ufx_ops_destory(struct fb_info *info)
|
|
Jean Delvare |
a097ae |
+{
|
|
Jean Delvare |
a097ae |
+ struct ufx_data *dev = info->par;
|
|
Jean Delvare |
a097ae |
+ int node = info->node;
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
- kfree(dev);
|
|
Jean Delvare |
a097ae |
+ /* Assume info structure is freed after this point */
|
|
Jean Delvare |
a097ae |
+ framebuffer_release(info);
|
|
Jean Delvare |
a097ae |
+
|
|
Jean Delvare |
a097ae |
+ pr_debug("fb_info for /dev/fb%d has been freed", node);
|
|
Jean Delvare |
a097ae |
+
|
|
Jean Delvare |
a097ae |
+ /* release reference taken by kref_init in probe() */
|
|
Jean Delvare |
a097ae |
+ kref_put(&dev->kref, ufx_free);
|
|
Jean Delvare |
a097ae |
}
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
+
|
|
Jean Delvare |
a097ae |
static void ufx_release_urb_work(struct work_struct *work)
|
|
Jean Delvare |
a097ae |
{
|
|
Jean Delvare |
a097ae |
struct urb_node *unode = container_of(work, struct urb_node,
|
|
Jean Delvare |
a097ae |
@@ -1134,14 +1142,9 @@ static void ufx_release_urb_work(struct work_struct *work)
|
|
Jean Delvare |
a097ae |
up(&unode->dev->urbs.limit_sem);
|
|
Jean Delvare |
a097ae |
}
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
-static void ufx_free_framebuffer_work(struct work_struct *work)
|
|
Jean Delvare |
a097ae |
+static void ufx_free_framebuffer(struct ufx_data *dev)
|
|
Jean Delvare |
a097ae |
{
|
|
Jean Delvare |
a097ae |
- struct ufx_data *dev = container_of(work, struct ufx_data,
|
|
Jean Delvare |
a097ae |
- free_framebuffer_work.work);
|
|
Jean Delvare |
a097ae |
struct fb_info *info = dev->info;
|
|
Jean Delvare |
a097ae |
- int node = info->node;
|
|
Jean Delvare |
a097ae |
-
|
|
Jean Delvare |
a097ae |
- unregister_framebuffer(info);
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
if (info->cmap.len != 0)
|
|
Jean Delvare |
a097ae |
fb_dealloc_cmap(&info->cmap);
|
|
Jean Delvare |
a097ae |
@@ -1153,11 +1156,6 @@ static void ufx_free_framebuffer_work(struct work_struct *work)
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
dev->info = NULL;
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
- /* Assume info structure is freed after this point */
|
|
Jean Delvare |
a097ae |
- framebuffer_release(info);
|
|
Jean Delvare |
a097ae |
-
|
|
Jean Delvare |
a097ae |
- pr_debug("fb_info for /dev/fb%d has been freed", node);
|
|
Jean Delvare |
a097ae |
-
|
|
Jean Delvare |
a097ae |
/* ref taken in probe() as part of registering framebfufer */
|
|
Jean Delvare |
a097ae |
kref_put(&dev->kref, ufx_free);
|
|
Jean Delvare |
a097ae |
}
|
|
Jean Delvare |
a097ae |
@@ -1169,11 +1167,13 @@ static int ufx_ops_release(struct fb_info *info, int user)
|
|
Jean Delvare |
a097ae |
{
|
|
Jean Delvare |
a097ae |
struct ufx_data *dev = info->par;
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
+ mutex_lock(&disconnect_mutex);
|
|
Jean Delvare |
a097ae |
+
|
|
Jean Delvare |
a097ae |
dev->fb_count--;
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
/* We can't free fb_info here - fbmem will touch it when we return */
|
|
Jean Delvare |
a097ae |
if (dev->virtualized && (dev->fb_count == 0))
|
|
Jean Delvare |
a097ae |
- schedule_delayed_work(&dev->free_framebuffer_work, HZ);
|
|
Jean Delvare |
a097ae |
+ ufx_free_framebuffer(dev);
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
if ((dev->fb_count == 0) && (info->fbdefio)) {
|
|
Jean Delvare |
a097ae |
fb_deferred_io_cleanup(info);
|
|
Jean Delvare |
a097ae |
@@ -1186,6 +1186,8 @@ static int ufx_ops_release(struct fb_info *info, int user)
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
kref_put(&dev->kref, ufx_free);
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
+ mutex_unlock(&disconnect_mutex);
|
|
Jean Delvare |
a097ae |
+
|
|
Jean Delvare |
a097ae |
return 0;
|
|
Jean Delvare |
a097ae |
}
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
@@ -1292,6 +1294,7 @@ static const struct fb_ops ufx_ops = {
|
|
Jean Delvare |
a097ae |
.fb_blank = ufx_ops_blank,
|
|
Jean Delvare |
a097ae |
.fb_check_var = ufx_ops_check_var,
|
|
Jean Delvare |
a097ae |
.fb_set_par = ufx_ops_set_par,
|
|
Jean Delvare |
a097ae |
+ .fb_destroy = ufx_ops_destory,
|
|
Jean Delvare |
a097ae |
};
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
/* Assumes &info->lock held by caller
|
|
Jean Delvare |
a097ae |
@@ -1673,9 +1676,6 @@ static int ufx_usb_probe(struct usb_interface *interface,
|
|
Jean Delvare |
a097ae |
goto destroy_modedb;
|
|
Jean Delvare |
a097ae |
}
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
- INIT_DELAYED_WORK(&dev->free_framebuffer_work,
|
|
Jean Delvare |
a097ae |
- ufx_free_framebuffer_work);
|
|
Jean Delvare |
a097ae |
-
|
|
Jean Delvare |
a097ae |
retval = ufx_reg_read(dev, 0x3000, &id_rev);
|
|
Jean Delvare |
a097ae |
check_warn_goto_error(retval, "error %d reading 0x3000 register from device", retval);
|
|
Jean Delvare |
a097ae |
dev_dbg(dev->gdev, "ID_REV register value 0x%08x", id_rev);
|
|
Jean Delvare |
a097ae |
@@ -1748,10 +1748,12 @@ static int ufx_usb_probe(struct usb_interface *interface,
|
|
Jean Delvare |
a097ae |
static void ufx_usb_disconnect(struct usb_interface *interface)
|
|
Jean Delvare |
a097ae |
{
|
|
Jean Delvare |
a097ae |
struct ufx_data *dev;
|
|
Jean Delvare |
a097ae |
+ struct fb_info *info;
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
mutex_lock(&disconnect_mutex);
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
dev = usb_get_intfdata(interface);
|
|
Jean Delvare |
a097ae |
+ info = dev->info;
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
pr_debug("USB disconnect starting\n");
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
@@ -1765,12 +1767,15 @@ static void ufx_usb_disconnect(struct usb_interface *interface)
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
/* if clients still have us open, will be freed on last close */
|
|
Jean Delvare |
a097ae |
if (dev->fb_count == 0)
|
|
Jean Delvare |
a097ae |
- schedule_delayed_work(&dev->free_framebuffer_work, 0);
|
|
Jean Delvare |
a097ae |
+ ufx_free_framebuffer(dev);
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
- /* release reference taken by kref_init in probe() */
|
|
Jean Delvare |
a097ae |
- kref_put(&dev->kref, ufx_free);
|
|
Jean Delvare |
a097ae |
+ /* this function will wait for all in-flight urbs to complete */
|
|
Jean Delvare |
a097ae |
+ if (dev->urbs.count > 0)
|
|
Jean Delvare |
a097ae |
+ ufx_free_urb_list(dev);
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
- /* consider ufx_data freed */
|
|
Jean Delvare |
a097ae |
+ pr_debug("freeing ufx_data %p", dev);
|
|
Jean Delvare |
a097ae |
+
|
|
Jean Delvare |
a097ae |
+ unregister_framebuffer(info);
|
|
Jean Delvare |
a097ae |
|
|
Jean Delvare |
a097ae |
mutex_unlock(&disconnect_mutex);
|
|
Jean Delvare |
a097ae |
}
|
|
Jean Delvare |
a097ae |
--
|
|
Jean Delvare |
a097ae |
2.35.3
|
|
Jean Delvare |
a097ae |
|