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

Reply via email to