DF-3888 / num_levels_underflow.c
/* * DF-3888 harness — kv_dpm_get_sclk / kv_set_valid_clock_range num_levels==0 * -> levels[num_levels - 1] wild read (+~48 GB). * * Mirrors sys/dev/drm/amd/amdgpu/kv_dpm.c:2929-2939 (get_sclk) and * :1791/:1817 (set_valid_clock_range) with 1:1 struct layouts (kv_dpm.h), * for a parsed power state whose ucNumDPMLevels==0 (or all clockInfoIndex * entries invalid) -> ps->num_levels stays 0 from kzalloc. * * Modes: * ./t accounting: prints the unsigned index and byte offset. * ./t crash executes the verbatim read against a single-page mapping * -> read at levels + 0xFFFFFFFF*12 = +51539607540 -> SIGSEGV * (kernel equivalent: page fault in kernel mode -> panic). * * Unprivileged trigger in the kernel: AMDGPU_INFO ioctl / AMDGPU_INFO_DEV_INFO * (amdgpu_kms.c:700, DRM_AUTH|DRM_RENDER_ALLOW) -> amdgpu_dpm_get_sclk(false) * -> kv_dpm_get_sclk; also dce_v8_0 modeset (dce_v8_0.c:971). */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <sys/mman.h> #define SUMO_MAX_HARDWARE_POWERLEVELS 5 struct kv_pl { uint32_t sclk; uint8_t vddc_index; uint8_t ds_divider_index; uint8_t ss_divider_index; uint8_t allow_gnb_slow; uint8_t force_nbp_state; uint8_t display_wm; uint8_t vce_wm; }; struct kv_ps { struct kv_pl levels[SUMO_MAX_HARDWARE_POWERLEVELS]; uint32_t num_levels; uint8_t need_dfs_bypass; uint8_t dpm0_pg_nb_ps_lo, dpm0_pg_nb_ps_hi, dpmx_nb_ps_lo, dpmx_nb_ps_hi; }; /* verbatim kv_dpm.c:2929-2939 shape */ static uint32_t kv_dpm_get_sclk(struct kv_ps *requested_state, int low) { if (low) return requested_state->levels[0].sclk; else return requested_state->levels[requested_state->num_levels - 1].sclk; } /* verbatim kv_dpm.c:1790-1794 shape (kv_set_valid_clock_range high loop) */ static uint32_t kv_set_valid_clock_range_sink(struct kv_ps *new_ps) { uint32_t i; for (i = 4; i > 0; i--) { /* graphics_dpm_level_count-1 = 4 */ if (i <= new_ps->levels[new_ps->num_levels - 1].sclk) break; } return new_ps->levels[new_ps->num_levels - 1].sclk; } int main(int argc, char **argv) { int crash = (argc > 1 && strcmp(argv[1], "crash") == 0); /* kzalloc'd kv_ps for a VBIOS state with zero valid clock levels: * kv_parse_power_table loop (:2756-2769) never calls * kv_parse_pplib_clock_info -> num_levels stays 0. */ if (crash) { uint8_t *page = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); if (page == MAP_FAILED) { perror("mmap"); return 1; } struct kv_ps *ps = (void *)page; ps->num_levels = 0; /* kzalloc value, never set */ printf("crash mode: levels at %p, reading levels[num_levels-1].sclk\n", (void *)ps->levels); fflush(stdout); (void)kv_dpm_get_sclk(ps, 0); printf("NOT REACHED\n"); return 0; } struct kv_ps ps; memset(&ps, 0, sizeof(ps)); /* num_levels = 0 */ uint64_t idx = (uint64_t)(uint32_t)(ps.num_levels - 1); printf("num_levels = %u\n", ps.num_levels); printf("index used : levels[num_levels - 1] = levels[%llu]\n", (unsigned long long)idx); printf("byte offset: %llu * sizeof(kv_pl)=%zu -> levels + %llu bytes (~%.1f GB)\n", (unsigned long long)idx, sizeof(struct kv_pl), (unsigned long long)(idx * sizeof(struct kv_pl)), (double)(idx * sizeof(struct kv_pl)) / 1e9); printf("kernel sinks: kv_dpm.c:2938 (get_sclk, unprivileged via AMDGPU_INFO ioctl), kv_dpm.c:1791 and :1817 (set_valid_clock_range)\n"); return 0; } |