/*
 * DF-1029 — OOB heap read via unchecked blk_desc_len in MODE SENSE
 * parsing (scsi_ch auto-probe path).
 *
 * Bug location: sys/bus/cam/scsi/scsi_all.h:1418 (find_mode_page_6)
 *               sys/bus/cam/scsi/scsi_ch.c:494-498, 546-547, 1409-1419, 1474-1484
 *
 * find_mode_page_6() at scsi_all.h:1418 is:
 *
 *     page_start = (void *)((u_int8_t *)&mode_header[1] +
 *                           mode_header->blk_desc_len);
 *
 * where blk_desc_len is a u_int8_t at struct offset 3 of the MODE SENSE
 * response, i.e. fully device-controlled.  chstart (scsi_ch.c:494)
 * allocates only:
 *
 *     mode_buffer_len = sizeof(struct scsi_mode_header_6) +   // 4
 *                       sizeof(struct scsi_mode_blk_desc)  +   // 8
 *                       sizeof(struct page_element_address_assignment);  // 20
 *                   // total 32 bytes
 *
 * If the device lies and returns blk_desc_len = 0xFF (255),
 * find_mode_page_6 returns mode_buffer + 4 + 255 = mode_buffer + 259,
 * ~227 bytes past the 32-byte allocation.  chdone (line 546-547),
 * chgetparams (line 1409-1419 reads 16 bytes from ea->mtea/nmte/etc and
 * line 1474-1484 reads cap->move_from/exchange_with) then dereference
 * this OOB pointer, reading kernel heap into softc state.  Some of that
 * softc state is then exposed to userspace via CHIOGPARAMS.
 *
 * Reachability on the audit guest:
 *   - Same as DF-1028: scsi_ch is part of cam.ko but only attaches to
 *     SCSI changer devices (type 8). The QEMU DVD-ROM is a CD-ROM
 *     (type 5), so no ch peripheral attaches and chstart/chdone/chgetparams
 *     never run. The bug is latent.
 *
 * This harness documents the trigger and the fix.
 */
#include <stdio.h>

int main(void)
{
    printf("=== DF-1029 trigger documentation ===\n");
    printf("\nVulnerable path (auto-triggered at SCSI changer probe):\n");
    printf("  chregister -> chstart CH_STATE_PROBE (scsi_ch.c:485)\n");
    printf("    scsi_mode_sense(..., mode_buffer, mode_buffer_len=32, ...)\n");
    printf("    // device replies; mode_buffer[3] = blk_desc_len (device-controlled)\n");
    printf("  chdone -> find_mode_page_6(mode_header)        // scsi_ch.c:546\n");
    printf("    page_start = &mode_header[1] + mode_header->blk_desc_len\n");
    printf("    // if blk_desc_len==0xFF, page_start = mode_buffer+259,\n");
    printf("    //   227 bytes past the 32-byte allocation\n");
    printf("  chgetparams -> find_mode_page_6(...) twice      // scsi_ch.c:1409,1474\n");
    printf("    softc->sc_firsts/counts/movemask/exchangemask\n");
    printf("    // populated from OOB-read bytes; partially exposed via CHIOGPARAMS\n");
    printf("\nTrigger precondition: a SCSI changer device whose MODE SENSE\n");
    printf("  response lies about blk_desc_len. Auto-triggers at probe;\n");
    printf("  no user interaction needed once the device attaches.\n");
    printf("\nStatus on this audit guest (only QEMU DVD-ROM, no SCSI\n");
    printf("  changer, no /dev/ch0): NOT REACHABLE. Bug confirmed by code\n");
    printf("  trace at sys/bus/cam/scsi/scsi_ch.c:494-498, 546-547,\n");
    printf("  1409-1419, 1474-1484 and scsi_all.h:1418. Fix: validate\n");
    printf("  mode_header->blk_desc_len against the buffer size before\n");
    printf("  calling find_mode_page_6, or return early on a too-large\n");
    printf("  value. See fix.diff and VERDICT.md.\n");
    return 0;
}
