/*
 * DF-1797 source-confirmation harness (smu_helper phm_get_svi2_* OOB write).
 *
 * The buggy kernel code lives in AMD GPU PowerPlay init and is only
 * reachable with AMD GPU hardware whose VBIOS advertises a voltage
 * dependency/lookup table with count > 32.  This harness reproduces the
 * index-arithmetic primitive: the function writes entries[i] for
 * i = 0..count-1 into a fixed array of 32 entries without checking count.
 *
 *  sys/dev/drm/amd/powerplay/hwmgr/smu_helper.c:258,286,314 sets
 *     vol_table->count = dep_table->count;
 *  then loops i=0..count-1 writing vol_table->entries[i] (lines 260-263,
 *  288-291, 316-319) into entries[PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES=32]
 *  (ppatomctrl.h:211).  Compare ppatomctrl.c:546-551 which DOES bound
 *  ucGpioEntryNum <= 32 before its loop.
 *
 * Build:  cc -O2 -o harness harness.c
 * Run:    ./harness
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES 32   /* ppatomctrl.h:45 */

struct entry { unsigned int value, smio_low; };
struct pp_atomctrl_voltage_table {
    unsigned int count;
    unsigned int mask_low, phase_delay;
    struct entry entries[PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES];
};

/* Model of phm_get_svi2_vdd_voltage_table (smu_helper.c:300-322):
 * NO upper-bound check on lookup->count before writing entries[i]. */
static void
phm_get_svi2_vdd_voltage_table_model(struct pp_atomctrl_voltage_table *vol,
                                     unsigned int count, const struct entry *src)
{
    int i;
    vol->mask_low = 0;
    vol->phase_delay = 0;
    vol->count = count;                              /* line 314 */
    for (i = 0; i < (int)vol->count; i++)            /* line 316 */
        vol->entries[i] = src[i];                    /* line 317 — OOB when count>32 */
}

int main(void) {
    /* Allocate vol followed by a sentinel byte-region to detect OOB. */
    unsigned char *blob = calloc(1, sizeof(struct pp_atomctrl_voltage_table) + 1024);
    struct pp_atomctrl_voltage_table *vol = (void *)blob;
    unsigned char *sentinel = blob + sizeof(struct pp_atomctrl_voltage_table);
    for (int i = 0; i < 1024; i++) sentinel[i] = 0xCC;

    /* Malicious VBIOS lookup table: 64 entries. */
    unsigned int count = 64;
    struct entry src[64];
    for (int i = 0; i < 64; i++) { src[i].value = 1100+i; src[i].smio_low = 0; }

    phm_get_svi2_vdd_voltage_table_model(vol, count, src);

    /* Count sentinel bytes that got overwritten by the OOB entries. */
    int corrupted = 0;
    for (int i = 0; i < 1024; i++) if (sentinel[i] != 0xCC) corrupted++;

    printf("DF-1797: phm_get_svi2_vdd_voltage_table wrote %d entries past "
           "entries[32] (count=%u max=%d) -> %d bytes of trailing memory corrupted "
           "(sentinel bytes touched=%d)\n",
           (int)count - PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES,
           vol->count, PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES,
           (int)(count - PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES) * (int)sizeof(struct entry),
           corrupted);
    printf("Matches smu_helper.c:314 (count=lookup->count) + :317 (entries[i]=..) "
           "with no bound; contrast ppatomctrl.c:546-551 which DOES check.\n");
    free(blob);
    return 0;
}
