DF-1029 / poc.c
/* * 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; } |