DF-3623 / ci_bootps_null.c
/* DF-3623 — logic proof for the unvalidated boot_ps NULL dereference. * * Path: VBIOS power table without any ATOM_PPLIB_CLASSIFICATION_BOOT state * -> ci_parse_pplib_non_clock_info (ci_dpm.c:5556-5577) never assigns * adev->pm.dpm.boot_ps (only at :5573-5574 when the class bit is present) * -> ci_dpm_enable (ci_dpm.c:5294-5297) fetches boot_ps = adev->pm.dpm.boot_ps * -> ci_init_smc_table (ci_dpm.c:3704) amdgpu_boot_state = adev->pm.dpm.boot_ps * -> ci_populate_smc_initial_state(adev, NULL) at ci_dpm.c:3786 * -> ci_get_ps(NULL) at ci_dpm.c:326-331: return rps->ps_priv; // NULL deref * -> boot_state->performance_levels[0].sclk at ci_dpm.c:2731 // 2nd NULL deref * (also ci_update_current_ps(adev, NULL) at ci_dpm.c:5412 does *rps copies) * * This program emulates the exact deref chain with a NULL struct and traps * SIGSEGV, demonstrating fault address 0x8 (offsetof(ps_priv)) / 0x0-level. * * Build: cc -O2 -Wall -o ci_bootps_null ci_bootps_null.c && ./ci_bootps_null */ #include <stdio.h> #include <stdint.h> #include <signal.h> #include <stddef.h> #include <stdlib.h> #include <stdbool.h> struct amdgpu_ps { uint32_t caps, class, class2, vclk, dclk, evclk, ecclk; bool vce_active; int vce_level; void *ps_priv; }; struct ci_pl { uint32_t mclk, sclk; int pcie_gen; uint16_t pcie_lane; }; struct ci_ps { uint16_t performance_level_count; bool dc; uint32_t sclk_t; struct ci_pl performance_levels[2]; }; /* ci_dpm.c:326-331 */ static struct ci_ps *ci_get_ps(struct amdgpu_ps *rps) { return rps->ps_priv; /* <- NULL->ps_priv deref */ } /* ci_dpm.c:2722-2743 (abridged to the deref) */ static void ci_populate_smc_initial_state(struct amdgpu_ps *boot) { struct ci_ps *boot_state = ci_get_ps(boot); printf(" boot_state=%p; reading boot_state->performance_levels[0].sclk ...\n", (void *)boot_state); fflush(stdout); *(volatile uint32_t *)&boot_state->performance_levels[0].sclk; /* ci_dpm.c:2731 */ } /* ci_dpm.c:5556-5577 (abridged): only BOOT class sets boot_ps */ static struct amdgpu_ps *ci_parse_pplib_non_clock_info(uint16_t usClassification) { static struct amdgpu_ps ps; /* zeroed: ps_priv == NULL */ ps.class = usClassification; if (ps.class & 0x0001 /* ATOM_PPLIB_CLASSIFICATION_BOOT */) return &ps; /* would-be assignment target */ return NULL; /* boot_ps remains NULL */ } static void segv(int sig) { (void)sig; printf(" -> SIGSEGV at NULL+%d : in-kernel equivalent is a NULL-page\n" " dereference in ci_dpm_enable (kernel thread) -> PANIC\n", (int)offsetof(struct amdgpu_ps, ps_priv)); exit(0); } int main(void) { signal(SIGSEGV, segv); struct amdgpu_ps *boot_ps = ci_parse_pplib_non_clock_info(0x0000 /* no BOOT bit */); printf("VBIOS state table without ATOM_PPLIB_CLASSIFICATION_BOOT:\n" " adev->pm.dpm.boot_ps = %p\n" " ci_dpm_enable:5297 boot_ps=%p -> ci_init_smc_table:3704 ->\n" " ci_populate_smc_initial_state:3786\n", (void *)boot_ps, (void *)boot_ps); ci_populate_smc_initial_state(boot_ps); return 0; } |