--- a/sys/dev/video/vga/vga_switcheroo.c +++ b/sys/dev/video/vga/vga_switcheroo.c @@ -1020,13 +1020,36 @@ break; case MOD_UNLOAD: - case MOD_SHUTDOWN: + /* + * Refuse to unload while clients or a handler are still + * registered: freeing vgasr_priv would leave every + * vga_switcheroo_*() caller (drm_edid.c, i915, amdgpu) + * operating on freed memory (UAF). Unregister them first. + */ + if (vgasr_priv->registered_clients > 0 || + vgasr_priv->handler != NULL) { + kprintf("vga_switcheroo: %d client(s)/handler still " + "registered, refusing unload\n", + vgasr_priv->registered_clients); + return (EBUSY); + } destroy_dev(vga_switcheroo_dev); kfree(vga_switcheroo_buf, M_VGA_SWITCHEROO_BUF); + lockuninit(&vgasr_priv->mux_hw_lk); kfree(vgasr_priv, M_VGA_SWITCHEROO_VGASR_PRIV); + vga_switcheroo_buf = NULL; + vgasr_priv = NULL; kprintf("vga_switcheroo device unloaded\n"); break; + case MOD_SHUTDOWN: + /* + * Keep all state alive at shutdown: DRM drivers may call + * into vga_switcheroo from their own shutdown paths, and + * nothing is gained by freeing it here. + */ + break; + default: error = EOPNOTSUPP; break; --- a/sys/dev/drm/drm_drv.c +++ b/sys/dev/drm/drm_drv.c @@ -1144,6 +1144,10 @@ MODULE_DEPEND(drm, pci, 1, 1, 1); MODULE_DEPEND(drm, iicbus, 1, 1, 1); +/* drm_edid.c drm_get_edid_switcheroo() calls vga_switcheroo_lock_ddc()/ + * vga_switcheroo_unlock_ddc(); drm.ko cannot even link without it. */ +MODULE_DEPEND(drm, vga_switcheroo, 1, 1, 1); + struct dev_ops drm_cdevsw = { { "drm", 0, D_TRACKCLOSE | D_MPSAFE }, .d_open = drm_open, --- a/sys/dev/drm/i915/i915_pci.c +++ b/sys/dev/drm/i915/i915_pci.c @@ -894,3 +894,7 @@ #ifdef CONFIG_ACPI MODULE_DEPEND(i915, acpi, 1, 1, 1); #endif + +/* i915_drv.c load path calls vga_switcheroo_handler_flags() and + * vga_switcheroo_force_migd() directly (i915_drv.c:741-742). */ +MODULE_DEPEND(i915, vga_switcheroo, 1, 1, 1); --- a/sys/dev/drm/amd/amdgpu/amdgpu_drv.c +++ b/sys/dev/drm/amd/amdgpu/amdgpu_drv.c @@ -1434,4 +1434,8 @@ #ifdef CONFIG_ACPI MODULE_DEPEND(amdgpu, acpi, 1, 1, 1); #endif + +/* amdgpu_kms.c amdgpu_driver_lastclose_kms() calls + * vga_switcheroo_process_delayed_switch() directly. */ +MODULE_DEPEND(amdgpu, vga_switcheroo, 1, 1, 1); #endif