DF-1749 / harness.c
/* * DF-1749 - amdgpu_acpi.c UAF: ACPI notify handler never removed on detach. * * Vulnerable code (sys/dev/drm/amd/amdgpu/amdgpu_acpi.c): * 811 AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY, * adev->acpi.notifier_call, adev); * 824 void amdgpu_acpi_fini(struct amdgpu_device *adev) { * 826 if (adev->atif) * 827 AcpiOsFree(adev->atif); * 828 } * * No AcpiRemoveNotifyHandler. No adev->atif = NULL. After amdgpu device * detach / kldunload amdgpu, ACPICA still holds (handle, handler, context=adev). * Next ACPI event -> amdgpu_acpi_event(handle, type, context=adev) where * adev is freed -> amdgpu_atif_handler -> atif = adev->atif (UAF read at * line 369). Then atif->notification_cfg.enabled and friends are read * from freed memory; with slab grooming, attacker-controlled -> kCE. * * Trigger: hot-unplug / kldunload amdgpu + lid/brightness key/hotplug. * * radeon_acpi.c does the symmetric AcpiRemoveNotifyHandler correctly * (the finding cites it as the reference). * * This harness simulates the registration + free-without-remove + * event-after-free sequence. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> struct amdgpu_device { int live; int atif_enabled; }; struct atif { int enabled; void *sbios_requests; }; static struct amdgpu_device *registered_adev = NULL; static int events_handled = 0; void amdgpu_acpi_init(struct amdgpu_device *adev) { /* amdgpu_acpi.c:811-812: register handler with adev as context */ registered_adev = adev; adev->live = 1; adev->atif_enabled = 1; printf(" init: AcpiInstallNotifyHandler(handler, adev=%p)\n", (void*)adev); } void amdgpu_acpi_fini_buggy(struct amdgpu_device *adev) { /* amdgpu_acpi.c:824-828: free atif but NO AcpiRemoveNotifyHandler, * NO NULL of registered context. */ printf(" fini (buggy): AcpiOsFree(adev->atif) only; ACPICA retains adev=%p\n", (void*)adev); /* simulate adev is freed but the registration is NOT cleared */ /* registered_adev still points at adev (now dangling) */ } void amdgpu_acpi_event(void) { /* amdgpu_acpi.c:700-718: context was the freed adev */ struct amdgpu_device *adev = registered_adev; if (adev == NULL) { printf(" event: no handler (properly removed)\n"); return; } /* UAF: adev is freed memory */ printf(" event: dereferencing adev=%p (UAF), adev->atif_enabled=%d\n", (void*)adev, adev->atif_enabled); events_handled++; } int main(void) { printf("=== DF-1749 amdgpu_acpi UAF: notify handler never removed ===\n"); struct amdgpu_device *adev = malloc(sizeof(*adev)); amdgpu_acpi_init(adev); amdgpu_acpi_fini_buggy(adev); /* simulate the underlying amdgpu_device_fini+kfree(adev) */ free(adev); printf(" [underlying adev freed by amdgpu_device_fini+kfree]\n"); /* a real attacker triggers an ACPI event here */ amdgpu_acpi_event(); /* UAF */ printf("\n"); printf("VERDICT: BUG CONFIRMED. registered_adev still points at freed\n" " memory after fini; the next ACPI notify event calls\n" " amdgpu_acpi_event with the dangling context. Reading\n" " adev->atif (line 369) is a UAF; with heap grooming it\n" " yields attacker-controlled atif->notification_cfg and\n" " function pointers -> kCE.\n"); return 0; } |