/*
 * DF-2202 trigger stub — amdgpu df_v1_7 OOB array index.
 *
 * This file is a STATIC ANALYSIS TRIGGER. It is not, and cannot be, a runtime
 * exploit: the vulnerable path lives inside the amdgpu DRM module which is
 * only reachable on Vega10 / Vega12 (CHIP_VEGA10 / CHIP_VEGA12) discrete GPU
 * hardware that this guest does not have. The function chain is:
 *
 *   soc15.c:521-522  adev->df_funcs = &df_v1_7_funcs;        // VEGA10/12 only
 *   gmc_v9_0.c:812   numchan = adev->df_funcs->get_hbm_channel_number(adev);
 *
 * which calls
 *
 *   df_v1_7.c:65  fb_channel_number = adev->df_funcs->get_fb_channel_number(adev);
 *   df_v1_7.c:67  return df_v1_7_channel_number[fb_channel_number];
 *
 * `get_fb_channel_number` masks a HW register field to a 4-bit value
 * (0..15) but the table has only 9 entries (indices 0..8). Values 9..15
 * read 1..6 u32 words (4..24 bytes) past the end of the static array.
 *
 * The "trigger" is the act of initializing a Vega10/12 dGPU with an
 * IntLvNumChan register field holding 9..15 — produced either by a malicious
 * VBIOS ROM (hotplugged PCIe card), a malicious hypervisor SR-IOV VF, or a
 * transient read during early bring-up. None of these can be reproduced on
 * this audit guest; verification is therefore source-only.
 *
 * This stub exists so a human reader of the evidence pack has a runnable
 * demonstration of the *indexing* bug in isolation. It does NOT touch kernel
 * memory; it builds as a userspace program and proves the table is OOB-exposed
 * to its full 4-bit input domain.
 */

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

/*
 * Verbatim copy of sys/dev/drm/amd/amdgpu/df_v1_7.c:30 (9 elements, 0..8).
 */
static const uint32_t df_v1_7_channel_number[] = {1, 2, 0, 4, 0, 8, 0, 16, 2};

#define TABLE_NENTRY (sizeof(df_v1_7_channel_number) / sizeof(df_v1_7_channel_number[0]))

/*
 * Verbatim mask/shift from
 * sys/dev/drm/amd/include/asic_reg/df/df_1_7_sh_mask.h:39,44
 */
#define DF_CS_AON0_DramBaseAddress0__IntLvNumChan__SHIFT  0x4
#define DF_CS_AON0_DramBaseAddress0__IntLvNumChan_MASK    0x000000F0L

/* Emulate df_v1_7_get_fb_channel_number(): register field decoded into index. */
static uint32_t df_v1_7_get_fb_channel_number(uint32_t hw_reg)
{
    uint32_t tmp = hw_reg;
    tmp &= DF_CS_AON0_DramBaseAddress0__IntLvNumChan_MASK;
    tmp >>= DF_CS_AON0_DramBaseAddress0__IntLvNumChan__SHIFT;
    return tmp;
}

int main(void)
{
    printf("DF-2202 trigger stub — amdgpu df_v1_7 OOB array index\n");
    printf("table df_v1_7_channel_number[] has %zu entries (indices 0..%zu)\n",
           TABLE_NENTRY, TABLE_NENTRY - 1);
    printf("IntLvNumChan field is 4-bit -> values 0..15\n");
    printf("\n");

    int oob_count = 0;
    for (uint32_t reg = 0; reg < 0x10; reg++) {
        uint32_t idx = df_v1_7_get_fb_channel_number(reg << DF_CS_AON0_DramBaseAddress0__IntLvNumChan__SHIFT);
        const char *where = (idx < TABLE_NENTRY) ? "IN-BOUNDS" : "OOB";
        if (idx >= TABLE_NENTRY) {
            size_t off_words = idx - TABLE_NENTRY;        /* 0..6 */
            size_t off_bytes = off_words * sizeof(uint32_t); /* 0..24 */
            oob_count++;
            printf("  field=%2u  ->  INDEX %2u  %s  (u32 read at offset %zu word(s) = %zu byte(s) past end)\n",
                   idx, idx, where, off_words, off_bytes);
        } else {
            printf("  field=%2u  ->  INDEX %2u  %s  value=%u\n",
                   idx, idx, where, df_v1_7_channel_number[idx]);
        }
    }

    printf("\n");
    printf("%d of 16 possible field values land OUT-OF-BOUNDS on the table.\n", oob_count);
    printf("OOB reads land at offset 0..6 u32 words (0..24 bytes) past the end\n");
    printf("of the static array, reading whatever 4-byte word lives there.\n");
    return 0;
}
