⬢ DragonFlyBSD Kernel Audit
DF-4352 / df4352.c
← back to finding ↓ download raw
/*
 * DF-4352 model proof - dce110_resource.c (DCE11 / Carrizo-Stoney)
 * ----------------------------------------------------------------
 * dce110_link_encoder_create() (sys/dev/drm/amd/display/dc/dce110/
 * dce110_resource.c:576-592) performs three const-table lookups using
 * VBIOS-derived enums with no validation:
 *
 *      &link_enc_regs[enc_init_data->transmitter],       // 7 entries (:588)
 *      &link_enc_aux_regs[enc_init_data->channel - 1],   // 6 entries (:589)
 *      &link_enc_hpd_regs[enc_init_data->hpd_source]     // 6 entries (:590)
 *
 * The three values are produced at link bring-up (core/dc_link.c):
 *   transmitter : translate_encoder_to_transmitter()  :1006-1024+  returns
 *                TRANSMITTER_UNKNOWN (-1) for unrecognized encoder ids,
 *                and NUTMEG/TRAvis/UNIPHY_G map to 6..9;
 *   channel     : get_ddc_line()                      :964-1004   returns
 *                CHANNEL_ID_UNKNOWN (0) by default, DDC_VGA (7),
 *                I2C_PAD (8) explicitly;
 *   hpd_source  : get_hpd_line()                      :925-962    returns
 *                HPD_SOURCEID_UNKNOWN (7) by default.
 *
 * An out-of-range index reads a bogus 'struct dce110_link_enc_registers'
 * (or aux/hpd regs) from adjacent .rodata; dce110_link_encoder_construct
 * stores that pointer and every later REG_READ and REG_UPDATE on the
 * encoder dereferences it as an MMIO register base -> VBIOS-steerable
 * wild MMIO offsets (bring-up hang / machine check at minimum).
 *
 * Twin of DF-4312 (dcn10) / DF-4319 (dce80) at the dce110 instance,
 * with different table sizes (7/6/6) and a -1 shift on channel.
 *
 * This model reproduces the three index expressions against the real
 * enum domains and reports every OOB case.
 */
#include <stdio.h>

/* enum domains verbatim from include/grph_object_defs.h:45-94 */
enum hpd_source_id {
    HPD_SOURCEID1 = 0, HPD_SOURCEID2, HPD_SOURCEID3,
    HPD_SOURCEID4, HPD_SOURCEID5, HPD_SOURCEID6,
    HPD_SOURCEID_COUNT,        /* 6 */
    HPD_SOURCEID_UNKNOWN       /* 7 */
};
enum channel_id {
    CHANNEL_ID_UNKNOWN = 0, CHANNEL_ID_DDC1, CHANNEL_ID_DDC2,
    CHANNEL_ID_DDC3, CHANNEL_ID_DDC4, CHANNEL_ID_DDC5, CHANNEL_ID_DDC6,
    CHANNEL_ID_DDC_VGA,        /* 7 */
    CHANNEL_ID_I2C_PAD,        /* 8 */
    CHANNEL_ID_COUNT
};
enum transmitter {
    TRANSMITTER_UNKNOWN = (-1L),           /* -1 */
    TRANSMITTER_UNIPHY_A,                  /*  0 */
    TRANSMITTER_UNIPHY_B, TRANSMITTER_UNIPHY_C, TRANSMITTER_UNIPHY_D,
    TRANSMITTER_UNIPHY_E, TRANSMITTER_UNIPHY_F,
    TRANSMITTER_NUTMEG_CRT,                /*  6 */
    TRANSMITTER_TRAVIS_CRT,                /*  7 */
    TRANSMITTER_TRAVIS_LCD,                /*  8 */
    TRANSMITTER_UNIPHY_G,                  /*  9 */
    TRANSMITTER_COUNT
};

#define link_enc_regs_N     7     /* dce110_resource.c:259-267 */
#define link_enc_aux_regs_N 6     /* dce110_resource.c:230-237  */
#define link_enc_hpd_regs_N 6     /* dce110_resource.c:244-251  */

static int oob(long idx, long n, const char *what, const char *val)
{
    if (idx < 0 || idx >= n) {
        printf("  OOB  %-24s = %-3ld  -> index %ld into %d-entry table\n",
               what, idx, idx, (int)n);
        printf("       value: %s\n", val);
        return 1;
    }
    return 0;
}

int main(void)
{
    int bad = 0, tested = 0;

    printf("DF-4352 model: dce110_link_encoder_create const-table index "
           "domains\n");
    printf("lookup expressions verbatim from dce110_resource.c:588-590\n");
    printf("table sizes: link_enc_regs=%d, link_enc_aux_regs=%d, "
           "link_enc_hpd_regs=%d\n\n",
           link_enc_regs_N, link_enc_aux_regs_N, link_enc_hpd_regs_N);

    /* ---- transmitter domain (dc_link.c translate_encoder_to_transmitter) ---- */
    {
        long dom[] = { TRANSMITTER_UNKNOWN,
                       TRANSMITTER_UNIPHY_A, TRANSMITTER_UNIPHY_B,
                       TRANSMITTER_UNIPHY_C, TRANSMITTER_UNIPHY_D,
                       TRANSMITTER_UNIPHY_E, TRANSMITTER_UNIPHY_F,
                       TRANSMITTER_NUTMEG_CRT, TRANSMITTER_TRAVIS_CRT,
                       TRANSMITTER_TRAVIS_LCD, TRANSMITTER_UNIPHY_G };
        const char *nm[] = { "TRANSMITTER_UNKNOWN (unrecognized encoder id)",
                       "UNIPHY_A", "UNIPHY_B", "UNIPHY_C", "UNIPHY_D",
                       "UNIPHY_E", "UNIPHY_F", "NUTMEG_CRT (external enc)",
                       "TRAVIS_CRT (external enc)", "TRAVIS_LCD (external enc)",
                       "UNIPHY_G" };
        printf("link_enc_regs[transmitter]:\n");
        for (unsigned i = 0; i < sizeof(dom)/sizeof(dom[0]); i++) {
            tested++;
            bad += oob(dom[i], link_enc_regs_N, "transmitter", nm[i]);
        }
    }
    /* ---- channel domain (dc_link.c get_ddc_line) ---- */
    {
        long dom[] = { CHANNEL_ID_UNKNOWN, CHANNEL_ID_DDC1, CHANNEL_ID_DDC2,
                       CHANNEL_ID_DDC3, CHANNEL_ID_DDC4, CHANNEL_ID_DDC5,
                       CHANNEL_ID_DDC6, CHANNEL_ID_DDC_VGA,
                       CHANNEL_ID_I2C_PAD };
        const char *nm[] = { "CHANNEL_ID_UNKNOWN (default when no ddc pin)",
                       "DDC1", "DDC2", "DDC3", "DDC4", "DDC5", "DDC6",
                       "CHANNEL_ID_DDC_VGA (VGA connector)",
                       "CHANNEL_ID_I2C_PAD (I2C pad connector)" };
        printf("\nlink_enc_aux_regs[channel - 1]:\n");
        for (unsigned i = 0; i < sizeof(dom)/sizeof(dom[0]); i++) {
            tested++;
            bad += oob(dom[i] - 1, link_enc_aux_regs_N, "channel-1", nm[i]);
        }
    }
    /* ---- hpd domain (dc_link.c get_hpd_line) ---- */
    {
        long dom[] = { HPD_SOURCEID1, HPD_SOURCEID2, HPD_SOURCEID3,
                       HPD_SOURCEID4, HPD_SOURCEID5, HPD_SOURCEID6,
                       HPD_SOURCEID_COUNT, HPD_SOURCEID_UNKNOWN };
        const char *nm[] = { "HPD_SOURCEID1", "HPD_SOURCEID2",
                       "HPD_SOURCEID3", "HPD_SOURCEID4", "HPD_SOURCEID5",
                       "HPD_SOURCEID6", "HPD_SOURCEID_COUNT",
                       "HPD_SOURCEID_UNKNOWN (default: no HPD gpio)" };
        printf("\nlink_enc_hpd_regs[hpd_source]:\n");
        for (unsigned i = 0; i < sizeof(dom)/sizeof(dom[0]); i++) {
            tested++;
            bad += oob(dom[i], link_enc_hpd_regs_N, "hpd_source", nm[i]);
        }
    }

    printf("\n%d of %d reachable enum values index out of bounds\n", bad, tested);
    if (bad > 0)
        printf("CONST-TABLE OOB READ CONFIRMED (model): each OOB case reads "
               "adjacent .rodata that becomes the encoder's MMIO register "
               "base (wild MMIO on every later REG_*)\n");
    return bad ? 0 : 1;
}