/*
 * DF-1374 harness: OOB heap write via unchecked firmware-controlled
 * DeviceIndex in _mapping_process_dpm_pg0 (mps_mapping.c).
 *
 * This is the MPS driver's twin of DF-1282 (the mpr driver's identical bug).
 * OBJECT-LEVEL proof of the primitive: the mps driver cannot attach on the
 * audit QEMU guest (no LSI/Avago SAS HBA present), so the vulnerable path is
 * not runtime-reachable here. This harness replicates the exact kernel struct
 * (dev_mapping_table) and the exact unguarded indexing from
 * _mapping_process_dpm_pg0 with an attacker/firmware-controlled DeviceIndex
 * to PROVE the OOB write primitive is real.
 *
 * Cited path: sys/dev/raid/mps/mps_mapping.c:1495 (dev_idx = le16toh(DeviceIndex))
 *             sys/dev/raid/mps/mps_mapping.c:1497 (IR path WITH bounds check)
 *             sys/dev/raid/mps/mps_mapping.c:1544 (Enc/Slot sink, NO check)
 *             sys/dev/raid/mps/mps_mapping.c:1545 (num_slots loop, NO check)
 *             sys/dev/raid/mps/mps_mapping.c:1567 (Device-Persistence sink, NO check)
 *
 * Build: cc -O2 -o harness harness.c
 * Run:   ./harness
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

/* Mirror of sys/dev/raid/mps/mpsvar.h dev_mapping_table */
struct dev_mapping_table {
    uint64_t physical_id;
    uint32_t device_info;
    uint32_t phy_bits;
    uint16_t dpm_entry_num;
    uint16_t dev_handle;
    uint16_t reserved1;
    uint16_t id;
    uint8_t  missing_count;
    uint8_t  init_complete;
    uint8_t  TLR_bits;
    uint8_t  reserved2;
};

#define MAX_DEVICES   264u   /* MaxTargets(256)+max_volumes(8) typical */
#define GUARD_N       64     /* guard entries after the allocation */

int main(void)
{
    struct dev_mapping_table *table =
        calloc(MAX_DEVICES + GUARD_N, sizeof(*table));
    if (!table) { perror("calloc"); return 1; }
    for (unsigned g = MAX_DEVICES; g < MAX_DEVICES + GUARD_N; g++)
        memset(&table[g], 0xAA, sizeof(table[g]));

    printf("[DF-1374] mapping_table allocated for max_devices=%u entries "
           "(sizeof(entry)=%zu)\n", MAX_DEVICES, sizeof(*table));

    /* Replicate the Enc/Slot path of _mapping_process_dpm_pg0.
     * dev_idx is firmware-controlled (mps_mapping.c:1495). The IR path
     * (:1497) bounds-checks it; the Enc/Slot (:1544) and Device-Persistence
     * (:1567) paths do NOT. num_slots is firmware-controlled too. */
    uint32_t dev_idx   = MAX_DEVICES + 2;   /* firmware DeviceIndex >= max_devices */
    uint16_t num_slots = 4;
    uint32_t map_idx;
    struct dev_mapping_table *mt_entry;

    printf("[DF-1374] attacker/firmware DeviceIndex=%u, num_slots=%u\n",
           dev_idx, num_slots);

    if (dev_idx >= MAX_DEVICES) {
        printf("[DF-1374] BUG CONFIRMED: dev_idx=%u >= max_devices=%u -> "
               "write at mapping_table[%u] is %zu bytes PAST the allocation "
               "(into the adjacent slab object)\n",
               dev_idx, MAX_DEVICES, dev_idx,
               (size_t)(dev_idx - MAX_DEVICES) * sizeof(*table));
    }

    /* Execute the unguarded loop exactly as mps_mapping.c:1544-1563 */
    mt_entry = &table[dev_idx];                 /* :1544 OOB pointer */
    for (map_idx = dev_idx; map_idx < (dev_idx + num_slots);
         map_idx++, mt_entry++) {               /* :1545 */
        mt_entry->physical_id   = 0x4141414141414141ULL;
        mt_entry->phy_bits      = 0x42424242;
        mt_entry->id            = dev_idx;
        mt_entry->dpm_entry_num = 1;
        mt_entry->missing_count = 0;
        mt_entry->device_info   = 0x43434343;   /* MPS_DEV_RESERVED stand-in */
    }

    unsigned hits = 0;
    for (unsigned g = MAX_DEVICES; g < MAX_DEVICES + GUARD_N; g++) {
        uint8_t *p = (uint8_t *)&table[g], *end = p + sizeof(table[g]);
        for (; p < end; p++)
            if (*p != 0xAA) { hits++; break; }
    }
    printf("[DF-1374] guard/slab region corrupted: %s (%u of %u entries "
           "touched)\n", hits ? "YES -> next heap object(s) overwritten"
                               : "no", hits, GUARD_N);

    if (dev_idx >= MAX_DEVICES ||
        dev_idx + num_slots > MAX_DEVICES) {
        printf("[DF-1374] FIX: with bounds check, write SKIPPED "
               "(dev_idx=%u >= max_devices=%u) -> no corruption\n",
               dev_idx, MAX_DEVICES);
    }

    free(table);
    return hits ? 0 : 2;
}
