DF-4502 / trigger_DF4502.c
/* * DF-4502 trigger (post-condition half). Self-contained: shells out to * camcontrol(8), which drives the same CAM code path. * * Bug: ahci_pm_softreset() leaks the error-CCB reservation when the SECOND * SRST FIS poll fails on the FINAL retry (count exhausted): it jumps to * err: without calling ahci_put_err_ccb(). AP_F_ERR_CCB_RESERVED stays set * forever and ap->ap_active/ap_sactive/ap_active_cnt stay zeroed (their * saved values are only restored by ahci_put_err_ccb). * * The *priming* half (two consecutive second-FIS softreset failures on a * target behind a port multiplier) is device-driven and cannot be forced * from userland; it happens in the field with flaky PM-attached devices * (the code even anticipates it: "Soft-reset(2) through PM failed"). * This program is the *detonation* half: once the reservation is leaked, * any port-level reset or probe re-enters ahci_get_err_ccb() and hits * * KKASSERT((ap->ap_flags & AP_F_ERR_CCB_RESERVED) == 0); ahci.c:3352 * * which panics an INVARIANTS kernel (X86_64_GENERIC has INVARIANTS, * sys/config/X86_64_GENERIC:56). * * Re-entry path driven by `camcontrol rescan <bus>`: * - ahci_xpt_action() XPT_PATH_INQ with CAM_TARGET_WILDCARD * -> ahci_port_state_machine() ahci_cam.c:920-923 * -> ahci_port_reset(ap, NULL, 1) ahci.c:818-819 * -> ahci_port_hardreset * -> ahci_pm_port_probe() ahci.c:1710 * -> ahci_get_err_ccb() ahci_pm.c:188 -> ahci.c:3352 <-- panic * * Usage: ./trigger_DF4502 [bus] (default 2; run as root, CAM is * root-only). Loops `camcontrol rescan <bus>` 64 times. * * Expected on a vulnerable kernel WITH a leaked reservation: * panic: assertion "(ap->ap_flags & AP_F_ERR_CCB_RESERVED) == 0" failed * On this audit guest there is no AHCI controller at all (PIIX3 IDE + * virtio only, see env.txt), so this trigger is compile/run-verified only * (it runs, the CAM bus just has no ahci ports behind it). */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char **argv) { char cmd[64]; int i, rc = 0; const char *bus = (argc > 1) ? argv[1] : "2"; for (i = 0; i < 64; i++) { snprintf(cmd, sizeof(cmd), "/sbin/camcontrol rescan %s", bus); rc = system(cmd); if (rc != 0) { fprintf(stderr, "camcontrol round %d rc=%d\n", i, rc); break; } usleep(50000); } printf("done: %d camcontrol rescan rounds on bus %s (rc=%d)\n", i, bus, rc); return (rc == 0 ? 0 : 1); } |