XCP unplug currently restores partition minors onto the synthetic p_ddev and frees that device while files opened through those minors may still be active. Those files took drm_device references on the primary ddev because the minors were redirected at XCP alloc time. After unplug reverses the redirection, close paths operate on the already-freed p_ddev, causing a use-after-free and p_ddev refcount underflow while leaking the matching primary-device references.
Split teardown into two phases. amdgpu_xcp_dev_unplug() only calls drm_dev_unplug() so new opens fail, and leaves minor->dev pointing at the primary device. amdgpu_xcp_dev_fini() restores the partition device pointers and frees p_ddev from amdgpu_driver_release_kms(), after the primary drm_device's last reference is dropped and every redirected drm_file has completed drm_minor_release(). Signed-off-by: Luqmaan Irshad <[email protected]> --- drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 6 +++ drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c | 51 +++++++++++++++++++++---- drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h | 1 + 3 files changed, 51 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index f2f179421c4d..403ca8fb1bc9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -1638,6 +1638,12 @@ void amdgpu_driver_release_kms(struct drm_device *dev) { struct amdgpu_device *adev = drm_to_adev(dev); + /* + * XCP minors redirect their device reference to this DRM device. + * Reaching its final release guarantees that no open files still + * reference those minors, so the backing XCP devices can now be freed. + */ + amdgpu_xcp_dev_fini(adev); amdgpu_device_fini_sw(adev); pci_set_drvdata(adev->pdev, NULL); } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c index a8c79a66df1d..295cd9567512 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c @@ -412,7 +412,6 @@ int amdgpu_xcp_dev_register(struct amdgpu_device *adev, void amdgpu_xcp_dev_unplug(struct amdgpu_device *adev) { - struct drm_device *p_ddev; int i; if (!adev->xcp_mgr) @@ -422,12 +421,50 @@ void amdgpu_xcp_dev_unplug(struct amdgpu_device *adev) if (!adev->xcp_mgr->xcp[i].ddev) break; - p_ddev = adev->xcp_mgr->xcp[i].ddev; - drm_dev_unplug(p_ddev); - p_ddev->render->dev = adev->xcp_mgr->xcp[i].rdev; - p_ddev->primary->dev = adev->xcp_mgr->xcp[i].pdev; - p_ddev->driver = adev->xcp_mgr->xcp[i].driver; - p_ddev->vma_offset_manager = adev->xcp_mgr->xcp[i].vma_offset_manager; + /* + * Stop new opens, but keep the partition DRM device and its + * minors alive. Existing drm_file objects still point at these + * minors, whose ->dev was redirected to the primary DRM device + * when the XCP device was allocated. + */ + drm_dev_unplug(adev->xcp_mgr->xcp[i].ddev); + } +} + +void amdgpu_xcp_dev_fini(struct amdgpu_device *adev) +{ + struct amdgpu_xcp *xcp; + struct drm_device *p_ddev; + int i; + + if (!adev->xcp_mgr) + return; + + for (i = 1; i < MAX_XCP; i++) { + xcp = &adev->xcp_mgr->xcp[i]; + if (!xcp->ddev) + break; + + p_ddev = xcp->ddev; + + /* + * Normally this was done by amdgpu_xcp_dev_unplug(). Also + * cover initialization unwind and other release paths. + */ + if (p_ddev->registered) + drm_dev_unplug(p_ddev); + + /* + * The primary DRM device is in its final release callback, so + * every file that acquired a reference through these redirected + * minors has completed drm_minor_release(). It is now safe to + * restore the partition device and free it. + */ + p_ddev->render->dev = xcp->rdev; + p_ddev->primary->dev = xcp->pdev; + p_ddev->driver = xcp->driver; + p_ddev->vma_offset_manager = xcp->vma_offset_manager; + xcp->ddev = NULL; amdgpu_xcp_drm_dev_free(p_ddev); } } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h index 33157409eda0..e2fd32af9932 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h @@ -181,6 +181,7 @@ int amdgpu_xcp_get_inst_details(struct amdgpu_xcp *xcp, int amdgpu_xcp_dev_register(struct amdgpu_device *adev, const struct pci_device_id *ent); void amdgpu_xcp_dev_unplug(struct amdgpu_device *adev); +void amdgpu_xcp_dev_fini(struct amdgpu_device *adev); int amdgpu_xcp_open_device(struct amdgpu_device *adev, struct amdgpu_fpriv *fpriv, struct drm_file *file_priv); -- 2.53.0
