XCP unplug currently restores the partition minors' device pointers to the synthetic p_ddev and frees that device while files opened through those minors may still be active. Those files acquired drm_device references on the primary ddev because the minors were redirected at XCP allocation 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, leaving 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(), when the primary drm_device reaches final release and all references held by redirected files have been dropped. Signed-off-by: Luqmaan Irshad <[email protected]> --- drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 1 + drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c | 40 ++++++++++++++++++++----- drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h | 1 + 3 files changed, 35 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..f4bc9e6eb97b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -1638,6 +1638,7 @@ void amdgpu_driver_release_kms(struct drm_device *dev) { struct amdgpu_device *adev = drm_to_adev(dev); + 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..c780d6794b89 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,39 @@ 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; + 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; + + /* Unregister devices on paths that did not unplug them earlier. */ + if (p_ddev->registered) + drm_dev_unplug(p_ddev); + + /* + * All XCP minor files have been released, so 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
