Move kfd_process_remove_sysfs() earlier in
kfd_process_wq_release() so that all sysfs/procfs entries are
removed before tearing down PDDs and dropping lead_thread.
The per-process sysfs attributes are backed by struct
kfd_process_device, and their show/store callbacks dereference
PDD fields. Since sysfs removal waits for active callbacks to
complete, removing these entries first closes a race where
userspace reads sdma_* and stats_* files after PDD teardown.

This race caused NULL pointer dereferences observed in
kfd_sdma_activity_worker and kfd_procfs_stats_show.

Also harden kfd_process_remove_sysfs() against partially
initialized or already-freed objects:
- Check kobj_queues before removing PASID and deleting it
- Skip NULL pdd entries
- Guard kobj_stats and kobj_counters before use

These checks prevent invalid dereferences during cleanup.

Fixes: NULL pointer dereference in KFD sysfs/procfs stats paths
Change-Id: I405b8fb95d3c5e163dfc45928da54f31546d92cc
Cc: Felix Kuehling <[email protected]>
Cc: Alex Deucher <[email protected]>
Signed-off-by: Geoffrey McRae <[email protected]>
---
 drivers/gpu/drm/amd/amdkfd/kfd_process.c | 44 +++++++++++++++---------
 1 file changed, 28 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c 
b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
index d28ca581cad0..b47e7dac8b2d 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
@@ -1160,28 +1160,36 @@ static void kfd_process_remove_sysfs(struct kfd_process 
*p)
        if (!p->kobj)
                return;

-       sysfs_remove_file(p->kobj, &p->attr_pasid);
-       kobject_del(p->kobj_queues);
-       kobject_put(p->kobj_queues);
-       p->kobj_queues = NULL;
+       if (p->kobj_queues) {
+               sysfs_remove_file(p->kobj, &p->attr_pasid);
+               kobject_del(p->kobj_queues);
+               kobject_put(p->kobj_queues);
+               p->kobj_queues = NULL;
+       }

        for (i = 0; i < p->n_pdds; i++) {
                pdd = p->pdds[i];
+               if (!pdd)
+                       continue;

                sysfs_remove_file(p->kobj, &pdd->attr_vram);
                sysfs_remove_file(p->kobj, &pdd->attr_sdma);

-               sysfs_remove_file(pdd->kobj_stats, &pdd->attr_evict);
-               if (pdd->dev->kfd2kgd->get_cu_occupancy)
-                       sysfs_remove_file(pdd->kobj_stats,
-                                         &pdd->attr_cu_occupancy);
-               kobject_del(pdd->kobj_stats);
-               kobject_put(pdd->kobj_stats);
-               pdd->kobj_stats = NULL;
+               if (pdd->kobj_stats) {
+                       sysfs_remove_file(pdd->kobj_stats, &pdd->attr_evict);
+                       if (pdd->dev->kfd2kgd->get_cu_occupancy)
+                               sysfs_remove_file(pdd->kobj_stats,
+                                                 &pdd->attr_cu_occupancy);
+                       kobject_del(pdd->kobj_stats);
+                       kobject_put(pdd->kobj_stats);
+                       pdd->kobj_stats = NULL;
+               }
        }

        for_each_set_bit(i, p->svms.bitmap_supported, p->n_pdds) {
                pdd = p->pdds[i];
+               if (!pdd || !pdd->kobj_counters)
+                       continue;

                sysfs_remove_file(pdd->kobj_counters, &pdd->attr_faults);
                sysfs_remove_file(pdd->kobj_counters, &pdd->attr_page_in);
@@ -1239,6 +1247,15 @@ static void kfd_process_wq_release(struct work_struct 
*work)

        kfd_debugfs_remove_process(p);

+       /*
+       * Remove proc/sysfs entries before tearing down PDDs or dropping
+       * lead_thread. The per-process sysfs attributes are embedded in
+       * struct kfd_process_device and the show callbacks dereference PDD
+       * fields. sysfs removal waits for active show/store callbacks, so this
+       * closes a race with userspace reading sdma_*/stats_* files.
+       */
+       kfd_process_remove_sysfs(p);
+
        kfd_process_kunmap_signal_bo(p);
        kfd_process_free_outstanding_kfd_bos(p);
        svm_range_list_fini(p);
@@ -1252,11 +1269,6 @@ static void kfd_process_wq_release(struct work_struct 
*work)

        put_task_struct(p->lead_thread);

-       /* the last step is removing process entries under /sys
-        * to indicate the process has been terminated.
-        */
-       kfd_process_remove_sysfs(p);
-
        kfree(p);
 }

--
2.43.0

Reply via email to