DF-3656 / verdict.json
{ "finding_id": "DF-3656", "status": "inconclusive", "reproduced": 0, "impact": "none", "confidence": "certain", "verdict": "Source-certain error-path UAF, not runnable on the audit guest. (1) amdgpu_dm_connector_init() registers the DDC adapter with i2c_add_adapter (amdgpu_dm.c:4080) but its out_free handler kfrees it on any later failure (4133) without i2c_del_adapter \u2014 the adapter stays in the i2c core with adapdata pointing at freed memory; the next DDC/EDID/CEC transaction is a UAF. (2) amdgpu_dm_initialize_drm_device()s fail path (1937-1942) unconditionally kfrees the last aencoder/aconnector and all mode_info->planes[] even when they were fully registered into dev->mode_config.{encoder,connector,plane}_lists (encoder via amdgpu_dm_encoder_init:4164, connector via drm_connector_init:4089, planes via drm_universal_plane_init:3647+) \u2014 and the drivers own error unwind immediately calls drm_mode_config_cleanup() (amdgpu_dm_destroy_drm_device:1947) which walks those lists and invokes the freed objects destroy hooks; HPD IRQs also use the freed aconnector cookie (handle_hpd_irq -> aconnector->hpd_lock, 1083). Trigger requires an allocation or debugfs failure during DM init (fault-injection class) \u2014 hence Low severity. Guest is virtio-only (env.txt) and the trigger additionally needs induced OOM, so runtime reproduction is impossible here: inconclusive, source-certain. Distinct from DF-1153 (reset-function kzalloc failures), different functions and lifetime owners.", "exploit_chain": "Fault-injected amdgpu init failure after registration (e.g. amdgpu_irq_add_id failure at 1915, or connector_debugfs_init failure at 4120) -> fail path kfrees registered objects -> amdgpu_dm_fini -> drm_mode_config_cleanup iterates mode_config lists over freed memory -> use-after-free write/read during teardown (or freed i2c adapter dereference on the next DDC access).", "evidence": "VERDICT.md (full ownership trace with registration/free line proofs); fix.diff (de-register i2c before free; only free not-yet-registered objects, let drm_mode_config_cleanup own the rest; git apply --check clean); env.txt (guest has no AMD GPU)", "kernel_refs": [ "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm.c:4080", "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm.c:4131", "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm.c:1937", "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm.c:1947", "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm.c:4164" ], "poc_changes": "n/a \u2014 no seeded PoC; no harness is meaningful for an OOM-timing kernel-object ownership bug", "attempts": 1, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 10, "guest_dirty": 0, "build_cmd": "n/a", "run_cmd": "n/a", "code_hash": "", "notes": "pass 2 amdgpu_dm.c error-path lifetime class (briefing item 5/6 adjacent); guest left clean", "recommended_fix": "In amdgpu_dm_connector_init failure paths call i2c_del_adapter() before kfree(i2c); in amdgpu_dm_initialize_drm_devices fail path only free objects DRM core does not yet own (sentinel ids) and leave registered objects to drm_mode_config_cleanup().", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "Requires amdgpu hardware plus fault injection to validate (baseline UAF vs patched clean teardown). fix.diff implements de-registration/ownership fix; git apply --check verified against sys/; not applied (read-only audit target).", "fix_evidence": "fix.diff in this pack" } |