On 2026-06-10 23:01, Geoffrey McRae wrote:
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;
I agree with Kent that this is probably overly paranoid since you are
fixing the sequence. kfd_create_process_device_data initializes the pdds
in such a way that it should be impossible to find a NULL-pointer in
this array.
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;
Same here. pdd cannot be NULL. Checking pdd->kobj_counters is necessary,
though.
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.
It's actually worse because kfd_process_remove_sysfs itself is
dereferencing pdd pointers. I guess the only reason that doesn't blow up
is because kfd_process_destroy_pdds also sets p->n_pdds = 0. So the
sysfs cleanup never worked as intended. You may want to update the patch
description and this commend with that in mind. Anyway, the patch looks
good. With the two nit-picks above fixed, the patch is
Reviewed-by: Felix Kuehling <[email protected]>
+ */
+ 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