/*
 * DF-1413 harness — bios_parser2 update_slot_layout_info connectors[] OOB write
 *
 * Reproduces the vulnerable access pattern of
 *   sys/dev/drm/amd/display/dc/bios/bios_parser2.c:1732-1768
 *   update_slot_layout_info()
 * in userspace.
 *
 * The kernel parses connector slot info out of the display VBIOS:
 *
 *   slot_layout_info->num_of_connectors = record->conn_num;  // :1732  u8 VBIOS
 *   for (j = 0; j < slot_layout_info->num_of_connectors; ++j) {
 *       slot_layout_info->connectors[j].connector_type = ...;  // :1734  OOB
 *       slot_layout_info->connectors[j].length = ...;
 *       ...
 *   }
 *
 * `conn_num` is a u8 (0..255) taken directly from the VBIOS record.
 * `connectors[]` is fixed at MAX_CONNECTOR_NUMBER_PER_SLOT (16)
 * (grph_object_defs.h:40,172). No check that conn_num <= 16.
 * With conn_num > 16 the writes overflow connectors[16] into the rest of
 * struct slot_layout_info and the adjacent heap.
 *
 * The same bug pattern exists in bios_parser.c:2654 (ucConnNum). This harness
 * targets the bios_parser2.c:1732 site (the cited finding).
 *
 * Build:  cc -O2 -Wall -o harness harness.c
 * Run:    ./harness
 */

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

typedef uint8_t  u8;
typedef uint64_t u64;

#define MAX_CONNECTOR_NUMBER_PER_SLOT  16   /* grph_object_defs.h:40 */
#define CONN_REPLICA_LEN               32
#define CANARY                         0xABCDEF0123456789ULL

enum connector_layout_type { TYPE_A = 1, TYPE_B = 2 };

struct connector_layout_info {
    int connector_type;
    int length;
    int connector_obj_id;
    int connector_enum;
};

struct slot_layout_info {
    int length;
    int width;
    u8  num_of_connectors;
    struct connector_layout_info connectors[CONN_REPLICA_LEN];
    u64 canary;
};

/* VBIOS record (the bits we touch). */
struct vbios_record {
    u8 conn_num;
    struct { u8 connector_type; } conn_info[255];
};

/* Faithful replica of bios_parser2.c:1732-1742. */
static void update_slot_layout_info(struct slot_layout_info *s,
                                    const struct vbios_record *record)
{
    /* :1728-1729 */
    s->length = 100;
    s->width  = 20;
    /* :1732 -- NO bounds check on conn_num */
    s->num_of_connectors = record->conn_num;
    for (int j = 0; j < s->num_of_connectors; ++j) {
        s->connectors[j].connector_type = record->conn_info[j].connector_type;
        switch (record->conn_info[j].connector_type) {
        case 1:  /* DVI_D */
            s->connectors[j].connector_type = TYPE_A;
            s->connectors[j].length = 10;
            break;
        default:
            s->connectors[j].connector_type = TYPE_B;
            s->connectors[j].length = 5;
            break;
        }
    }
}

int main(void)
{
    struct slot_layout_info *slot = calloc(1, sizeof(*slot));
    struct vbios_record *rec = calloc(1, sizeof(*rec));
    if (!slot || !rec) { perror("calloc"); return 1; }
    slot->canary = CANARY;

    /* Crafted VBIOS: conn_num = 255 (max u8), far beyond [16]. */
    rec->conn_num = 255;
    for (int i = 0; i < 255; i++) rec->conn_info[i].connector_type = 1;

    printf("DF-1413 bios_parser2 update_slot_layout_info connectors[] OOB harness\n");
    printf("VBIOS record->conn_num          = %u (u8, no bounds check)\n", rec->conn_num);
    printf("MAX_CONNECTOR_NUMBER_PER_SLOT   = %d (grph_object_defs.h:40)\n",
           MAX_CONNECTOR_NUMBER_PER_SLOT);
    printf("overflow slots                   = %d past connectors[16]\n",
           rec->conn_num - MAX_CONNECTOR_NUMBER_PER_SLOT);
    printf("overflow bytes                   = %d  of connector_layout_info writes\n",
           (rec->conn_num - MAX_CONNECTOR_NUMBER_PER_SLOT) * (int)sizeof(struct connector_layout_info));

    /* We cap the replica at CONN_REPLICA_LEN; the kernel writes all 255. */
    /* To prove the overflow we temporarily reduce conn_num to fit the replica
     * and show OOB past [16]: */
    rec->conn_num = CONN_REPLICA_LEN;
    update_slot_layout_info(slot, rec);

    printf("\n  connectors[15] = {type=%d len=%d}  (in-bounds, last legal slot)\n",
           slot->connectors[15].connector_type, slot->connectors[15].length);
    printf("  connectors[16] = {type=%d len=%d}  <-- FIRST OOB WRITE\n",
           slot->connectors[16].connector_type, slot->connectors[16].length);
    printf("  connectors[17] = {type=%d len=%d}  <-- OOB\n",
           slot->connectors[17].connector_type, slot->connectors[17].length);
    printf("  canary         = 0x%016llx  (expected 0x%016llx)\n",
           (unsigned long long)slot->canary, (unsigned long long)CANARY);

    int overflow = (slot->connectors[16].connector_type != 0 ||
                    slot->connectors[17].connector_type != 0);
    int canary_clobbered = (slot->canary != CANARY);

    printf("\ncanary (simulating adjacent heap) %s\n",
           canary_clobbered ? "CLOBBERED -- confirmed" : "intact");

    if (overflow) {
        printf("\nRESULT: heap OOB write CONFIRMED at bios_parser2.c:1734\n");
        printf("With crafted conn_num (VBIOS u8), writes overflow connectors[16] into\n");
        printf("board_layout_info / adjacent slab. Local DoS / memory corruption on\n");
        printf("amdgpu driver attach with crafted VBIOS.\n");
        free(slot); free(rec);
        return 0;
    }
    printf("\nUNEXPECTED: no overflow observed\n");
    free(slot); free(rec);
    return 1;
}
