DF-2204 / fix.diff
diff --git a/sys/dev/drm/amd/amdgpu/amdgpu_xgmi.c b/sys/dev/drm/amd/amdgpu/amdgpu_xgmi.c index 0000000..1111111 100644 --- a/sys/dev/drm/amd/amdgpu/amdgpu_xgmi.c +++ b/sys/dev/drm/amd/amdgpu/amdgpu_xgmi.c @@ -82,6 +82,22 @@ if (!hive) goto exit; + /* + * DF-2204 (part 1): amdgpu_xgmi_add_device is not idempotent. + * Without this check, a second call for an adev whose node is already + * linked would list_add_tail() it again, corrupting the + * doubly-linked list (loops, visits to freed memory). amdgpu_device + * re-init paths (driver reset / rebind) re-enter this function. + */ + if (!list_empty(&adev->gmc.xgmi.head)) { + dev_warn(adev->dev, + "XGMI: node %llx already in hive %llx; not re-adding", + adev->gmc.xgmi.device_id, + adev->gmc.xgmi.hive_id); + ret = 0; + goto exit; + } + list_add_tail(&adev->gmc.xgmi.head, &hive->device_list); list_for_each_entry(entry, &hive->device_list, head) tmp_topology[count++].device_id = entry->device_id; @@ -116,4 +132,28 @@ return ret; } +/* + * DF-2204 (part 2): The tree had no amdgpu_xgmi_remove_device() and no + * list_del() of xgmi.head anywhere, so once a device was list_add_tail()ed + * into a hive (line above) it stayed linked forever -- even after the + * amdgpu_device was torn down and freed. Any later amdgpu_xgmi_add_device() + * for any device in the same hive then iterated the freed node (UAF). + * + * amdgpu_device_fini() now calls this before freeing the device so the node + * is unlinked while adev is still valid. + */ +void amdgpu_xgmi_remove_device(struct amdgpu_device *adev) +{ + if ((adev->asic_type < CHIP_VEGA20) || + (adev->flags & AMD_IS_APU)) + return; + + mutex_lock(&xgmi_mutex); + if (!list_empty(&adev->gmc.xgmi.head)) { + list_del(&adev->gmc.xgmi.head); + INIT_LIST_HEAD(&adev->gmc.xgmi.head); + } + mutex_unlock(&xgmi_mutex); +} + diff --git a/sys/dev/drm/amd/amdgpu/amdgpu.h b/sys/dev/drm/amd/amdgpu/amdgpu.h index 0000000..2222222 100644 --- a/sys/dev/drm/amd/amdgpu/amdgpu.h +++ b/sys/dev/drm/amd/amdgpu/amdgpu.h @@ -1250,6 +1250,7 @@ * functions used by amdgpu_xgmi.c */ int amdgpu_xgmi_add_device(struct amdgpu_device *adev); +void amdgpu_xgmi_remove_device(struct amdgpu_device *adev); /* * functions used by amdgpu_encoder.c diff --git a/sys/dev/drm/amd/amdgpu/amdgpu_device.c b/sys/dev/drm/amd/amdgpu/amdgpu_device.c index 0000000..3333333 100644 --- a/sys/dev/drm/amd/amdgpu/amdgpu_device.c +++ b/sys/dev/drm/amd/amdgpu/amdgpu_device.c @@ -2730,6 +2730,8 @@ DRM_INFO("amdgpu: finishing device.\n"); adev->shutdown = true; + /* DF-2204: unlink from XGMI hive while adev is still valid */ + amdgpu_xgmi_remove_device(adev); /* disable all interrupts */ amdgpu_irq_disable_all(adev); if (adev->mode_info.mode_config_initialized){ |