/*
 * DF-1183 harness: demonstrates the missing count check in
 *   vega10_get_soc_index_for_max_uclk()  at vega10_hwmgr.c:3444-3453
 *
 * The function indexes vdd_dep_on_mclk->entries[NUM_UCLK_DPM_LEVELS - 1]
 * unconditionally, but the table is allocated for ucNumEntries slots
 * (from VBIOS u8).  If ucNumEntries < NUM_UCLK_DPM_LEVELS (4), the
 * hardcoded [3] is an OOB read.
 *
 * NUM_UCLK_DPM_LEVELS == 4 (sys/dev/drm/amd/powerplay/inc/smu9_driver_if.h:41).
 *
 * Allocation (vega10_processpptables.c:565-569):
 *   table_size = sizeof(uint32_t) + sizeof(phm_ppt_v1_clock_voltage_dependency_record) * ucNumEntries;
 *   mclk_table = kzalloc(table_size, GFP_KERNEL);
 *
 * Caller chain (vega10_hwmgr.c):
 *   vega10_upload_dpm_bootup_level() at line 3476-3477:
 *     if (data->smc_state_table.mem_boot_level == NUM_UCLK_DPM_LEVELS - 1)
 *         socclk_idx = vega10_get_soc_index_for_max_uclk(hwmgr);
 *
 * Compile: cc -O2 -o harness harness.c
 * Run:     ./harness
 */

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

#define NUM_UCLK_DPM_LEVELS 4

struct mclk_record { uint8_t vddInd; uint8_t pad[3]; uint32_t clk; uint32_t memclk; };
struct mclk_table  { uint32_t count; struct mclk_record entries[1]; };

static struct mclk_table *alloc_mclk(unsigned n)
{
    size_t sz = sizeof(uint32_t) + sizeof(struct mclk_record) * n;
    struct mclk_table *t = calloc(1, sz);
    if (!t) { perror("calloc"); exit(1); }
    t->count = n;
    return t;
}

/* Faithful transcription of vega10_get_soc_index_for_max_uclk: no check. */
static int buggy_index(struct mclk_table *t)
{
    return t->entries[NUM_UCLK_DPM_LEVELS - 1].vddInd + 1;
}

/* Fixed version: validate count first. */
static int fixed_index(struct mclk_table *t)
{
    if (t->count < NUM_UCLK_DPM_LEVELS)
        return -1;
    return t->entries[NUM_UCLK_DPM_LEVELS - 1].vddInd + 1;
}

int main(void)
{
    /* VBIOS with only 2 MCLK entries (ucNumEntries = 2). */
    struct mclk_table *t = alloc_mclk(2);

    printf("== buggy ==\n");
    printf("vega10_get_soc_index_for_max_uclk hardcoded "
           "entries[%d] but count=%u => OOB read (CWE-125)\n",
           NUM_UCLK_DPM_LEVELS - 1, t->count);
    printf("Returned index (garbage, from past-allocation bytes): %d\n",
           buggy_index(t));

    printf("\n== fixed ==\n");
    printf("Fixed version returns %d (validated count < %d)\n",
           fixed_index(t), NUM_UCLK_DPM_LEVELS);

    printf("\n=== DF-1183 logic-level result ===\n");
    printf("Hardcoded entries[3] without count check on a table allocated\n");
    printf("for ucNumEntries slots. Malicious VBIOS with ucNumEntries<4\n");
    printf("reads kernel heap. Path runs only when amdgpu powerplay is\n");
    printf("attached to a real Vega10 GPU - no AMD HW on this guest.\n");

    free(t);
    return 0;
}
