DragonFlyBSD Kernel Audit
DF-1182 / harness.c
← back to finding ↓ download raw
/*
 * DF-1182 harness: demonstrates the missing bounds check on the
 * state-record clock indices (ucSocClockIndexLow/High,
 * ucGfxClockIndexLow/High, ucMemClockIndexLow/High — each u8 0-255)
 * used to index into the VBIOS dependency tables in
 *   vega10_get_pp_table_entry_callback_func()
 * at vega10_hwmgr.c:2970-3063.
 *
 * The function is reached only when amdgpu powerplay enumerates power
 * states from the VBIOS PowerPlay table — runs only on a real Vega10 GPU.
 *
 * Confirmed sites (vega10_hwmgr.c):
 *   3042-3043: socclk_dep_table->entries[state_entry->ucSocClockIndexLow].ulClk
 *   3044-3045: gfxclk_dep_table->entries[state_entry->ucGfxClockIndexLow].ulClk
 *   3046-3047: mclk_dep_table->entries[state_entry->ucMemClockIndexLow].ulMemClk
 *   3051-3052: socclk_dep_table->entries[state_entry->ucSocClockIndexHigh].ulClk
 *   3054-3055 / 3057-3058: gfxclk_dep_table->entries[state_entry->ucGfxClockIndexHigh]
 *   3061-3062: mclk_dep_table->entries[state_entry->ucMemClockIndexHigh].ulMemClk
 *
 * Same class as DF-1168 (smu7). The read values become the power-state
 * clocks and are exposed via sysfs.
 *
 * Compile: cc -O2 -o harness harness.c
 * Run:     ./harness
 */

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

struct dep_record { uint8_t vddInd; uint8_t pad[3]; uint32_t clk; };
struct dep_table  { uint8_t ucNumEntries; uint8_t pad[3]; struct dep_record entries[1]; };

static struct dep_table *alloc_dep(unsigned n)
{
    size_t sz = sizeof(uint32_t) + sizeof(struct dep_record) * n;
    struct dep_table *t = calloc(1, sz);
    if (!t) { perror("calloc"); exit(1); }
    t->ucNumEntries = (uint8_t)n;
    for (unsigned i = 0; i < n; i++) t->entries[i].clk = 1000 + i;
    return t;
}

static void check(const char *name, struct dep_table *t, uint8_t idx)
{
    if (idx >= t->ucNumEntries)
        printf("[OOB] %s->entries[%u] (ucNumEntries=%u) reads past %u-byte "
               "allocation (CWE-125)\n",
               name, idx, t->ucNumEntries,
               (unsigned)(sizeof(uint32_t) + sizeof(struct dep_record) * t->ucNumEntries));
    else
        printf("[ok]  %s[%u].clk = %u\n", name, idx, t->entries[idx].clk);
}

int main(void)
{
    struct dep_table *soc   = alloc_dep(2);   /* VBIOS supplies only 2 levels */
    struct dep_table *gfx   = alloc_dep(3);
    struct dep_table *mclk  = alloc_dep(1);

    /* Malicious state record from VBIOS: u8 indices out of range. */
    uint8_t ucSocClockIndexLow  = 200;
    uint8_t ucGfxClockIndexLow  = 100;
    uint8_t ucMemClockIndexLow  = 5;
    uint8_t ucSocClockIndexHigh = 255;
    uint8_t ucGfxClockIndexHigh = 50;
    uint8_t ucMemClockIndexHigh = 99;

    check("socclk", soc,  ucSocClockIndexLow);
    check("gfxclk", gfx,  ucGfxClockIndexLow);
    check("mclk",   mclk, ucMemClockIndexLow);
    check("socclk", soc,  ucSocClockIndexHigh);
    check("gfxclk", gfx,  ucGfxClockIndexHigh);
    check("mclk",   mclk, ucMemClockIndexHigh);

    printf("\n=== DF-1182 logic-level result ===\n");
    printf("Six u8 clock indices are used to index dep tables with NO\n");
    printf("bounds check against ucNumEntries.  Malicious VBIOS reads\n");
    printf("kernel heap past the small dep-table allocation.\n");
    printf("Path runs only when amdgpu powerplay attaches to a real Vega10\n");
    printf("GPU - no AMD hardware on this guest, so unreachable here.\n");

    free(soc); free(gfx); free(mclk);
    return 0;
}