qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

scsi-generic: avoid invalid access to struct when emulating block limits

Emulation of the block limits VPD page called back into scsi-disk.c,
which however expected the request to be for a SCSIDiskState and
accessed a scsi-generic device outside the bounds of its struct
(namely to retrieve s->max_unmap_size and s->max_io_size).

To avoid this, move the emulation code to a separate function that
takes a new SCSIBlockLimits struct and marshals it into the VPD
response format.

Reported-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

+104 -84
+1 -1
hw/scsi/Makefile.objs
··· 1 - common-obj-y += scsi-disk.o 1 + common-obj-y += scsi-disk.o emulation.o 2 2 common-obj-y += scsi-generic.o scsi-bus.o 3 3 common-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o 4 4 common-obj-$(CONFIG_MPTSAS_SCSI_PCI) += mptsas.o mptconfig.o mptendian.o
+42
hw/scsi/emulation.c
··· 1 + #include "qemu/osdep.h" 2 + #include "qemu/units.h" 3 + #include "qemu/bswap.h" 4 + #include "hw/scsi/emulation.h" 5 + 6 + int scsi_emulate_block_limits(uint8_t *outbuf, const SCSIBlockLimits *bl) 7 + { 8 + /* required VPD size with unmap support */ 9 + memset(outbuf, 0, 0x3c); 10 + 11 + outbuf[0] = bl->wsnz; /* wsnz */ 12 + 13 + if (bl->max_io_sectors) { 14 + /* optimal transfer length granularity. This field and the optimal 15 + * transfer length can't be greater than maximum transfer length. 16 + */ 17 + stw_be_p(outbuf + 2, MIN(bl->min_io_size, bl->max_io_sectors)); 18 + 19 + /* maximum transfer length */ 20 + stl_be_p(outbuf + 4, bl->max_io_sectors); 21 + 22 + /* optimal transfer length */ 23 + stl_be_p(outbuf + 8, MIN(bl->opt_io_size, bl->max_io_sectors)); 24 + } else { 25 + stw_be_p(outbuf + 2, bl->min_io_size); 26 + stl_be_p(outbuf + 8, bl->opt_io_size); 27 + } 28 + 29 + /* max unmap LBA count */ 30 + stl_be_p(outbuf + 16, bl->max_unmap_sectors); 31 + 32 + /* max unmap descriptors */ 33 + stl_be_p(outbuf + 20, bl->max_unmap_descr); 34 + 35 + /* optimal unmap granularity; alignment is zero */ 36 + stl_be_p(outbuf + 24, bl->unmap_sectors); 37 + 38 + /* max write same size, make it the same as maximum transfer length */ 39 + stl_be_p(outbuf + 36, bl->max_io_sectors); 40 + 41 + return 0x3c; 42 + }
+20 -72
hw/scsi/scsi-disk.c
··· 33 33 #include "qapi/error.h" 34 34 #include "qemu/error-report.h" 35 35 #include "hw/scsi/scsi.h" 36 + #include "hw/scsi/emulation.h" 36 37 #include "scsi/constants.h" 37 38 #include "sysemu/sysemu.h" 38 39 #include "sysemu/block-backend.h" ··· 589 590 return (uint8_t *)r->iov.iov_base; 590 591 } 591 592 592 - int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf) 593 + static int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf) 593 594 { 594 595 SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); 595 596 uint8_t page_code = req->cmd.buf[2]; ··· 691 692 } 692 693 case 0xb0: /* block limits */ 693 694 { 694 - unsigned int unmap_sectors = 695 + SCSIBlockLimits bl = {}; 696 + 697 + if (s->qdev.type == TYPE_ROM) { 698 + DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n", 699 + page_code); 700 + return -1; 701 + } 702 + bl.wsnz = 1; 703 + bl.unmap_sectors = 695 704 s->qdev.conf.discard_granularity / s->qdev.blocksize; 696 - unsigned int min_io_size = 705 + bl.min_io_size = 697 706 s->qdev.conf.min_io_size / s->qdev.blocksize; 698 - unsigned int opt_io_size = 707 + bl.opt_io_size = 699 708 s->qdev.conf.opt_io_size / s->qdev.blocksize; 700 - unsigned int max_unmap_sectors = 709 + bl.max_unmap_sectors = 701 710 s->max_unmap_size / s->qdev.blocksize; 702 - unsigned int max_io_sectors = 711 + bl.max_io_sectors = 703 712 s->max_io_size / s->qdev.blocksize; 713 + /* 255 descriptors fit in 4 KiB with an 8-byte header */ 714 + bl.max_unmap_descr = 255; 704 715 705 - if (s->qdev.type == TYPE_ROM) { 706 - DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n", 707 - page_code); 708 - return -1; 709 - } 710 716 if (s->qdev.type == TYPE_DISK) { 711 717 int max_transfer_blk = blk_get_max_transfer(s->qdev.conf.blk); 712 718 int max_io_sectors_blk = 713 719 max_transfer_blk / s->qdev.blocksize; 714 720 715 - max_io_sectors = 716 - MIN_NON_ZERO(max_io_sectors_blk, max_io_sectors); 717 - 718 - /* min_io_size and opt_io_size can't be greater than 719 - * max_io_sectors */ 720 - if (min_io_size) { 721 - min_io_size = MIN(min_io_size, max_io_sectors); 722 - } 723 - if (opt_io_size) { 724 - opt_io_size = MIN(opt_io_size, max_io_sectors); 725 - } 721 + bl.max_io_sectors = 722 + MIN_NON_ZERO(max_io_sectors_blk, bl.max_io_sectors); 726 723 } 727 - /* required VPD size with unmap support */ 728 - buflen = 0x40; 729 - memset(outbuf + 4, 0, buflen - 4); 730 - 731 - outbuf[4] = 0x1; /* wsnz */ 732 - 733 - /* optimal transfer length granularity */ 734 - outbuf[6] = (min_io_size >> 8) & 0xff; 735 - outbuf[7] = min_io_size & 0xff; 736 - 737 - /* maximum transfer length */ 738 - outbuf[8] = (max_io_sectors >> 24) & 0xff; 739 - outbuf[9] = (max_io_sectors >> 16) & 0xff; 740 - outbuf[10] = (max_io_sectors >> 8) & 0xff; 741 - outbuf[11] = max_io_sectors & 0xff; 742 - 743 - /* optimal transfer length */ 744 - outbuf[12] = (opt_io_size >> 24) & 0xff; 745 - outbuf[13] = (opt_io_size >> 16) & 0xff; 746 - outbuf[14] = (opt_io_size >> 8) & 0xff; 747 - outbuf[15] = opt_io_size & 0xff; 748 - 749 - /* max unmap LBA count, default is 1GB */ 750 - outbuf[20] = (max_unmap_sectors >> 24) & 0xff; 751 - outbuf[21] = (max_unmap_sectors >> 16) & 0xff; 752 - outbuf[22] = (max_unmap_sectors >> 8) & 0xff; 753 - outbuf[23] = max_unmap_sectors & 0xff; 754 - 755 - /* max unmap descriptors, 255 fit in 4 kb with an 8-byte header */ 756 - outbuf[24] = 0; 757 - outbuf[25] = 0; 758 - outbuf[26] = 0; 759 - outbuf[27] = 255; 760 - 761 - /* optimal unmap granularity */ 762 - outbuf[28] = (unmap_sectors >> 24) & 0xff; 763 - outbuf[29] = (unmap_sectors >> 16) & 0xff; 764 - outbuf[30] = (unmap_sectors >> 8) & 0xff; 765 - outbuf[31] = unmap_sectors & 0xff; 766 - 767 - /* max write same size */ 768 - outbuf[36] = 0; 769 - outbuf[37] = 0; 770 - outbuf[38] = 0; 771 - outbuf[39] = 0; 772 - 773 - outbuf[40] = (max_io_sectors >> 24) & 0xff; 774 - outbuf[41] = (max_io_sectors >> 16) & 0xff; 775 - outbuf[42] = (max_io_sectors >> 8) & 0xff; 776 - outbuf[43] = max_io_sectors & 0xff; 724 + buflen += scsi_emulate_block_limits(outbuf + buflen, &bl); 777 725 break; 778 726 } 779 727 case 0xb1: /* block device characteristics */
+25 -10
hw/scsi/scsi-generic.c
··· 16 16 #include "qemu-common.h" 17 17 #include "qemu/error-report.h" 18 18 #include "hw/scsi/scsi.h" 19 + #include "hw/scsi/emulation.h" 19 20 #include "sysemu/block-backend.h" 20 21 21 22 #ifdef __linux__ ··· 181 182 /* Also take care of the opt xfer len. */ 182 183 stl_be_p(&r->buf[12], 183 184 MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12]))); 184 - } else if (page == 0x00 && s->needs_vpd_bl_emulation) { 185 + } else if (s->needs_vpd_bl_emulation && page == 0x00) { 185 186 /* 186 187 * Now we're capable of supplying the VPD Block Limits 187 188 * response if the hardware can't. Add it in the INQUIRY ··· 209 210 } 210 211 } 211 212 212 - static int scsi_emulate_block_limits(SCSIGenericReq *r) 213 + static int scsi_generic_emulate_block_limits(SCSIGenericReq *r, SCSIDevice *s) 213 214 { 214 - r->buflen = scsi_disk_emulate_vpd_page(&r->req, r->buf); 215 + int len; 216 + uint8_t buf[64]; 217 + 218 + SCSIBlockLimits bl = { 219 + .max_io_sectors = blk_get_max_transfer(s->conf.blk) / s->blocksize 220 + }; 221 + 222 + memset(r->buf, 0, r->buflen); 223 + stb_p(buf, s->type); 224 + stb_p(buf + 1, 0xb0); 225 + len = scsi_emulate_block_limits(buf + 4, &bl); 226 + assert(len <= sizeof(buf) - 4); 227 + stw_be_p(buf + 2, len); 228 + 229 + memcpy(r->buf, buf, MIN(r->buflen, len + 4)); 230 + 215 231 r->io_header.sb_len_wr = 0; 216 232 217 233 /* ··· 253 269 * resulted in sense error but would need emulation. 254 270 * In this case, emulate a valid VPD response. 255 271 */ 256 - if (s->needs_vpd_bl_emulation) { 257 - int is_vpd_bl = r->req.cmd.buf[0] == INQUIRY && 258 - r->req.cmd.buf[1] & 0x01 && 259 - r->req.cmd.buf[2] == 0xb0; 260 - 261 - if (is_vpd_bl && sg_io_sense_from_errno(-ret, &r->io_header, &sense)) { 262 - len = scsi_emulate_block_limits(r); 272 + if (s->needs_vpd_bl_emulation && 273 + r->req.cmd.buf[0] == INQUIRY && 274 + (r->req.cmd.buf[1] & 0x01) && 275 + r->req.cmd.buf[2] == 0xb0) { 276 + if (sg_io_sense_from_errno(-ret, &r->io_header, &sense)) { 277 + len = scsi_generic_emulate_block_limits(r, s); 263 278 /* 264 279 * No need to let scsi_read_complete go on and handle an 265 280 * INQUIRY VPD BL request we created manually.
+16
include/hw/scsi/emulation.h
··· 1 + #ifndef HW_SCSI_EMULATION_H 2 + #define HW_SCSI_EMULATION_H 1 3 + 4 + typedef struct SCSIBlockLimits { 5 + bool wsnz; 6 + uint16_t min_io_size; 7 + uint32_t max_unmap_descr; 8 + uint32_t opt_io_size; 9 + uint32_t max_unmap_sectors; 10 + uint32_t unmap_sectors; 11 + uint32_t max_io_sectors; 12 + } SCSIBlockLimits; 13 + 14 + int scsi_emulate_block_limits(uint8_t *outbuf, const SCSIBlockLimits *bl); 15 + 16 + #endif
-1
include/hw/scsi/scsi.h
··· 189 189 void scsi_device_unit_attention_reported(SCSIDevice *dev); 190 190 void scsi_generic_read_device_inquiry(SCSIDevice *dev); 191 191 int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed); 192 - int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf); 193 192 int scsi_SG_IO_FROM_DEV(BlockBackend *blk, uint8_t *cmd, uint8_t cmd_size, 194 193 uint8_t *buf, uint8_t buf_size); 195 194 SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun);