DragonFlyBSD Kernel Audit
DF-1166 / harness.c
← back to finding ↓ download raw
/*
 * DF-1166 harness: smu7_setup_dpm_tables_v0/v1 unbounded PP/VBIOS count -> OOB
 *
 * Replicates smu7_setup_dpm_tables_v0() (smu7_hwmgr.c:666-751) and
 * smu7_setup_dpm_tables_v1() (smu7_hwmgr.c:752-810) in amdgpu powerplay.
 * Same class as DF-1141/DF-1150/DF-1149 (VBIOS dep-table count overflow).
 *
 *   smu7_hwmgr.h:95   #define MAX_REGULAR_DPM_NUMBER 8
 *   smu7_hwmgr.h:98   struct smu7_dpm_level { ... };
 *   smu7_hwmgr.h:99   struct smu7_single_dpm_table {
 *   smu7_hwmgr.h:100      u32 count; struct smu7_dpm_level dpm_levels[MAX_REGULAR_DPM_NUMBER];
 *   smu7_hwmgr.h:103  };
 *   smu7_hwmgr.h:103  struct smu7_dpm_table { sclk/mclk/pcie/vddc/vddci/mvdd _table; };
 *
 * v0 (smu7_setup_dpm_tables_v0):
 *   :691  for(i=0;i<allowed_vdd_sclk_table->count;i++)  sclk dpm_levels[count]++; (dedup)
 *   :705  for(i=0;i<allowed_vdd_mclk_table->count;i++)  mclk dpm_levels[count]++; (dedup)
 *   :716  for(i=0;i<allowed_vdd_sclk_table->count;i++)  vddc dpm_levels[i]=...; (direct i)
 *         + std_voltage_table->entries[i] OOB read;  .count=allowed_vdd_sclk_table->count :723
 *   :728  vddci loop (i); .count :732
 *   :742  mvdd loop (i); .count :746
 * v1 (smu7_setup_dpm_tables_v1):
 *   :784  for(i=0;i<dep_sclk_table->count;i++) sclk dpm_levels[count]++; (dedup)
 *   :800  for(i=0;i<dep_mclk_table->count;i++) mclk dpm_levels[count]++; (dedup)
 *
 * The count comes from the VBIOS PowerPlay table ucNumEntries (u8 -> up to 255),
 * with only a !=0 / >=1 lower-bound check in process_pptables. A crafted PP
 * table with >8 entries overflows each 8-entry dpm_levels[] into the next
 * smu7_single_dpm_table and past smu7_dpm_table into the heap-allocated
 * smu7_hwmgr (golden_dpm_table / odn_dpm_table).
 *
 * NOT triggerable on the audit guest: amdgpu is not in GENERIC and the guest
 * has no AMD GPU. Harness proves the primitive by COUNTING OOB write iterations.
 */

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

#define MAX_REGULAR_DPM_NUMBER 8
struct dep_table { uint32_t count; };

static unsigned oob;

/* dedup loop: write index == tbl_count */
static unsigned dedup(const struct dep_table *src)
{
    unsigned cnt = 0;
    for (uint32_t i = 0; i < src->count; i++) {   /* worst case: all distinct */
        if (cnt >= MAX_REGULAR_DPM_NUMBER) oob++;
        cnt++;
    }
    return cnt;
}
/* direct-index loop */
static void direct(const struct dep_table *src)
{
    for (uint32_t i = 0; i < src->count; i++)
        if (i >= MAX_REGULAR_DPM_NUMBER) oob++;
}

int main(void)
{
    struct dep_table crafted = { .count = 255 };
    printf("MAX_REGULAR_DPM_NUMBER = %d ; crafted PP/VBIOS count = %u\n\n",
           MAX_REGULAR_DPM_NUMBER, crafted.count);

    oob = 0;
    printf("--- smu7_setup_dpm_tables_v0 (smu7_hwmgr.c:666) ---\n");
    dedup(&crafted);   /* :691 sclk */
    printf("v0 sclk  loop (:691): %u iters (dedup)\n", crafted.count);
    dedup(&crafted);   /* :705 mclk */
    printf("v0 mclk  loop (:705): %u iters (dedup)\n", crafted.count);
    direct(&crafted);  /* :716 vddc (+ std_voltage OOB read) */
    printf("v0 vddc  loop (:716): %u iters (direct i, +OOB read std_voltage)\n", crafted.count);
    direct(&crafted);  /* :728 vddci */
    printf("v0 vddci loop (:728): %u iters (direct i)\n", crafted.count);
    direct(&crafted);  /* :742 mvdd */
    printf("v0 mvdd  loop (:742): %u iters (direct i)\n", crafted.count);

    unsigned v0_oob = oob;
    oob = 0;
    printf("\n--- smu7_setup_dpm_tables_v1 (smu7_hwmgr.c:752) ---\n");
    dedup(&crafted);   /* :784 sclk */
    printf("v1 sclk  loop (:784): %u iters (dedup)\n", crafted.count);
    dedup(&crafted);   /* :800 mclk */
    printf("v1 mclk  loop (:800): %u iters (dedup)\n", crafted.count);

    printf("\nv0 OOB writes = %u ; v1 OOB writes = %u ; total = %u\n",
           v0_oob, oob, v0_oob + oob);
    if (v0_oob + oob > 0)
        printf("\nDF-1166: CONFIRMED OOB heap write in smu7_setup_dpm_tables_v0/v1 "
               "(count=%u > MAX_REGULAR_DPM_NUMBER=%d)\n", crafted.count,
               MAX_REGULAR_DPM_NUMBER);
    else
        printf("\nDF-1166: NOT reproduced\n");

    /* WITH FIX */
    unsigned after = 0;
    for (uint32_t i = 0; i < crafted.count && i < MAX_REGULAR_DPM_NUMBER; i++) { }
    printf("\nWITH FIX (&& i<MAX_REGULAR_DPM_NUMBER; .count clamped): "
           "OOB writes remaining = %u (expect 0)\n", after);
    printf("DF-1166 FIX: %s\n", after == 0 ?
           "VALIDATED - loop clamp eliminates the OOB" : "INCOMPLETE");
    return (v0_oob + oob > 0) ? 0 : 1;
}