/*
 * 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;
}
