/*
 * DF-1356 harness: heap overflow in AMR_CMD_PASS ioctl via unchecked
 * user-controlled CDB length (amr.c amr_ioctl).
 *
 * OBJECT-LEVEL proof of the primitive. The amr (LSI MegaRAID) driver does
 * not attach on the audit QEMU guest (no MegaRAID HBA present), and even on
 * a host with the controller /dev/amrN is created UID_ROOT GID_OPERATOR mode
 * 0600 (S_IRUSR|S_IWUSR only), so this is a root->kernel path. The vulnerable
 * code path is therefore not runtime-reachable here. This harness replicates
 * the EXACT kernel struct layout (union amr_ccb / struct amr_passthrough) and
 * the exact unchecked bcopy from amr_ioctl to PROVE the heap-overflow
 * primitive is real.
 *
 * Cited path: sys/dev/raid/amr/amr.c:832  (len = au_cmd[2];   user u8 0..255)
 *             sys/dev/raid/amr/amr.c:833  (_ap->ap_cdb_length = len;)
 *             sys/dev/raid/amr/amr.c:834  (bcopy(au_cmd+3, _ap->ap_cdb, len);)
 *   struct:  sys/dev/raid/amr/amrreg.h:489 (ap_cdb[AMR_MAX_CDB_LEN=10])
 *            sys/dev/raid/amr/amrreg.h:90  (AMR_MAX_CDB_LEN 0x0a)
 *            sys/dev/raid/amr/amrvar.h:110 (union amr_ccb { ...; uint8_t bytes[128]; })
 *   perms:   sys/dev/raid/amr/amr.c:261    (make_dev UID_ROOT GID_OPERATOR S_IRUSR|S_IWUSR)
 *
 * Build: cc -O2 -o harness harness.c
 * Run:   ./harness
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>

#define AMR_MAX_CDB_LEN    10     /* amrreg.h:90  0x0a */
#define AMR_MAX_REQ_SENSE  32     /* amrreg.h:92  0x20 */

/* Faithful copy of struct amr_passthrough (amrreg.h:478), __packed. */
struct amr_passthrough {
    uint8_t  ap_timeout_etc;            /* bitfields: timeout:3,ars:1,dummy:3,islogical:1 */
    uint8_t  ap_logical_drive_no;
    uint8_t  ap_channel;
    uint8_t  ap_scsi_id;
    uint8_t  ap_queue_tag;
    uint8_t  ap_queue_action;
    uint8_t  ap_cdb[AMR_MAX_CDB_LEN];   /* offset 6, 10 bytes            */
    uint8_t  ap_cdb_length;
    uint8_t  ap_request_sense_length;
    uint8_t  ap_request_sense_area[AMR_MAX_REQ_SENSE];
    uint8_t  ap_no_sg_elements;
    uint8_t  ap_scsi_status;
    uint8_t  pad[2];                    /* __packed keeps u32 aligned naturally */
    uint32_t ap_data_transfer_address;
    uint32_t ap_data_transfer_length;
} __attribute__((packed));

/* Faithful copy of union amr_ccb (amrvar.h:110): 128-byte DMA-coherent alloc. */
#define AMR_CCB_SIZE 128
union amr_ccb {
    struct amr_passthrough ccb_pthru;
    uint8_t                bytes[AMR_CCB_SIZE];
};

