/*
 * DF-3911 trigger/analysis — unvalidated IOC-authored firmware image walk in
 * mpt_download_fw() (sys/dev/disk/mpt/mpt.c:2467-2485)
 *
 * Kernel path (hardware-gated: LSI MPT Fusion IOC — malicious or faulty):
 *   When IOCFACTS sets MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT, mpt_upload_fw()
 *   lets the IOC DMA an "image" of ioc_facts.FWImageSize bytes into
 *   mpt->fw_image (mpt.c:2751-2784).  Every field of MpiFwHeader_t and
 *   MpiExtImageHeader_t is therefore IOC-controlled.  After a hard reset,
 *   mpt_download_fw() walks it with zero validation:
 *     - mpt_diag_outsl(mpt, fw_hdr->LoadStartAddress, (uint32_t*)fw_hdr,
 *                      fw_hdr->ImageSize)     [mpt.c:2469]
 *       ImageSize is NOT checked against mpt->fw_image_size -> reads up to
 *       4 GiB past the DMA buffer, streaming kernel memory to the PCI diag
 *       data port until an unmapped page faults -> panic.
 *     - ext = (MpiExtImageHeader_t *)((uintptr_t)fw_hdr + ext_offset)
 *       with ext_offset = fw_hdr/ext->NextImageHeaderOffset unvalidated
 *       [mpt.c:2474-2482] -> wild pointer reads at attacker-chosen offsets
 *       (including loops that never terminate).
 *
 * This harness reproduces the pointer arithmetic in userspace with a
 * malicious ext_offset and ImageSize: it segfaults on the wild read, the
 * userspace analogue of the kernel page fault.
 */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

typedef struct { uint32_t ImageSize, NextImageHeaderOffset, LoadStartAddress; } Hdr;

int main(void)
{
    uint8_t  buf[4096];              /* stands in for mpt->fw_image */
    uint32_t fw_image_size = 4096;
    Hdr     *fw_hdr = (Hdr *)buf;

    /* IOC authors: huge ImageSize, wild next-image offset */
    fw_hdr->ImageSize           = 0x40000000; /* 1 GiB >> 4096 allocated */
    fw_hdr->NextImageHeaderOffset = 0xfffff000;

    printf("fw_image_size=%u, IOC ImageSize=%u, IOC NextImageHeaderOffset=%#x\n",
           fw_image_size, fw_hdr->ImageSize, fw_hdr->NextImageHeaderOffset);

    if (fw_hdr->ImageSize > fw_image_size)
        printf("UNFIXED KERNEL: walks %u bytes from a %u-byte buffer -> OOB read\n",
               fw_hdr->ImageSize, fw_image_size);

    /* the unfixed walk, verbatim shape: */
    Hdr *ext = (Hdr *)((uintptr_t)fw_hdr + fw_hdr->NextImageHeaderOffset);
    printf("reading ext header at fw_image+%#x ...\n", fw_hdr->NextImageHeaderOffset);
    fflush(stdout);
    printf("ext->NextImageHeaderOffset=%#x\n", ext->NextImageHeaderOffset); /* wild read -> SIGSEGV */
    return 0;
}
