DF-3889 / boot_ps_null.c
/* * DF-3889 harness — boot_ps NULL dereference in kv_dpm_disable -> * kv_update_current_ps -> kv_get_ps(NULL) -> rps->ps_priv. * * Mirrors sys/dev/drm/amd/amdgpu/kv_dpm.c:371-376 (kv_get_ps), * :1223-1233 (kv_update_current_ps) and the sink at :1400 * (kv_update_current_ps(adev, adev->pm.dpm.boot_ps)), for a VBIOS power * table with no ATOM_PPLIB_CLASSIFICATION_BOOT state * (kv_parse_pplib_non_clock_info :2672-2675 never sets boot_ps; sw_init * :3026 assigns the NULL into current_ps/requested_ps). * * Modes: * ./t accounting: prints the dereference site / offset. * ./t crash executes the verbatim NULL deref -> SIGSEGV at * (void *)0 + offsetof(struct amdgpu_ps, ps_priv) * (kernel equivalent: page fault in kernel mode -> panic). */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <stddef.h> #include <sys/mman.h> /* 1:1 struct amdgpu_ps (amdgpu_dpm.h:55-69) */ struct amdgpu_ps { uint32_t caps; /* vbios flags */ uint32_t class; /* vbios flags */ uint32_t class2; /* vbios flags */ uint32_t vclk; uint32_t dclk; uint32_t evclk; uint32_t ecclk; uint8_t vce_active; uint8_t _pad[3]; int32_t vce_level; /* enum amd_vce_level */ void *ps_priv; /* asic priv */ }; struct kv_ps { int dummy; }; /* verbatim kv_dpm.c:371-376 */ static struct kv_ps *kv_get_ps(struct amdgpu_ps *rps) { struct kv_ps *ps = rps->ps_priv; /* <-- NULL deref when rps == NULL */ return ps; } /* verbatim kv_dpm.c:1223-1233 shape (first line only is the fault) */ static void kv_update_current_ps(struct amdgpu_ps *rps) { struct kv_ps *new_ps = kv_get_ps(rps); (void)new_ps; } int main(int argc, char **argv) { int crash = (argc > 1 && strcmp(argv[1], "crash") == 0); /* VBIOS power table has states, none classified BOOT: * kv_parse_pplib_non_clock_info (:2672) never assigns boot_ps * kv_dpm_sw_init (:3026): current_ps = requested_ps = boot_ps = NULL * dpm inits and runs fine ... * suspend / driver detach -> kv_dpm_hw_fini -> kv_dpm_disable (:1400): * kv_update_current_ps(adev, adev->pm.dpm.boot_ps); // NULL */ struct amdgpu_ps *boot_ps = NULL; printf("offsetof(struct amdgpu_ps, ps_priv) = %zu\n", offsetof(struct amdgpu_ps, ps_priv)); printf("kv_dpm_disable (:1400) calls kv_update_current_ps(adev, boot_ps=NULL)\n"); printf(" -> kv_get_ps(NULL) (:371-373): rps->ps_priv reads *(void**)(NULL + %zu)\n", offsetof(struct amdgpu_ps, ps_priv)); fflush(stdout); if (crash) { printf("crash mode: executing verbatim deref\n"); fflush(stdout); kv_update_current_ps(boot_ps); printf("NOT REACHED\n"); } return 0; } |