DF-4144 / run.2.log
################ BASELINE (verbatim production code) ################ ===== [BASELINE] MODE layout ===== sizeof(struct radeon_ps) = 48 (kernel x86_64: 48) offsetof(radeon_ps, ps_priv) = 40 (kernel: 40; last member of the struct) sizeof(struct radeon_power_state) = 48 sizeof(struct trinity_pl) = 12; offsetof(allow_gnb_slow) = 7 sizeof(struct trinity_ps) = 92; offsetof(levels) = 4; offsetof(num_levels) = 0 sizeof(struct sumo_ps) = 128 (per-state kzalloc size at :1779) sizeof(struct trinity_power_info) = 528 requested_rps..requested_ps gap = 48 (== sizeof(radeon_ps)? YES) &pi->requested_ps - 1 == MSB(&pi->requested_rps.ps_priv)? YES levels[num_levels-1].allow_gnb_slow with num_levels==0 -> ps-1 (levels[-1] spans ps-8..ps+3) in-kernel consequence: ps_priv = 0xffff.... heap pointer; MSB 0xff -> 0x00 = 0x00ffff.... non-canonical -> page fault on next trinity_get_ps RESULT: OK layout ----- rc=0 ----- ===== [BASELINE] MODE good ===== RIG: trinity_dpm_init rc=0 num_ps=2 boot_ps=0x80047e000 RIG: trinity_dpm_enable rc=0 RIG: trinity_dpm_disable done RIG: pre_set_power_state rc=0 RIG: get_sclk(low)=30000 (expect 30000) RESULT: OK good-table regression (init/enable/disable/fini clean) ----- rc=0 ----- ===== [BASELINE] MODE 4143-enable ===== RIG: init rc=0 num_ps=2 boot_ps=0x0 (no BOOT-classified state -> stays NULL) RIG: simulating radeon_pm.c:1448 'radeon_dpm_init(rdev);' -- return value DISCARDED -- then :1449 current_ps=requested_ps=boot_ps and :1453 radeon_dpm_enable(rdev) RESULT: FAULT addr=0x28 guard/unmapped address NOTE: fault address 40 < page size -> NULL-pointer dereference at member offset 40 ----- rc=88 ----- ===== [BASELINE] MODE 4143-presel ===== RIG: requested_ps=0x0; verbatim trinity_dpm_pre_set_power_state (:1240 derefs it) RESULT: FAULT addr=0x0 guard/unmapped address NOTE: fault address 0 < page size -> NULL-pointer dereference at member offset 0 ----- rc=88 ----- ===== [BASELINE] MODE 4143-initfail ===== RIG: init rc=-22 (parse aborted on NULL power_state[1].clock_info at :1777) boot_ps=0x0 RIG: simulating radeon_pm.c:1448 'radeon_dpm_init(rdev);' -- return value DISCARDED -- then :1449 current_ps=requested_ps=boot_ps and :1453 radeon_dpm_enable(rdev) RESULT: FAULT addr=0x28 guard/unmapped address NOTE: fault address 40 < page size -> NULL-pointer dereference at member offset 40 ----- rc=88 ----- ===== [BASELINE] MODE 4144-doublefree ===== RIG: injecting kzalloc(128) failure (#2) RIG: init rc=-12; dpm.ps=0x80047e000 (kfreed at :1781, pointer NOT NULLed), num_ps=0, boot_ps=0x80047e000 (points INTO the freed array) RIG: allocator confirms dpm.ps 0x80047e000 is FREED while rdev still holds it RIG: simulating unload: radeon_pm.c:1705 radeon_dpm_fini(rdev) (unconditional) RESULT: DOUBLEFREE ptr=0x80047e000 (allocation of 96 bytes freed a 2nd time) ----- rc=89 ----- ===== [BASELINE] MODE 4144-uaf ===== RIG: injecting kzalloc(128) failure (#2) RIG: init rc=-12; boot_ps=0x80047e000 -> &freed_dpm.ps[0]; :1129 reads boot_ps->ps_priv RIG: simulating radeon_pm.c:1448 'radeon_dpm_init(rdev);' -- return value DISCARDED -- then :1449 current_ps=requested_ps=boot_ps and :1453 radeon_dpm_enable(rdev) RESULT: FAULT addr=0x80047e028 USE-AFTER-FREE in freed allocation 0x80047e000..0x80047e060 (+40 bytes in, alloc size 96) ----- rc=88 ----- ===== [BASELINE] MODE 4145-guard ===== drm: ERROR drm: ERROR UVD clock index not found! RIG: init rc=0 nb_dpm_enable=1 RIG: ps=0x8004c6000 (page start, PROT_NONE page before) num_levels=0 class=SDSTATE; verbatim trinity_apply_state_adjust_rules -> :1619 ps->levels[ps->num_levels - 1].allow_gnb_slow = 0 RIG: num_levels is u32: num_levels-1 = 0xFFFFFFFF, sizeof(trinity_pl)=12 -> the store address is ps + 0xbffffffff (a WILD write ~48GB past the object, NOT ps-1: the unsigned index wraps before scaling) RESULT: FAULT addr=0x14004c5fff guard/unmapped address ----- rc=88 ----- ===== [BASELINE] MODE 4145-fullflow ===== drm: ERROR drm: ERROR UVD clock index not found! RIG: init rc=0 RIG: state1 num_levels=0 (ucNumDPMLevels==0 -> num_levels never set by :1726) RIG: &pi->requested_ps=0x80047c1b0 ; :1619 store target = 0x140047c1af (ps + 0xBFFFFFFFF, u32 wrap) ; canary page mapped at 0x140047b000, pre-filled 0xAA RIG: pre_set_power_state rc=0 RIG: canary byte at target: 0xaa -> 0x00 (WRITTEN 0x00 BY THE VERBATIM STORE) RIG: canary region bytes changed: 1 (of 12288) RIG: requested_ps.Dpm0PgNbPsLo=2 DpmXNbPsLo=2 (:1603-:1618 EXECUTED: the 1/2 defaults from :1604/:1606 were overwritten at :1615/:1617 immediately before the :1619 OOB store) RIG: verbatim trinity_dpm_get_sclk(low=false) = 0xaaaaaaaa -- reads levels[0xFFFFFFFF].sclk at ps + 0xBFFFFFFF8 (the SAME wild region; value = 0xAA-repeated canary bytes unless the store overlapped: 0xaaaaaaaa) RESULT: OK 4145-fullflow (wild OOB write + OOB read at ps+0xBFFFFFFFF demonstrated) ----- rc=0 ----- ===== [BASELINE] MODE 4146-offset ===== RIG: usStateArrayOffset=0x1000, PowerPlayInfo at bios+0x500, image 0x1000 -> state_array=bios+0x1500 (past the image); :1767 reads ucNumEntries RESULT: FAULT addr=0x800479500 guard/unmapped address ----- rc=88 ----- ===== [BASELINE] MODE 4146-stride ===== RIG: state at 0x1FFD with ucNumDPMLevels=255; :1788 idx[j] walk crosses the image end at j=1 (addr bios+0x2000) RESULT: FAULT addr=0x80047a000 guard/unmapped address ----- rc=88 ----- ===== [BASELINE] MODE 4146-canary ===== RIG: state array at img+0x40: ucNumEntries=1 state0 @ img+0x541 bytes: ff 01 00 05 06 07 08 ff ff ff (idx[0..7] =) 0 5 6 7 8 255 255 255 RIG: 1-entry state array, ucNumDPMLevels=255; bytes past the state entry: 5,6,7,8 at idx[1..4] then 0xff; at idx[253] (state+254 = 0x640) sits the NON-CLOCK ARRAY header (ucNumEntries=3) -- the unbounded walk crosses into the adjacent table and parses its metadata as clock indices. clock entries carry sclk=1000*i+777 RIG: init rc=0 RIG: state0 num_levels=5 RIG: level 0 sclk=777 RIG: level 1 sclk=5777 RIG: level 2 sclk=6777 RIG: level 3 sclk=7777 RIG: level 4 sclk=3777 RESULT: OK 4146-canary (data-flow proof printed above) ----- rc=0 ----- ===== [BASELINE] MODE 4146-powerguard ===== RIG: legacy pre-parse sized power_state[] for ucNumStates=1 (radeon_atombios.c:2592, 48-byte entry); trinity loop bound is state_array->ucNumEntries=255; array at page end -> i=1 read faults RESULT: FAULT addr=0x80047c008 guard/unmapped address ----- rc=88 ----- ===== [BASELINE] MODE 4146-nullabort ===== RIG: ucNumEntries=4 > filled legacy power_state entries (1); state0 non-BOOT RIG: init rc=-22 (parse hit NULL power_state[1].clock_info at :1777-1778) RIG: simulating radeon_pm.c:1448 'radeon_dpm_init(rdev);' -- return value DISCARDED -- then :1449 current_ps=requested_ps=boot_ps and :1453 radeon_dpm_enable(rdev) RESULT: FAULT addr=0x28 guard/unmapped address NOTE: fault address 40 < page size -> NULL-pointer dereference at member offset 40 ----- rc=88 ----- ################ FIXED (same slice + the four fix.diffs) ################ ===== [FIXED] MODE layout ===== sizeof(struct radeon_ps) = 48 (kernel x86_64: 48) offsetof(radeon_ps, ps_priv) = 40 (kernel: 40; last member of the struct) sizeof(struct radeon_power_state) = 48 sizeof(struct trinity_pl) = 12; offsetof(allow_gnb_slow) = 7 sizeof(struct trinity_ps) = 92; offsetof(levels) = 4; offsetof(num_levels) = 0 sizeof(struct sumo_ps) = 128 (per-state kzalloc size at :1779) sizeof(struct trinity_power_info) = 528 requested_rps..requested_ps gap = 48 (== sizeof(radeon_ps)? YES) &pi->requested_ps - 1 == MSB(&pi->requested_rps.ps_priv)? YES levels[num_levels-1].allow_gnb_slow with num_levels==0 -> ps-1 (levels[-1] spans ps-8..ps+3) in-kernel consequence: ps_priv = 0xffff.... heap pointer; MSB 0xff -> 0x00 = 0x00ffff.... non-canonical -> page fault on next trinity_get_ps RESULT: OK layout ----- rc=0 ----- ===== [FIXED] MODE good ===== RIG: trinity_dpm_init rc=0 num_ps=2 boot_ps=0x80047e000 RIG: trinity_dpm_enable rc=0 RIG: trinity_dpm_disable done RIG: pre_set_power_state rc=0 RIG: get_sclk(low)=30000 (expect 30000) RESULT: OK good-table regression (init/enable/disable/fini clean) ----- rc=0 ----- ===== [FIXED] MODE 4143-enable ===== drm: ERROR drm: ERROR no boot state; dpm init did not succeed RIG: init rc=0 num_ps=2 boot_ps=0x0 (no BOOT-classified state -> stays NULL) RIG: simulating radeon_pm.c:1448 'radeon_dpm_init(rdev);' -- return value DISCARDED -- then :1449 current_ps=requested_ps=boot_ps and :1453 radeon_dpm_enable(rdev) RIG: enable rc=-22 RESULT: OK (fixed) 4143-enable rejected cleanly, no fault (rc=-22) ----- rc=0 ----- ===== [FIXED] MODE 4143-presel ===== RESULT: OK (fixed) parse rejected the crafted table (init rc=0) before the vulnerable site ----- rc=0 ----- ===== [FIXED] MODE 4143-initfail ===== drm: ERROR drm: ERROR state count exceeds power_state table RIG: init rc=-22 (parse aborted on NULL power_state[1].clock_info at :1777) boot_ps=0x0 RESULT: OK (fixed) parse rejected the crafted table (init rc=-22) before the vulnerable site ----- rc=0 ----- ===== [FIXED] MODE 4144-doublefree ===== RIG: injecting kzalloc(128) failure (#2) RIG: init rc=-12; dpm.ps=0x0 (kfreed at :1781, pointer NOT NULLed), num_ps=0, boot_ps=0x0 (points INTO the freed array) RIG: simulating unload: radeon_pm.c:1705 radeon_dpm_fini(rdev) (unconditional) RESULT: OK (fixed) parse rejected the crafted table (init rc=-12) before the vulnerable site RIG: allocator report: (0 live) RESULT: OK (fixed) unwind + idempotent fini, no double free, no leak ----- rc=0 ----- ===== [FIXED] MODE 4144-uaf ===== RIG: injecting kzalloc(128) failure (#2) drm: ERROR drm: ERROR no boot state; dpm init did not succeed RIG: init rc=-12; boot_ps=0x0 -> &freed_dpm.ps[0]; :1129 reads boot_ps->ps_priv RESULT: OK (fixed) parse rejected the crafted table (init rc=-12) before the vulnerable site RIG: fixed enable rc=-22 (guard; boot_ps NULLed by unwind) RESULT: OK (fixed) no UAF: dangling boot_ps cleared ----- rc=0 ----- ===== [FIXED] MODE 4145-guard ===== drm: ERROR drm: ERROR power state 1 has no dpm levels RESULT: OK (fixed) parse rejected the crafted table (init rc=-22) before the vulnerable site ----- rc=0 ----- ===== [FIXED] MODE 4145-fullflow ===== drm: ERROR drm: ERROR power state 1 has no dpm levels RIG: init rc=-22 RESULT: OK (fixed) parse rejected the crafted table (init rc=-22) before the vulnerable site ----- rc=0 ----- ===== [FIXED] MODE 4146-offset ===== drm: ERROR drm: ERROR invalid powerplay array offsets RIG: usStateArrayOffset=0x1000, PowerPlayInfo at bios+0x500, image 0x1000 -> state_array=bios+0x1500 (past the image); :1767 reads ucNumEntries RIG: init rc=-22 RESULT: OK (fixed) parse rejected the crafted table (init rc=-22) before the vulnerable site ----- rc=0 ----- ===== [FIXED] MODE 4146-stride ===== drm: ERROR drm: ERROR invalid powerplay array offsets RIG: state at 0x1FFD with ucNumDPMLevels=255; :1788 idx[j] walk crosses the image end at j=1 (addr bios+0x2000) RIG: init rc=-22 RESULT: OK (fixed) parse rejected the crafted table (init rc=-22) before the vulnerable site ----- rc=0 ----- ===== [FIXED] MODE 4146-canary ===== RIG: state array at img+0x40: ucNumEntries=1 state0 @ img+0x541 bytes: ff 01 00 05 06 07 08 ff ff ff (idx[0..7] =) 0 5 6 7 8 255 255 255 RIG: 1-entry state array, ucNumDPMLevels=255; bytes past the state entry: 5,6,7,8 at idx[1..4] then 0xff; at idx[253] (state+254 = 0x640) sits the NON-CLOCK ARRAY header (ucNumEntries=3) -- the unbounded walk crosses into the adjacent table and parses its metadata as clock indices. clock entries carry sclk=1000*i+777 RIG: init rc=0 RIG: state0 num_levels=5 RIG: level 0 sclk=777 RIG: level 1 sclk=5777 RIG: level 2 sclk=6777 RIG: level 3 sclk=7777 RIG: level 4 sclk=3777 RESULT: OK 4146-canary (data-flow proof printed above) ----- rc=0 ----- ===== [FIXED] MODE 4146-powerguard ===== drm: ERROR drm: ERROR state count exceeds power_state table RIG: legacy pre-parse sized power_state[] for ucNumStates=1 (radeon_atombios.c:2592, 48-byte entry); trinity loop bound is state_array->ucNumEntries=255; array at page end -> i=1 read faults RIG: init rc=-22 RESULT: OK (fixed) parse rejected the crafted table (init rc=-22) before the vulnerable site ----- rc=0 ----- ===== [FIXED] MODE 4146-nullabort ===== drm: ERROR drm: ERROR state count exceeds power_state table drm: ERROR drm: ERROR no boot state; dpm init did not succeed RIG: ucNumEntries=4 > filled legacy power_state entries (1); state0 non-BOOT RIG: init rc=-22 (parse hit NULL power_state[1].clock_info at :1777-1778) RESULT: OK (fixed) parse rejected the crafted table (init rc=-22) before the vulnerable site RIG: fixed enable rc=-22 RESULT: OK (fixed) count-bound reject + enable guard, no fault ----- rc=0 -----