/*
 * DF-1018 harness — dastart TRIM unbounded ranges -> heap overflow + OOB DMA
 *
 * BUG: In dastart() at sys/bus/cam/scsi/scsi_da.c:1345-1360, the inner
 * while(count>0) loop writes req->data[ranges*8+0..7] incrementing
 * 'ranges' with NO bound against TRIM_MAX_RANGES (512). 'count' comes
 * from bp->b_bcount / softc->params.secsize, where secsize is device-
 * controlled (READ_CAPACITY). If secsize=1 and b_bcount=0x7FFF8000
 * (the max set by DAIOCTRIM at :463), count=2147418112, producing
 * ~32768 ranges into data[4096] -> ~258KB heap overflow past the
 * trim_request.data[] array into da_softc (bios[], etc.).
 *
 * Additionally, cam_fill_csio at :1378 sets dxfer_len =
 * ((ranges+63)/64)*512 = ~262KB with data_ptr=req->data (only 4096
 * valid), causing OOB DMA read of ~258KB kernel heap sent to the
 * device (info leak).
 *
 * The merge check at :1365-1367 only bounds SUBSEQUENT bios, not the
 * FIRST bio which is processed unconditionally.
 *
 * This harness demonstrates the overflow arithmetic. Runtime trigger
 * requires a SCSI Direct Access device with secsize=1 (or small) and
 * a TRIM request — this QEMU guest has only a DVD-ROM (no da device).
 *
 * Build: cc -o harness harness.c
 * Run:   ./harness
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#define TRIM_MAX_BLOCKS 8
#define TRIM_MAX_RANGES (TRIM_MAX_BLOCKS * 64)  /* 512 */

int main(void)
{
    /* Simulate trim_request.data[] — 4096 bytes = 512 * 8 */
    uint8_t data[TRIM_MAX_RANGES * 8];  /* 4096 bytes */
    uint32_t data_len = sizeof(data);

    /* Simulate a malicious device with secsize=1 */
    uint32_t secsize = 1;   /* device-controlled, no validation */

    /* DAIOCTRIM max b_bcount = 0x7FFF8000 (scsi_da.c:463) */
    uint32_t b_bcount = 0x7FFF8000;

    /* dastart:1341 — count = b_bcount / secsize */
    uint32_t count = b_bcount / secsize;

    printf("DF-1018: TRIM heap overflow arithmetic\n");
    printf("  TRIM_MAX_RANGES: %d (data array = %u bytes)\n",
           TRIM_MAX_RANGES, data_len);
    printf("  secsize (device-controlled): %u\n", secsize);
    printf("  b_bcount (DAIOCTRIM max):    0x%X\n", b_bcount);
    printf("  count = b_bcount/secsize:    %u\n", count);

    /* Simulate the inner while(count>0) loop */
    int ranges = 0;
    while (count > 0) {
        int c = count > 0xffff ? 0xffff : count;
        int off = ranges * 8;

        if (off >= (int)data_len) {
            /* OVERFLOW: writing past data[4096] */
            if (ranges == TRIM_MAX_RANGES)
                printf("\n  *** OVERFLOW STARTS HERE: ranges=%d, off=%d >= data_len=%u ***\n",
                       ranges, off, data_len);
        }

        count -= c;
        ranges++;
    }

    uint32_t overflow_bytes = (ranges * 8) - data_len;
    uint32_t dxfer_len = ((ranges + 63) / 64) * 512;

    printf("  Total ranges written:    %d (into array of %d)\n",
           ranges, TRIM_MAX_RANGES);
    printf("  Total bytes written:     %d\n", ranges * 8);
    printf("  Heap overflow:           %u bytes past data[]\n", overflow_bytes);
    printf("  OOB DMA dxfer_len:       %u bytes (cam_fill_csio :1378)\n", dxfer_len);
    printf("  OOB DMA info leak:       %u bytes of kernel heap sent to device\n",
           dxfer_len - data_len);

    printf("\n  BUG CONFIRMED: %d ranges written into data[%d] = %u byte overflow.\n",
           ranges, TRIM_MAX_RANGES, overflow_bytes);
    printf("  The first bio is processed unconditionally (merge check at :1365\n");
    printf("  only bounds subsequent bios). secsize=1 makes count enormous.\n");
    return 0;
}
