DF-4053 / sense_test.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | /* * DF-4053 trigger โ virtio_scsi.c sense memcpy dst/src swap. * * Sends an out-of-range READ(10) to the vtscsi0 da0/pass0 device so the * target returns CHECK CONDITION with a known sense (ILLEGAL REQUEST, * ASC 0x21 "LOGICAL BLOCK ADDRESS OUT OF RANGE"). * * On a correct driver the CCB comes back with CAM_AUTOSNS_VALID and * sense_data = 70 00 05 00 ... 21 00 ... * On the buggy DragonFly driver (virtio_scsi.c:1392-1394 memcpy(dst,src) * swapped vs FreeBSD), sense_data is the bzero()ed buffer: all zeros. * * argv[1] = csio.sense_len (default 255) โ demonstrates that a pass(4) * CAMIOCOMMAND CCB carries an UNVALIDATED sense_len (u_int8_t, <=255) * straight into the driver's length math: * copy_len = csio->sense_len - sense_resid (== device sense_len * when device sense_len < ccb sense_len) * With a malicious virtio-scsi backend returning sense_len >= 97 this * copy overflows vsr_uresp.cmd.sense[96] in struct vtscsi_request. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <bus/cam/cam.h> #include <bus/cam/cam_ccb.h> #include <bus/cam/scsi/scsi_message.h> #include <camlib.h> static void hexdump(const unsigned char *p, int len) { int i; for (i = 0; i < len; i++) { if (i % 16 == 0) printf(" %02x:", i); printf(" %02x", p[i]); if (i % 16 == 15) printf("\n"); } if (len % 16) printf("\n"); } int main(int argc, char **argv) { struct cam_device *dev; union ccb ccb; unsigned char data[512]; int sense_len = (argc > 1) ? atoi(argv[1]) : 255; int i; dev = cam_open_device("/dev/pass0", O_RDWR); if (dev == NULL) { fprintf(stderr, "cam_open_device: %s\n", cam_errbuf); return (1); } memset(&ccb, 0, sizeof(ccb)); memset(data, 0, sizeof(data)); /* READ(10) LBA=0xfffffff0 (out of range on a 64MB disk), 1 block */ ccb.ccb_h.func_code = XPT_SCSI_IO; ccb.ccb_h.path_id = dev->path_id; ccb.ccb_h.target_id = dev->target_id; ccb.ccb_h.target_lun = dev->target_lun; ccb.ccb_h.timeout = 5000; ccb.ccb_h.flags = CAM_DIR_IN; ccb.csio.tag_action = MSG_SIMPLE_Q_TAG; ccb.csio.cdb_len = 10; ccb.csio.data_ptr = data; ccb.csio.dxfer_len = sizeof(data); ccb.csio.sense_len = (sense_len > 0 && sense_len <= 255) ? sense_len : 255; ccb.csio.cdb_io.cdb_bytes[0] = 0x28; /* READ(10) */ ccb.csio.cdb_io.cdb_bytes[2] = 0xff; ccb.csio.cdb_io.cdb_bytes[3] = 0xff; ccb.csio.cdb_io.cdb_bytes[4] = 0xff; ccb.csio.cdb_io.cdb_bytes[5] = 0xf0; ccb.csio.cdb_io.cdb_bytes[8] = 0x01; printf("sending out-of-range READ(10) with csio.sense_len=%u\n", ccb.csio.sense_len); if (cam_send_ccb(dev, &ccb) < 0) { perror("cam_send_ccb"); return (1); } printf("ccb_h.status = 0x%02x%s%s\n", ccb.ccb_h.status, (ccb.ccb_h.status & CAM_AUTOSNS_VALID) ? " CAM_AUTOSNS_VALID" : "", (ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR ? " CAM_SCSI_STATUS_ERROR" : ""); printf("csio.scsi_status = 0x%02x (0x02 = CHECK CONDITION)\n", ccb.csio.scsi_status); printf("csio.sense_resid = %u\n", ccb.csio.sense_resid); printf("csio.sense_data[0..%d]:\n", ccb.csio.sense_len > 32 ? 31 : ccb.csio.sense_len - 1); hexdump((unsigned char *)&ccb.csio.sense_data, ccb.csio.sense_len > 32 ? 32 : ccb.csio.sense_len); /* * A valid SCSI sense always starts with error code 0x70 (current) * or 0x71 (deferred). Byte 2 high nibble = sense key (5 = ILLEGAL * REQUEST), byte 12 = ASC (0x21 = LBA out of range). */ { unsigned char *s = (unsigned char *)&ccb.csio.sense_data; int bad = (s[0] != 0x70 && s[0] != 0x71); printf("VERDICT: sense_data is %s (%s)\n", bad ? "ZEROED/EMPTY โ memcpy direction is SWAPPED" : "REAL", bad ? "bug reproduced" : "driver delivered autosense"); if (!bad) printf(" sense key=0x%02x ASC=0x%02x ASCQ=0x%02x\n", s[2] & 0x0f, s[12], s[13]); } cam_close_device(dev); return (0); } |