int main(void)
{
    /* In the kernel, ac->ac_ccb points at one 128-byte DMA-coherent ccb out
     * of a cluster of such ccbs. Overflowing ap_cdb corrupts the fields after
     * it (sense area, data transfer addr/len) and, for len large enough, runs
     * past the 128-byte ccb into the adjacent ccb / DMA memory. We model that
     * with two adjacent ccbs + a guard region of canaries. */
    uint8_t *region = calloc(1, AMR_CCB_SIZE * 2 + 64); /* ccb0 | ccb1 | guard */
    if (!region) { perror("calloc"); return 1; }
    union amr_ccb *ccb0 = (union amr_ccb *)(region);
    union amr_ccb *ccb1 = (union amr_ccb *)(region + AMR_CCB_SIZE);
    uint8_t *guard = region + AMR_CCB_SIZE * 2;
    memset(guard, 0xAA, 64);

    /* attacker au_cmd buffer: byte[2] = len (the CDB length), bytes[3..] = CDB
     * payload. amr.c:832 len = au_cmd[2];  amr.c:834 bcopy(au_cmd+3, ap_cdb, len) */
    uint8_t au_cmd[256 + 8];
    memset(au_cmd, 0x41, sizeof(au_cmd));   /* attacker-controlled content */

    printf("[DF-1356] union amr_ccb = %d bytes; ap_cdb at offset %zu, len=%d\n",
           AMR_CCB_SIZE, offsetof(struct amr_passthrough, ap_cdb),
           AMR_MAX_CDB_LEN);
    printf("[DF-1356] ap_passthrough packed size = %zu bytes\n",
           sizeof(struct amr_passthrough));

    /* ===== Trigger the bug exactly as amr.c:825-834 ===== */
    struct amr_passthrough *_ap = &ccb0->ccb_pthru;
    bzero(_ap, sizeof(*_ap));

    int len = au_cmd[2] = 255;              /* user u8, far beyond AMR_MAX_CDB_LEN */
    _ap->ap_cdb_length = len;
    bcopy(au_cmd + 3, _ap->ap_cdb, len);    /* amr.c:834 — the overflow        */

    printf("[DF-1356] attacker len=%u (au_cmd[2]); AMR_MAX_CDB_LEN=%d -> "
           "%d bytes overflow past ap_cdb\n", len, AMR_MAX_CDB_LEN,
           len - AMR_MAX_CDB_LEN);

    /* What got corrupted inside ccb0 (past ap_cdb)? */
    printf("[DF-1356] ccb0.ap_request_sense_length now = 0x%02x (attacker byte)\n",
           _ap->ap_request_sense_length);
    printf("[DF-1356] ccb0.ap_data_transfer_address now = 0x%08x "
           "(DMA ptr corrupted)\n", _ap->ap_data_transfer_address);
    printf("[DF-1356] ccb0.ap_data_transfer_length now = 0x%08x\n",
           _ap->ap_data_transfer_length);

    /* Did the 255-byte bcopy run past the 60-byte passthrough struct / the
     * 128-byte ccb union into ccb1 and the guard? */
    unsigned overflow_past_union = 0;
    for (unsigned o = sizeof(struct amr_passthrough); o < AMR_CCB_SIZE; o++)
        if (ccb0->bytes[o] == 0x41) overflow_past_union++;
    unsigned ccb1_hits = 0, guard_hits = 0;
    for (unsigned o = 0; o < AMR_CCB_SIZE; o++)
        if (ccb1->bytes[o] == 0x41) ccb1_hits++;
    for (unsigned o = 0; o < 64; o++)
        if (guard[o] != 0xAA) guard_hits++;

    printf("[DF-1356] bytes written past ap_passthrough but inside ccb0: %u\n",
           overflow_past_union);
    printf("[DF-1356] ADJACENT ccb1 corrupted: %s (%u attacker bytes landed in it)\n",
           ccb1_hits ? "YES -> next DMA ccb overwritten" : "no", ccb1_hits);
    printf("[DF-1356] guard region past ccb1 corrupted: %s (%u bytes)\n",
           guard_hits ? "YES" : "no", guard_hits);

    /* ===== Show the fix ===== */
    printf("[DF-1356] FIX: with 'if (len > AMR_MAX_CDB_LEN) len = AMR_MAX_CDB_LEN;' "
           "before the bcopy, write stays inside ap_cdb[10] -> no overflow\n");

    int bad = (overflow_past_union > 0 || ccb1_hits > 0 || guard_hits > 0);
    free(region);
    return bad ? 0 : 2;   /* exit 0 == primitive demonstrated */
}
