fbdev: smscufx: Fix several use-after-free bugs

Several types of UAFs can occur when physically removing a USB device.

Adds ufx_ops_destroy() function to .fb_destroy of fb_ops, and
in this function, there is kref_put() that finally calls ufx_free().

This fix prevents multiple UAFs.

Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
Link: https://lore.kernel.org/linux-fbdev/20221011153436.GA4446@ubuntu/
Cc: <stable@vger.kernel.org>
Signed-off-by: Helge Deller <deller@gmx.de>

authored by Hyunwoo Kim and committed by Helge Deller cc67482c 70281592

Changed files
+31 -26
drivers
video
fbdev
+31 -26
drivers/video/fbdev/smscufx.c
··· 97 97 struct kref kref; 98 98 int fb_count; 99 99 bool virtualized; /* true when physical usb device not present */ 100 - struct delayed_work free_framebuffer_work; 101 100 atomic_t usb_active; /* 0 = update virtual buffer, but no usb traffic */ 102 101 atomic_t lost_pixels; /* 1 = a render op failed. Need screen refresh */ 103 102 u8 *edid; /* null until we read edid from hw or get from sysfs */ ··· 1116 1117 { 1117 1118 struct ufx_data *dev = container_of(kref, struct ufx_data, kref); 1118 1119 1119 - /* this function will wait for all in-flight urbs to complete */ 1120 - if (dev->urbs.count > 0) 1121 - ufx_free_urb_list(dev); 1122 - 1123 - pr_debug("freeing ufx_data %p", dev); 1124 - 1125 1120 kfree(dev); 1126 1121 } 1122 + 1123 + static void ufx_ops_destory(struct fb_info *info) 1124 + { 1125 + struct ufx_data *dev = info->par; 1126 + int node = info->node; 1127 + 1128 + /* Assume info structure is freed after this point */ 1129 + framebuffer_release(info); 1130 + 1131 + pr_debug("fb_info for /dev/fb%d has been freed", node); 1132 + 1133 + /* release reference taken by kref_init in probe() */ 1134 + kref_put(&dev->kref, ufx_free); 1135 + } 1136 + 1127 1137 1128 1138 static void ufx_release_urb_work(struct work_struct *work) 1129 1139 { ··· 1142 1134 up(&unode->dev->urbs.limit_sem); 1143 1135 } 1144 1136 1145 - static void ufx_free_framebuffer_work(struct work_struct *work) 1137 + static void ufx_free_framebuffer(struct ufx_data *dev) 1146 1138 { 1147 - struct ufx_data *dev = container_of(work, struct ufx_data, 1148 - free_framebuffer_work.work); 1149 1139 struct fb_info *info = dev->info; 1150 - int node = info->node; 1151 - 1152 - unregister_framebuffer(info); 1153 1140 1154 1141 if (info->cmap.len != 0) 1155 1142 fb_dealloc_cmap(&info->cmap); ··· 1155 1152 fb_destroy_modelist(&info->modelist); 1156 1153 1157 1154 dev->info = NULL; 1158 - 1159 - /* Assume info structure is freed after this point */ 1160 - framebuffer_release(info); 1161 - 1162 - pr_debug("fb_info for /dev/fb%d has been freed", node); 1163 1155 1164 1156 /* ref taken in probe() as part of registering framebfufer */ 1165 1157 kref_put(&dev->kref, ufx_free); ··· 1167 1169 { 1168 1170 struct ufx_data *dev = info->par; 1169 1171 1172 + mutex_lock(&disconnect_mutex); 1173 + 1170 1174 dev->fb_count--; 1171 1175 1172 1176 /* We can't free fb_info here - fbmem will touch it when we return */ 1173 1177 if (dev->virtualized && (dev->fb_count == 0)) 1174 - schedule_delayed_work(&dev->free_framebuffer_work, HZ); 1178 + ufx_free_framebuffer(dev); 1175 1179 1176 1180 if ((dev->fb_count == 0) && (info->fbdefio)) { 1177 1181 fb_deferred_io_cleanup(info); ··· 1185 1185 info->node, user, dev->fb_count); 1186 1186 1187 1187 kref_put(&dev->kref, ufx_free); 1188 + 1189 + mutex_unlock(&disconnect_mutex); 1188 1190 1189 1191 return 0; 1190 1192 } ··· 1294 1292 .fb_blank = ufx_ops_blank, 1295 1293 .fb_check_var = ufx_ops_check_var, 1296 1294 .fb_set_par = ufx_ops_set_par, 1295 + .fb_destroy = ufx_ops_destory, 1297 1296 }; 1298 1297 1299 1298 /* Assumes &info->lock held by caller ··· 1676 1673 goto destroy_modedb; 1677 1674 } 1678 1675 1679 - INIT_DELAYED_WORK(&dev->free_framebuffer_work, 1680 - ufx_free_framebuffer_work); 1681 - 1682 1676 retval = ufx_reg_read(dev, 0x3000, &id_rev); 1683 1677 check_warn_goto_error(retval, "error %d reading 0x3000 register from device", retval); 1684 1678 dev_dbg(dev->gdev, "ID_REV register value 0x%08x", id_rev); ··· 1748 1748 static void ufx_usb_disconnect(struct usb_interface *interface) 1749 1749 { 1750 1750 struct ufx_data *dev; 1751 + struct fb_info *info; 1751 1752 1752 1753 mutex_lock(&disconnect_mutex); 1753 1754 1754 1755 dev = usb_get_intfdata(interface); 1756 + info = dev->info; 1755 1757 1756 1758 pr_debug("USB disconnect starting\n"); 1757 1759 ··· 1767 1765 1768 1766 /* if clients still have us open, will be freed on last close */ 1769 1767 if (dev->fb_count == 0) 1770 - schedule_delayed_work(&dev->free_framebuffer_work, 0); 1768 + ufx_free_framebuffer(dev); 1771 1769 1772 - /* release reference taken by kref_init in probe() */ 1773 - kref_put(&dev->kref, ufx_free); 1770 + /* this function will wait for all in-flight urbs to complete */ 1771 + if (dev->urbs.count > 0) 1772 + ufx_free_urb_list(dev); 1774 1773 1775 - /* consider ufx_data freed */ 1774 + pr_debug("freeing ufx_data %p", dev); 1775 + 1776 + unregister_framebuffer(info); 1776 1777 1777 1778 mutex_unlock(&disconnect_mutex); 1778 1779 }