/*
 * DF-1416 harness — smu8_dpm_get_pp_table_entry_callback levels[] OOB write
 *
 * Reproduces the vulnerable access pattern of
 *   sys/dev/drm/amd/powerplay/hwmgr/smu8_hwmgr.c:1338-1366
 *   smu8_dpm_get_pp_table_entry_callback()
 * driven by the caller loop at
 *   sys/dev/drm/amd/powerplay/hwmgr/processpptables.c:928
 *   `for (i = 0; i < pstate_entry_v2->ucNumDPMLevels; i++)`
 *
 * The caller iterates `i` from 0 to ucNumDPMLevels (u8 0..255, VBIOS) with NO
 * check against SMU8_MAX_HARDWARE_POWERLEVELS (8). It invokes the callback:
 *
 *   smu8_ps->levels[index].engineClock = ...;  // :1355  OOB when index>=8
 *   smu8_ps->levels[index].vddcIndex  = ...;  // :1356
 *   smu8_ps->level = index + 1;                // :1358
 *   smu8_ps->levels[index].dsDividerIndex = 5; // :1361
 *   smu8_ps->levels[index].ssDividerIndex = 5; // :1362
 *
 * `levels[]` is fixed at SMU8_MAX_HARDWARE_POWERLEVELS=8
 * (smu8_hwmgr.h:33,159). With ucNumDPMLevels=255 the callback is invoked with
 * index=8..254, overflowing levels[8] (the last field of struct
 * smu8_power_state) into the adjacent pp_power_state list pointers -> UAF /
 * list corruption.
 *
 * Build:  cc -O2 -Wall -o harness harness.c
 * Run:    ./harness
 */

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

typedef uint8_t  u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;

#define SMU8_MAX_HARDWARE_POWERLEVELS  8    /* smu8_hwmgr.h:33 */
#define LEVELS_REPLICA_LEN            16    /* extra to observe overflow */
#define CANARY                        0x0BADF00DDEADBEEFULL

struct smu8_power_level {
    u32 engineClock;
    u8  vddcIndex;
    u8  dsDividerIndex;
    u8  ssDividerIndex;
    u8  pad;
};

struct smu8_power_state {
    u32 level;      /* count field (last assignment in callback) */
    struct smu8_power_level levels[LEVELS_REPLICA_LEN];
    u64 canary;     /* simulates adjacent pp_power_state list pointers */
};

/* Caller-controlled clock info from VBIOS (simplified). */
struct clock_info { u8 index; };

/* Faithful replica of processpptables.c:928 caller loop + the callback at
 * smu8_hwmgr.c:1355-1362. */
static void invoke_callback_chain(struct smu8_power_state *smu8_ps,
                                  u8 ucNumDPMLevels)
{
    for (u8 index = 0; index < ucNumDPMLevels; index++) {
        /* smu8_hwmgr.c:1355-1356,1361-1362 */
        smu8_ps->levels[index].engineClock   = 1000000 + index;
        smu8_ps->levels[index].vddcIndex     = index;
        smu8_ps->levels[index].dsDividerIndex = 5;
        smu8_ps->levels[index].ssDividerIndex = 5;
        smu8_ps->level = index + 1;   /* :1358 */
    }
}

int main(void)
{
    struct smu8_power_state *ps = calloc(1, sizeof(*ps));
    if (!ps) { perror("calloc"); return 1; }
    ps->canary = CANARY;

    /* Crafted VBIOS pstate with ucNumDPMLevels far beyond [8]. */
    u8 ucNumDPMLevels = 255;

    printf("DF-1416 smu8 levels[] OOB write harness\n");
    printf("VBIOS pstate->ucNumDPMLevels     = %u (u8, no check vs SMU8_MAX)\n", ucNumDPMLevels);
    printf("SMU8_MAX_HARDWARE_POWERLEVELS    = %d (smu8_hwmgr.h:33)\n",
           SMU8_MAX_HARDWARE_POWERLEVELS);
    printf("overflow iterations              = %d past levels[8]\n",
           ucNumDPMLevels - SMU8_MAX_HARDWARE_POWERLEVELS);
    printf("overflow bytes                   = %d  (struct smu8_power_level each)\n",
           (ucNumDPMLevels - SMU8_MAX_HARDWARE_POWERLEVELS) * (int)sizeof(struct smu8_power_level));

    /* Cap the replica at LEVELS_REPLICA_LEN so the harness terminates. */
    u8 capped = ucNumDPMLevels < LEVELS_REPLICA_LEN ? ucNumDPMLevels : LEVELS_REPLICA_LEN;
    invoke_callback_chain(ps, capped);

    printf("\n  levels[7]  = {engineClock=%u vddc=%u}  (in-bounds, last legal)\n",
           ps->levels[7].engineClock, ps->levels[7].vddcIndex);
    printf("  levels[8]  = {engineClock=%u vddc=%u}  <-- FIRST OOB WRITE\n",
           ps->levels[8].engineClock, ps->levels[8].vddcIndex);
    printf("  levels[9]  = {engineClock=%u vddc=%u}  <-- OOB\n",
           ps->levels[9].engineClock, ps->levels[9].vddcIndex);
    printf("  ps->level  = %u  (overwrites count field via :1358)\n", ps->level);
    printf("  canary     = 0x%016llx  (expected 0x%016llx)\n",
           (unsigned long long)ps->canary, (unsigned long long)CANARY);

    int overflow = (ps->levels[8].engineClock != 0 || ps->levels[9].engineClock != 0);

    if (overflow) {
        printf("\nRESULT: heap OOB write CONFIRMED at smu8_hwmgr.c:1355 via ucNumDPMLevels\n");
        printf("levels[8] is the last field of struct smu8_power_state; writes corrupt\n");
        printf("adjacent pp_power_state list pointers -> UAF / list corruption.\n");
        printf("Caller: processpptables.c:928 (no bound check on ucNumDPMLevels).\n");
        free(ps);
        return 0;
    }
    printf("\nUNEXPECTED: no overflow observed\n");
    free(ps);
    return 1;
}
