/*
 * DF-1918 source-confirmation harness — mrsas_passthru unbounded sense_off
 * yields an OOB pointer-sized (8-byte) write past the 1024-byte cmd->frame.
 *
 * sys/dev/raid/mrsas/mrsas_ioctl.c:291-293
 *   sense_ptr = (unsigned long *)
 *       ((unsigned long)cmd->frame + user_ioc->sense_off);
 *   *sense_ptr = ioctl_sense_phys_addr;
 *
 * user_ioc->sense_off is a u_int32_t (mrsas_ioctl.h:85) taken verbatim
 * from userspace and NEVER validated.  The store writes 8 bytes
 * (sizeof(unsigned long) on amd64) of a kernel physical DMA address at
 * offset sense_off from cmd->frame.  cmd->frame is bus_dmamem_alloc'd at
 * MRSAS_MFI_FRAME_SIZE=1024 bytes (mrsas.h:876, mrsas.c:451-462).  With
 * sense_off>=1016 (1024 - sizeof(unsigned long)) the 8-byte store spills
 * past the allocation.  sense_off=0xFFFFFF F8 wraps to (cmd->frame - 8).
 *
 * Pre-condition: mrsas_ioctl.c:267 `if (user_ioc->sense_len)` gates the
 * whole block, so the attacker must also set sense_len>0 — trivial.
 *
 * The primitive is more powerful than DF-1917 in one sense (the offset is
 * arbitrary, no SGE-count multiplier), less powerful in another (the
 * written VALUE is a fixed kernel physical address the attacker cannot
 * choose, only the OFFSET).  Combined with an info leak of the sense
 * buffer physical address it becomes a write-what-where primitive; absent
 * that, it is an arbitrary-offset 8-byte write of a low-entropy value.
 *
 * Why a harness: same Phase-6 blocker as DF-1917 — no LSI MegaRAID SAS
 * HBA in the QEMU audit guest (verified: pciconf -lv lists none;
 * /dev/mrsas* absent), so mrsas_passthru is unreachable at runtime, and
 * /dev/mrsas0 would be 0660 root:operator (mrsas.c:790) which maxx
 * (uid 1001, not in operator) cannot open anyway.
 *
 * Build:  cc -O2 -o harness harness.c
 * Run:    ./harness
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#define MRSAS_MFI_FRAME_SIZE 1024   /* mrsas.h:876 */

/* Verbatim mrsas_ioctl.c:291-293 store against a 1024-byte cmd->frame
 * model.  Returns the number of bytes written past the end. */
static unsigned int spill_bytes(const uint32_t sense_off)
{
    unsigned char *frame = calloc(1, MRSAS_MFI_FRAME_SIZE + 16);
    unsigned char *base  = frame;
    unsigned long  phys  = 0xDEADBEEFul;   /* model ioctl_sense_phys_addr */

    /* sense_ptr = (unsigned long *)((unsigned long)cmd->frame + sense_off) */
    unsigned long *sense_ptr = (unsigned long *)(base + sense_off);
    /* *sense_ptr = ioctl_sense_phys_addr;  -- the unchecked store */
    unsigned char *p   = (unsigned char *)sense_ptr;
    unsigned int  oob  = 0;
    for (unsigned int b = 0; b < sizeof(unsigned long); b++)
        if (p - base + b >= MRSAS_MFI_FRAME_SIZE) oob++;
    *sense_ptr = phys;     /* exactly what the kernel does */
    free(frame);
    return oob;
}

static void case_(const char *label, uint32_t sense_off)
{
    unsigned int oob = spill_bytes(sense_off);
    printf("  sense_off=0x%08x -> %u/8 bytes past 1024-byte frame %s\n",
           sense_off, oob, oob ? "(OOB WRITE)" : "(in-bounds)");
    (void)label;
}

int main(void)
{
    printf("DF-1918: mrsas_passthru unbounded sense_off (mrsas_ioctl.c:291-293)\n");
    printf("  *sense_ptr = ioctl_sense_phys_addr writes sizeof(unsigned long)\n");
    printf("  = 8 bytes at cmd->frame + sense_off.  sense_off is u32 from\n");
    printf("  userspace (mrsas_ioctl.h:85), never validated.\n\n");

    case_("in-bounds",    128u);
    case_("edge",         MRSAS_MFI_FRAME_SIZE - (unsigned)sizeof(unsigned long)); /* fully in-bounds */
    case_("oob-7",        MRSAS_MFI_FRAME_SIZE - 7u);   /* 7 of 8 bytes OOB */
    case_("oob-full",     MRSAS_MFI_FRAME_SIZE);        /* all 8 bytes OOB */
    case_("oob-deep",     MRSAS_MFI_FRAME_SIZE + 4096u);/* 1 page past */
    case_("wrap",         0xFFFFFFF8u);                 /* cmd->frame - 8 */

    printf("\n  Confirmed: sense_off >= 1017 spills 1..8 bytes; sense_off\n");
    printf("  >= 1024 spills entirely; 0xFFFFFFF8 wraps to cmd->frame - 8.\n");
    return 0;
}
