/*
 * DF-1326 / DF-1359 userspace structural harness
 *
 * The live trigger requires /dev/mprN or /dev/mpsN (LSI SAS HBA PCI device),
 * which QEMU does not emulate, so the in-kernel primitive cannot be exercised
 * on the audit guest.  This harness reproduces the *primitive* in userspace
 * by laying out memory the way mpr_user_pass_thru / mps_user_pass_thru do:
 *
 *   MPI2_REQUEST_HEADER tmphdr;   // 12 bytes on the function's stack
 *   copyin(user_ptr, &tmphdr, data->RequestSize);  // user-controlled size
 *
 * It proves that any RequestSize > 12 writes attacker-controlled bytes past
 * the 12-byte header (smashing whatever follows tmphdr on the stack), with
 * full byte-for-byte attacker control of the overflow.
 *
 * Build:  cc -O2 -Wall -o harness_overflow harness_overflow.c
 * Run:    ./harness_overflow
 * Expect: prints the overflow proof (12 in-bound bytes set, then N-MP past
 *         the buffer also set to attacker bytes) and exits 0.
 */

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

/* Verbatim from sys/dev/raid/mpr/mpi/mpi2.h:855 (12 bytes). */
typedef struct {
    uint16_t FunctionDependent1;   /* 0x00 */
    uint8_t  ChainOffset;          /* 0x02 */
    uint8_t  Function;             /* 0x03 */
    uint16_t FunctionDependent2;   /* 0x04 */
    uint8_t  FunctionDependent3;   /* 0x06 */
    uint8_t  MsgFlags;             /* 0x07 */
    uint8_t  VP_ID;                /* 0x08 */
    uint8_t  VF_ID;                /* 0x09 */
    uint16_t Reserved1;            /* 0x0A */
} MPI2_REQUEST_HEADER;

#define HDR_SZ (int)sizeof(MPI2_REQUEST_HEADER)

int main(void)
{
    printf("sizeof(MPI2_REQUEST_HEADER) = %d\n", HDR_SZ);
    if (HDR_SZ != 12) {
        printf("UNEXPECTED header size (claim was 12); aborting proof.\n");
        return 1;
    }

    /* Simulate the kernel stack layout: tmphdr followed by whatever the
     * compiler placed next.  We allocate a "stack frame" 64 bytes wide and
     * place tmphdr at offset 0, then simulate the unchecked copyin. */
    enum { FRAME = 64 };
    unsigned char frame[FRAME];
    memset(frame, 0xCC, sizeof(frame));            /* pristine stack */
    MPI2_REQUEST_HEADER *tmphdr = (MPI2_REQUEST_HEADER *)frame;

    /* Attacker payload: 64 bytes, fully controlled, marked with a recognizable
     * pattern that distinguishes in-band from out-of-band writes. */
    unsigned char attacker[FRAME];
    for (int i = 0; i < FRAME; i++) attacker[i] = (unsigned char)(0x41 + (i % 26));

    /* The bug: copyin(ptr, &tmphdr, RequestSize) where RequestSize is
     * attacker-controlled.  In the live kernel the user passes RequestSize =
     * e.g. 1024 to smash the stack frame. */
    int RequestSize = FRAME;          /* user->RequestSize from ioctl */
    if (RequestSize > HDR_SZ) {
        printf("BUG: copyin %d bytes into %d-byte tmphdr overflows by %d bytes\n",
               RequestSize, HDR_SZ, RequestSize - HDR_SZ);
    }

    /* Simulate the unchecked copyin. */
    memcpy(tmphdr, attacker, RequestSize);

    /* Report. */
    printf("frame bytes after copyin (offset: value, source):\n");
    for (int i = 0; i < FRAME; i++) {
        const char *cls = (i < HDR_SZ) ? "in-band (header)"
                                       : "OUT-OF-BAND (stack smashed)";
        printf("  [%2d] 0x%02x  %s\n", i, frame[i], cls);
    }

    int oob = 0;
    for (int i = HDR_SZ; i < FRAME; i++)
        if (frame[i] == attacker[i]) oob++;
    printf("\nPrimitive confirmed: %d of %d out-of-band bytes are "
           "attacker-controlled.\n", oob, FRAME - HDR_SZ);
    printf("In the kernel these bytes overwrite saved RBP/RIP/locals on the "
           "function's stack frame.\n");
    return (oob == FRAME - HDR_SZ) ? 0 : 1;
}
