/*
 * DF-3908 trigger/analysis — kernel stack OOB write in
 * tonga_program_memory_timing_parameters() (sys/dev/drm/amd/powerplay/smumgr/tonga_smumgr.c:1492-1507)
 *
 * Kernel path (hardware-gated: needs Tonga/Fiji GPU + crafted VBIOS):
 *   smu7_setup_dpm_tables_v1() copies the VBIOS SCLK/MCLK dependency-table
 *   entry counts (u8, up to 255, only deduplicated — never clamped to
 *   SMU72_MAX_LEVELS_*) into data->dpm_table.sclk_table.count /
 *   mclk_table.count.  tonga_init_smc_table() then calls
 *   tonga_program_memory_timing_parameters(), which indexes a 384-byte
 *   STACK object
 *       SMU72_Discrete_MCArbDramTimingTable arb_regs;   // entries[8][4], 12 B/entry
 *   with
 *       for (i = 0; i < sclk_table.count; i++)
 *         for (j = 0; j < mclk_table.count; j++)
 *           populate(..., &arb_regs.entries[i][j]);
 *   For sclk_count>8 or mclk_count>4 the writes run past the stack object
 *   (up to (255*4+255)*12 = 15300 bytes past a 384-byte frame), smashing
 *   adjacent stack frames / saved return addresses with GPU DRAM-timing
 *   register values derived from VBIOS-controlled clocks.
 *
 * This userspace harness reproduces the exact index arithmetic and prints
 * which writes land out of bounds (byte offset vs. the 384-byte object).
 */
#include <stdio.h>
#include <stdint.h>

#define SMU72_MAX_LEVELS_GRAPHICS 8
#define SMU72_MAX_LEVELS_MEMORY   4
#define ENTRY_SZ                  12   /* 2 x uint32 + uint8 + pad[3] */

int main(void)
{
    /* crafted VBIOS: 12 distinct SCLK dep entries, 6 distinct MCLK entries */
    uint32_t sclk_count = 12, mclk_count = 6;
    uint32_t obj_bytes  = SMU72_MAX_LEVELS_GRAPHICS *
                          SMU72_MAX_LEVELS_MEMORY * ENTRY_SZ; /* 384 */
    uint32_t i, j, oob_writes = 0, first_oob = 0, worst = 0;

    for (i = 0; i < sclk_count; i++) {
        for (j = 0; j < mclk_count; j++) {
            uint32_t off = (i * SMU72_MAX_LEVELS_MEMORY + j) * ENTRY_SZ;
            if (off + ENTRY_SZ > obj_bytes) {
                if (oob_writes == 0) first_oob = off;
                if (off > worst) worst = off;
                oob_writes++;
            }
        }
    }
    printf("arb_regs stack object        : %u bytes (entries[8][4], 12B each)\n",
           obj_bytes);
    printf("VBIOS dep counts (sclk,mclk) : %u x %u\n", sclk_count, mclk_count);
    printf("total writes                 : %u\n", sclk_count * mclk_count);
    printf("OOB writes past stack object : %u (first at +%u, worst at +%u..+%u)\n",
           oob_writes, first_oob, worst, worst + ENTRY_SZ - 1);
    printf("=> kernel stack corruption up to %u bytes past a %u-byte frame\n",
           worst + ENTRY_SZ - obj_bytes, obj_bytes);
    return (oob_writes > 0) ? 1 : 0; /* rc!=0 == corruption demonstrated */
}
