DF-3890 / vid_mapping_zero_entries.c
/* * DF-3890 harness — kv_convert_vid2_to_vid7 with num_entries == 0: * vid_mapping_table->entries[num_entries - 1] wild read (+~16 GB). * * Mirrors sys/dev/drm/amd/amdgpu/kv_dpm.c:72-92 (kv_convert_vid2_to_vid7, * sink at :90) and the identical pattern at :114 * (kv_convert_vid7_to_vid2), with 1:1 struct layouts (kv_dpm.h). * * Preconditions (all VBIOS-controlled, parse-legal): * - IntegratedSystemInfo V1_8 sAvail_SCLK[] has strictly-increasing * nonzero ulSupportedSCLK (so num_max_dpm_entries > 0 and the guard at * kv_dpm.c:2040 passes) but usVoltageID == 0 for every entry * -> sumo_construct_vid_mapping_table leaves every vid_7bit 0 * -> compaction breaks at i == 0 -> num_entries = 0. * - PowerPlayInfo table has no VddcDependencyOnSCLK subtable (or * usVddcDependencyOnSCLKOffset == 0) -> vddc_dependency_on_sclk.count * == 0 -> the else branch at kv_dpm.c:86-91 is taken. * * Reach: kv_dpm_init -> kv_parse_sys_info_table (:2617) * -> kv_construct_max_power_limits_table (:2045) * -> kv_convert_2bit_index_to_voltage (:690) * -> kv_convert_vid2_to_vid7 (:90) -- at every KV boot. * * Modes: * ./t accounting: prints the unsigned index / byte offset. * ./t crash executes the verbatim read -> read at entries + 0xFFFFFFFF*4 * = +17179869180 bytes -> SIGSEGV (kernel equivalent: page * fault in kernel mode -> panic). */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <sys/mman.h> #define SUMO_MAX_NUMBER_VOLTAGES 4 struct sumo_vid_mapping_entry { uint16_t vid_2bit; uint16_t vid_7bit; }; struct sumo_vid_mapping_table { uint32_t num_entries; struct sumo_vid_mapping_entry entries[SUMO_MAX_NUMBER_VOLTAGES]; }; /* verbatim kv_dpm.c:72-92 shape; vddc_sclk_table.count == 0 so only the * else branch is live. */ static uint32_t kv_convert_vid2_to_vid7(struct sumo_vid_mapping_table *vid_mapping_table, uint32_t vid_2bit) { uint32_t i; /* if (vddc_sclk_table && vddc_sclk_table->count) ... : count == 0 */ for (i = 0; i < vid_mapping_table->num_entries; i++) { if (vid_mapping_table->entries[i].vid_2bit == vid_2bit) return vid_mapping_table->entries[i].vid_7bit; } return vid_mapping_table->entries[vid_mapping_table->num_entries - 1].vid_7bit; /* :90 */ } int main(int argc, char **argv) { int crash = (argc > 1 && strcmp(argv[1], "crash") == 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 sumo_vid_mapping_table *vt = (void *)page; vt->num_entries = 0; /* all usVoltageID == 0 in crafted VBIOS */ printf("crash mode: entries at %p, reading entries[num_entries-1].vid_7bit\n", (void *)vt->entries); fflush(stdout); (void)kv_convert_vid2_to_vid7(vt, 0); printf("NOT REACHED\n"); return 0; } struct sumo_vid_mapping_table vt; memset(&vt, 0, sizeof(vt)); /* num_entries = 0 */ uint64_t idx = (uint64_t)(uint32_t)(vt.num_entries - 1); printf("num_entries = %u (crafted VBIOS: all usVoltageID == 0)\n", vt.num_entries); printf("index used : entries[%llu]\n", (unsigned long long)idx); printf("byte offset: %llu * sizeof(entry)=%zu -> entries + %llu bytes (~%.1f GB)\n", (unsigned long long)idx, sizeof(struct sumo_vid_mapping_entry), (unsigned long long)(idx * sizeof(struct sumo_vid_mapping_entry)), (double)(idx * sizeof(struct sumo_vid_mapping_entry)) / 1e9); printf("kernel sink: kv_dpm.c:90 (and twin :114), boot path kv_dpm.c:2617 -> :2045 -> :690\n"); return 0; } |