From 8381d89bec0ca233c45c03939735e6eb89aab022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Fri, 2 Jun 2017 18:12:25 +0400 Subject: ivshmem: use ram_from_fd() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of having its own mmap handling code, reuse the code from exec.c. Note: memory_region_init_ram_from_fd() adds some restrictions (check for xen, kvm sync-mmu, etc) and changes (such as size alignment). This may actually be more correct. Signed-off-by: Marc-André Lureau Message-Id: <20170602141229.15326-6-marcandre.lureau@redhat.com> Signed-off-by: Paolo Bonzini --- hw/misc/ivshmem.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'hw') diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index 6367d041f0..2f0819d977 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -491,9 +491,9 @@ static void setup_interrupt(IVShmemState *s, int vector, Error **errp) static void process_msg_shmem(IVShmemState *s, int fd, Error **errp) { + Error *local_err = NULL; struct stat buf; size_t size; - void *ptr; if (s->ivshmem_bar2) { error_setg(errp, "server sent unexpected shared memory message"); @@ -522,15 +522,13 @@ static void process_msg_shmem(IVShmemState *s, int fd, Error **errp) } /* mmap the region and map into the BAR2 */ - ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (ptr == MAP_FAILED) { - error_setg_errno(errp, errno, "Failed to mmap shared memory"); - close(fd); + memory_region_init_ram_from_fd(&s->server_bar2, OBJECT(s), + "ivshmem.bar2", size, true, fd, &local_err); + if (local_err) { + error_propagate(errp, local_err); return; } - memory_region_init_ram_ptr(&s->server_bar2, OBJECT(s), - "ivshmem.bar2", size, ptr); - memory_region_set_fd(&s->server_bar2, fd); + s->ivshmem_bar2 = &s->server_bar2; } -- cgit v1.2.3 From 134550bf81a026e18cf58b81e2c2cceaf516f92e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 1 Jun 2017 17:18:39 +0200 Subject: megasas: do not read sense length more than once from frame Avoid TOC-TOU bugs depending on how the compiler behaves. Signed-off-by: Paolo Bonzini --- hw/scsi/megasas.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'hw') diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 804122ab05..1888118e5f 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -309,9 +309,11 @@ static int megasas_build_sense(MegasasCmd *cmd, uint8_t *sense_ptr, PCIDevice *pcid = PCI_DEVICE(cmd->state); uint32_t pa_hi = 0, pa_lo; hwaddr pa; + int frame_sense_len; - if (sense_len > cmd->frame->header.sense_len) { - sense_len = cmd->frame->header.sense_len; + frame_sense_len = cmd->frame->header.sense_len; + if (sense_len > frame_sense_len) { + sense_len = frame_sense_len; } if (sense_len) { pa_lo = le32_to_cpu(cmd->frame->pass.sense_addr_lo); -- cgit v1.2.3 From 24c0c77af515acbf0f9705e8096f33ef24d37430 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 1 Jun 2017 17:18:57 +0200 Subject: megasas: do not read iovec count more than once from frame Avoid TOC-TOU bugs depending on how the compiler behaves. Signed-off-by: Paolo Bonzini --- hw/scsi/megasas.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'hw') diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 1888118e5f..c353118882 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -675,15 +675,16 @@ out: static int megasas_map_dcmd(MegasasState *s, MegasasCmd *cmd) { dma_addr_t iov_pa, iov_size; + int iov_count; cmd->flags = le16_to_cpu(cmd->frame->header.flags); - if (!cmd->frame->header.sge_count) { + iov_count = cmd->frame->header.sge_count; + if (!iov_count) { trace_megasas_dcmd_zero_sge(cmd->index); cmd->iov_size = 0; return 0; - } else if (cmd->frame->header.sge_count > 1) { - trace_megasas_dcmd_invalid_sge(cmd->index, - cmd->frame->header.sge_count); + } else if (iov_count > 1) { + trace_megasas_dcmd_invalid_sge(cmd->index, iov_count); cmd->iov_size = 0; return -EINVAL; } -- cgit v1.2.3 From 5104fac8539eaf155fc6de93e164be43e1e62242 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 1 Jun 2017 17:18:23 +0200 Subject: megasas: do not read DCMD opcode more than once from frame Avoid TOC-TOU bugs by storing the DCMD opcode in the MegasasCmd Signed-off-by: Paolo Bonzini --- hw/scsi/megasas.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) (limited to 'hw') diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index c353118882..a3f75c1650 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -63,6 +63,7 @@ typedef struct MegasasCmd { hwaddr pa; hwaddr pa_size; + uint32_t dcmd_opcode; union mfi_frame *frame; SCSIRequest *req; QEMUSGList qsg; @@ -513,6 +514,7 @@ static MegasasCmd *megasas_enqueue_frame(MegasasState *s, cmd->context &= (uint64_t)0xFFFFFFFF; } cmd->count = count; + cmd->dcmd_opcode = -1; s->busy++; if (s->consumer_pa) { @@ -1562,22 +1564,21 @@ static const struct dcmd_cmd_tbl_t { static int megasas_handle_dcmd(MegasasState *s, MegasasCmd *cmd) { - int opcode; int retval = 0; size_t len; const struct dcmd_cmd_tbl_t *cmdptr = dcmd_cmd_tbl; - opcode = le32_to_cpu(cmd->frame->dcmd.opcode); - trace_megasas_handle_dcmd(cmd->index, opcode); + cmd->dcmd_opcode = le32_to_cpu(cmd->frame->dcmd.opcode); + trace_megasas_handle_dcmd(cmd->index, cmd->dcmd_opcode); if (megasas_map_dcmd(s, cmd) < 0) { return MFI_STAT_MEMORY_NOT_AVAILABLE; } - while (cmdptr->opcode != -1 && cmdptr->opcode != opcode) { + while (cmdptr->opcode != -1 && cmdptr->opcode != cmd->dcmd_opcode) { cmdptr++; } len = cmd->iov_size; if (cmdptr->opcode == -1) { - trace_megasas_dcmd_unhandled(cmd->index, opcode, len); + trace_megasas_dcmd_unhandled(cmd->index, cmd->dcmd_opcode, len); retval = megasas_dcmd_dummy(s, cmd); } else { trace_megasas_dcmd_enter(cmd->index, cmdptr->desc, len); @@ -1592,13 +1593,11 @@ static int megasas_handle_dcmd(MegasasState *s, MegasasCmd *cmd) static int megasas_finish_internal_dcmd(MegasasCmd *cmd, SCSIRequest *req) { - int opcode; int retval = MFI_STAT_OK; int lun = req->lun; - opcode = le32_to_cpu(cmd->frame->dcmd.opcode); - trace_megasas_dcmd_internal_finish(cmd->index, opcode, lun); - switch (opcode) { + trace_megasas_dcmd_internal_finish(cmd->index, cmd->dcmd_opcode, lun); + switch (cmd->dcmd_opcode) { case MFI_DCMD_PD_GET_INFO: retval = megasas_pd_get_info_submit(req->dev, lun, cmd); break; @@ -1606,7 +1605,7 @@ static int megasas_finish_internal_dcmd(MegasasCmd *cmd, retval = megasas_ld_get_info_submit(req->dev, lun, cmd); break; default: - trace_megasas_dcmd_internal_invalid(cmd->index, opcode); + trace_megasas_dcmd_internal_invalid(cmd->index, cmd->dcmd_opcode); retval = MFI_STAT_INVALID_DCMD; break; } @@ -1827,7 +1826,6 @@ static void megasas_xfer_complete(SCSIRequest *req, uint32_t len) { MegasasCmd *cmd = req->hba_private; uint8_t *buf; - uint32_t opcode; trace_megasas_io_complete(cmd->index, len); @@ -1837,8 +1835,7 @@ static void megasas_xfer_complete(SCSIRequest *req, uint32_t len) } buf = scsi_req_get_buf(req); - opcode = le32_to_cpu(cmd->frame->dcmd.opcode); - if (opcode == MFI_DCMD_PD_GET_INFO && cmd->iov_buf) { + if (cmd->dcmd_opcode == MFI_DCMD_PD_GET_INFO && cmd->iov_buf) { struct mfi_pd_info *info = cmd->iov_buf; if (info->inquiry_data[0] == 0x7f) { @@ -1849,7 +1846,7 @@ static void megasas_xfer_complete(SCSIRequest *req, uint32_t len) memcpy(info->vpd_page83, buf, len); } scsi_req_continue(req); - } else if (opcode == MFI_DCMD_LD_GET_INFO) { + } else if (cmd->dcmd_opcode == MFI_DCMD_LD_GET_INFO) { struct mfi_ld_info *info = cmd->iov_buf; if (cmd->iov_buf) { -- cgit v1.2.3 From 36c327a69d723571f02a7691631667cdb1865ee1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 1 Jun 2017 17:23:13 +0200 Subject: megasas: do not read command more than once from frame Avoid TOC-TOU bugs by passing the frame_cmd down, and checking cmd->dcmd_opcode instead of cmd->frame->header.frame_cmd. Signed-off-by: Paolo Bonzini --- hw/scsi/megasas.c | 60 +++++++++++++++++++++++-------------------------------- 1 file changed, 25 insertions(+), 35 deletions(-) (limited to 'hw') diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index a3f75c1650..38e0a2f5ef 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -1591,12 +1591,13 @@ static int megasas_handle_dcmd(MegasasState *s, MegasasCmd *cmd) } static int megasas_finish_internal_dcmd(MegasasCmd *cmd, - SCSIRequest *req) + SCSIRequest *req, size_t resid) { int retval = MFI_STAT_OK; int lun = req->lun; trace_megasas_dcmd_internal_finish(cmd->index, cmd->dcmd_opcode, lun); + cmd->iov_size -= resid; switch (cmd->dcmd_opcode) { case MFI_DCMD_PD_GET_INFO: retval = megasas_pd_get_info_submit(req->dev, lun, cmd); @@ -1649,11 +1650,12 @@ static int megasas_enqueue_req(MegasasCmd *cmd, bool is_write) } static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd, - bool is_logical) + int frame_cmd) { uint8_t *cdb; bool is_write; struct SCSIDevice *sdev = NULL; + bool is_logical = (frame_cmd == MFI_CMD_LD_SCSI_IO); cdb = cmd->frame->pass.cdb; @@ -1661,7 +1663,7 @@ static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd, if (cmd->frame->header.target_id >= MFI_MAX_LD || cmd->frame->header.lun_id != 0) { trace_megasas_scsi_target_not_present( - mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical, + mfi_frame_desc[frame_cmd], is_logical, cmd->frame->header.target_id, cmd->frame->header.lun_id); return MFI_STAT_DEVICE_NOT_FOUND; } @@ -1671,19 +1673,20 @@ static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd, cmd->iov_size = le32_to_cpu(cmd->frame->header.data_len); trace_megasas_handle_scsi(mfi_frame_desc[cmd->frame->header.frame_cmd], - is_logical, cmd->frame->header.target_id, + trace_megasas_handle_scsi(mfi_frame_desc[frame_cmd], is_logical, + cmd->frame->header.target_id, cmd->frame->header.lun_id, sdev, cmd->iov_size); if (!sdev || (megasas_is_jbod(s) && is_logical)) { trace_megasas_scsi_target_not_present( - mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical, + mfi_frame_desc[frame_cmd], is_logical, cmd->frame->header.target_id, cmd->frame->header.lun_id); return MFI_STAT_DEVICE_NOT_FOUND; } if (cmd->frame->header.cdb_len > 16) { trace_megasas_scsi_invalid_cdb_len( - mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical, + mfi_frame_desc[frame_cmd], is_logical, cmd->frame->header.target_id, cmd->frame->header.lun_id, cmd->frame->header.cdb_len); megasas_write_sense(cmd, SENSE_CODE(INVALID_OPCODE)); @@ -1703,7 +1706,7 @@ static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd, cmd->frame->header.lun_id, cdb, cmd); if (!cmd->req) { trace_megasas_scsi_req_alloc_failed( - mfi_frame_desc[cmd->frame->header.frame_cmd], + mfi_frame_desc[frame_cmd], cmd->frame->header.target_id, cmd->frame->header.lun_id); megasas_write_sense(cmd, SENSE_CODE(NO_SENSE)); cmd->frame->header.scsi_status = BUSY; @@ -1725,11 +1728,11 @@ static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd, return MFI_STAT_INVALID_STATUS; } -static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd) +static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd, int frame_cmd) { uint32_t lba_count, lba_start_hi, lba_start_lo; uint64_t lba_start; - bool is_write = (cmd->frame->header.frame_cmd == MFI_CMD_LD_WRITE); + bool is_write = (frame_cmd == MFI_CMD_LD_WRITE); uint8_t cdb[16]; int len; struct SCSIDevice *sdev = NULL; @@ -1746,20 +1749,20 @@ static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd) } trace_megasas_handle_io(cmd->index, - mfi_frame_desc[cmd->frame->header.frame_cmd], + mfi_frame_desc[frame_cmd], cmd->frame->header.target_id, cmd->frame->header.lun_id, (unsigned long)lba_start, (unsigned long)lba_count); if (!sdev) { trace_megasas_io_target_not_present(cmd->index, - mfi_frame_desc[cmd->frame->header.frame_cmd], + mfi_frame_desc[frame_cmd], cmd->frame->header.target_id, cmd->frame->header.lun_id); return MFI_STAT_DEVICE_NOT_FOUND; } if (cmd->frame->header.cdb_len > 16) { trace_megasas_scsi_invalid_cdb_len( - mfi_frame_desc[cmd->frame->header.frame_cmd], 1, + mfi_frame_desc[frame_cmd], 1, cmd->frame->header.target_id, cmd->frame->header.lun_id, cmd->frame->header.cdb_len); megasas_write_sense(cmd, SENSE_CODE(INVALID_OPCODE)); @@ -1781,7 +1784,7 @@ static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd) cmd->frame->header.lun_id, cdb, cmd); if (!cmd->req) { trace_megasas_scsi_req_alloc_failed( - mfi_frame_desc[cmd->frame->header.frame_cmd], + mfi_frame_desc[frame_cmd], cmd->frame->header.target_id, cmd->frame->header.lun_id); megasas_write_sense(cmd, SENSE_CODE(NO_SENSE)); cmd->frame->header.scsi_status = BUSY; @@ -1799,23 +1802,11 @@ static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd) return MFI_STAT_INVALID_STATUS; } -static int megasas_finish_internal_command(MegasasCmd *cmd, - SCSIRequest *req, size_t resid) -{ - int retval = MFI_STAT_INVALID_CMD; - - if (cmd->frame->header.frame_cmd == MFI_CMD_DCMD) { - cmd->iov_size -= resid; - retval = megasas_finish_internal_dcmd(cmd, req); - } - return retval; -} - static QEMUSGList *megasas_get_sg_list(SCSIRequest *req) { MegasasCmd *cmd = req->hba_private; - if (cmd->frame->header.frame_cmd == MFI_CMD_DCMD) { + if (cmd->dcmd_opcode != -1) { return NULL; } else { return &cmd->qsg; @@ -1829,7 +1820,7 @@ static void megasas_xfer_complete(SCSIRequest *req, uint32_t len) trace_megasas_io_complete(cmd->index, len); - if (cmd->frame->header.frame_cmd != MFI_CMD_DCMD) { + if (cmd->dcmd_opcode != -1) { scsi_req_continue(req); return; } @@ -1872,7 +1863,7 @@ static void megasas_command_complete(SCSIRequest *req, uint32_t status, /* * Internal command complete */ - cmd_status = megasas_finish_internal_command(cmd, req, resid); + cmd_status = megasas_finish_internal_dcmd(cmd, req, resid); if (cmd_status == MFI_STAT_INVALID_STATUS) { return; } @@ -1943,6 +1934,7 @@ static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr, { uint8_t frame_status = MFI_STAT_INVALID_CMD; uint64_t frame_context; + int frame_cmd; MegasasCmd *cmd; /* @@ -1961,7 +1953,8 @@ static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr, s->event_count++; return; } - switch (cmd->frame->header.frame_cmd) { + frame_cmd = cmd->frame->header.frame_cmd; + switch (frame_cmd) { case MFI_CMD_INIT: frame_status = megasas_init_firmware(s, cmd); break; @@ -1972,18 +1965,15 @@ static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr, frame_status = megasas_handle_abort(s, cmd); break; case MFI_CMD_PD_SCSI_IO: - frame_status = megasas_handle_scsi(s, cmd, 0); - break; case MFI_CMD_LD_SCSI_IO: - frame_status = megasas_handle_scsi(s, cmd, 1); + frame_status = megasas_handle_scsi(s, cmd, frame_cmd); break; case MFI_CMD_LD_READ: case MFI_CMD_LD_WRITE: - frame_status = megasas_handle_io(s, cmd); + frame_status = megasas_handle_io(s, cmd, frame_cmd); break; default: - trace_megasas_unhandled_frame_cmd(cmd->index, - cmd->frame->header.frame_cmd); + trace_megasas_unhandled_frame_cmd(cmd->index, frame_cmd); s->event_count++; break; } -- cgit v1.2.3 From b356807fcdfc45583c437f761fc579ab2a8eab11 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 1 Jun 2017 17:25:03 +0200 Subject: megasas: do not read SCSI req parameters more than once from frame Signed-off-by: Paolo Bonzini --- hw/scsi/megasas.c | 60 ++++++++++++++++++++++++------------------------------- 1 file changed, 26 insertions(+), 34 deletions(-) (limited to 'hw') diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 38e0a2f5ef..135662df31 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -1653,42 +1653,39 @@ static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd, int frame_cmd) { uint8_t *cdb; + int target_id, lun_id, cdb_len; bool is_write; struct SCSIDevice *sdev = NULL; bool is_logical = (frame_cmd == MFI_CMD_LD_SCSI_IO); cdb = cmd->frame->pass.cdb; + target_id = cmd->frame->header.target_id; + lun_id = cmd->frame->header.lun_id; + cdb_len = cmd->frame->header.cdb_len; if (is_logical) { - if (cmd->frame->header.target_id >= MFI_MAX_LD || - cmd->frame->header.lun_id != 0) { + if (target_id >= MFI_MAX_LD || lun_id != 0) { trace_megasas_scsi_target_not_present( - mfi_frame_desc[frame_cmd], is_logical, - cmd->frame->header.target_id, cmd->frame->header.lun_id); + mfi_frame_desc[frame_cmd], is_logical, target_id, lun_id); return MFI_STAT_DEVICE_NOT_FOUND; } } - sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id, - cmd->frame->header.lun_id); + sdev = scsi_device_find(&s->bus, 0, target_id, lun_id); cmd->iov_size = le32_to_cpu(cmd->frame->header.data_len); - trace_megasas_handle_scsi(mfi_frame_desc[cmd->frame->header.frame_cmd], trace_megasas_handle_scsi(mfi_frame_desc[frame_cmd], is_logical, - cmd->frame->header.target_id, - cmd->frame->header.lun_id, sdev, cmd->iov_size); + target_id, lun_id, sdev, cmd->iov_size); if (!sdev || (megasas_is_jbod(s) && is_logical)) { trace_megasas_scsi_target_not_present( - mfi_frame_desc[frame_cmd], is_logical, - cmd->frame->header.target_id, cmd->frame->header.lun_id); + mfi_frame_desc[frame_cmd], is_logical, target_id, lun_id); return MFI_STAT_DEVICE_NOT_FOUND; } - if (cmd->frame->header.cdb_len > 16) { + if (cdb_len > 16) { trace_megasas_scsi_invalid_cdb_len( mfi_frame_desc[frame_cmd], is_logical, - cmd->frame->header.target_id, cmd->frame->header.lun_id, - cmd->frame->header.cdb_len); + target_id, lun_id, cdb_len); megasas_write_sense(cmd, SENSE_CODE(INVALID_OPCODE)); cmd->frame->header.scsi_status = CHECK_CONDITION; s->event_count++; @@ -1702,12 +1699,10 @@ static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd, return MFI_STAT_SCSI_DONE_WITH_ERROR; } - cmd->req = scsi_req_new(sdev, cmd->index, - cmd->frame->header.lun_id, cdb, cmd); + cmd->req = scsi_req_new(sdev, cmd->index, lun_id, cdb, cmd); if (!cmd->req) { trace_megasas_scsi_req_alloc_failed( - mfi_frame_desc[frame_cmd], - cmd->frame->header.target_id, cmd->frame->header.lun_id); + mfi_frame_desc[frame_cmd], target_id, lun_id); megasas_write_sense(cmd, SENSE_CODE(NO_SENSE)); cmd->frame->header.scsi_status = BUSY; s->event_count++; @@ -1736,35 +1731,33 @@ static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd, int frame_cmd) uint8_t cdb[16]; int len; struct SCSIDevice *sdev = NULL; + int target_id, lun_id, cdb_len; lba_count = le32_to_cpu(cmd->frame->io.header.data_len); lba_start_lo = le32_to_cpu(cmd->frame->io.lba_lo); lba_start_hi = le32_to_cpu(cmd->frame->io.lba_hi); lba_start = ((uint64_t)lba_start_hi << 32) | lba_start_lo; - if (cmd->frame->header.target_id < MFI_MAX_LD && - cmd->frame->header.lun_id == 0) { - sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id, - cmd->frame->header.lun_id); + target_id = cmd->frame->header.target_id; + lun_id = cmd->frame->header.lun_id; + cdb_len = cmd->frame->header.cdb_len; + + if (target_id < MFI_MAX_LD && lun_id == 0) { + sdev = scsi_device_find(&s->bus, 0, target_id, lun_id); } trace_megasas_handle_io(cmd->index, - mfi_frame_desc[frame_cmd], - cmd->frame->header.target_id, - cmd->frame->header.lun_id, + mfi_frame_desc[frame_cmd], target_id, lun_id, (unsigned long)lba_start, (unsigned long)lba_count); if (!sdev) { trace_megasas_io_target_not_present(cmd->index, - mfi_frame_desc[frame_cmd], - cmd->frame->header.target_id, cmd->frame->header.lun_id); + mfi_frame_desc[frame_cmd], target_id, lun_id); return MFI_STAT_DEVICE_NOT_FOUND; } - if (cmd->frame->header.cdb_len > 16) { + if (cdb_len > 16) { trace_megasas_scsi_invalid_cdb_len( - mfi_frame_desc[frame_cmd], 1, - cmd->frame->header.target_id, cmd->frame->header.lun_id, - cmd->frame->header.cdb_len); + mfi_frame_desc[frame_cmd], 1, target_id, lun_id, cdb_len); megasas_write_sense(cmd, SENSE_CODE(INVALID_OPCODE)); cmd->frame->header.scsi_status = CHECK_CONDITION; s->event_count++; @@ -1781,11 +1774,10 @@ static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd, int frame_cmd) megasas_encode_lba(cdb, lba_start, lba_count, is_write); cmd->req = scsi_req_new(sdev, cmd->index, - cmd->frame->header.lun_id, cdb, cmd); + lun_id, cdb, cmd); if (!cmd->req) { trace_megasas_scsi_req_alloc_failed( - mfi_frame_desc[frame_cmd], - cmd->frame->header.target_id, cmd->frame->header.lun_id); + mfi_frame_desc[frame_cmd], target_id, lun_id); megasas_write_sense(cmd, SENSE_CODE(NO_SENSE)); cmd->frame->header.scsi_status = BUSY; s->event_count++; -- cgit v1.2.3 From 87e459a810d7b1ec1638085b5a80ea3d9b43119a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 1 Jun 2017 17:26:14 +0200 Subject: megasas: always store SCSIRequest* into MegasasCmd This ensures that the request is unref'ed properly, and avoids a segmentation fault in the new qtest testcase that is added. This is CVE-2017-9503. Reported-by: Zhangyanyu Signed-off-by: Paolo Bonzini --- hw/scsi/megasas.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'hw') diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 135662df31..734fdaef90 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -609,6 +609,9 @@ static void megasas_reset_frames(MegasasState *s) static void megasas_abort_command(MegasasCmd *cmd) { /* Never abort internal commands. */ + if (cmd->dcmd_opcode != -1) { + return; + } if (cmd->req != NULL) { scsi_req_cancel(cmd->req); } @@ -1017,7 +1020,6 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun, uint64_t pd_size; uint16_t pd_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF); uint8_t cmdbuf[6]; - SCSIRequest *req; size_t len, resid; if (!cmd->iov_buf) { @@ -1026,8 +1028,8 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun, info->inquiry_data[0] = 0x7f; /* Force PQual 0x3, PType 0x1f */ info->vpd_page83[0] = 0x7f; megasas_setup_inquiry(cmdbuf, 0, sizeof(info->inquiry_data)); - req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd); - if (!req) { + cmd->req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd); + if (!cmd->req) { trace_megasas_dcmd_req_alloc_failed(cmd->index, "PD get info std inquiry"); g_free(cmd->iov_buf); @@ -1036,26 +1038,26 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun, } trace_megasas_dcmd_internal_submit(cmd->index, "PD get info std inquiry", lun); - len = scsi_req_enqueue(req); + len = scsi_req_enqueue(cmd->req); if (len > 0) { cmd->iov_size = len; - scsi_req_continue(req); + scsi_req_continue(cmd->req); } return MFI_STAT_INVALID_STATUS; } else if (info->inquiry_data[0] != 0x7f && info->vpd_page83[0] == 0x7f) { megasas_setup_inquiry(cmdbuf, 0x83, sizeof(info->vpd_page83)); - req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd); - if (!req) { + cmd->req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd); + if (!cmd->req) { trace_megasas_dcmd_req_alloc_failed(cmd->index, "PD get info vpd inquiry"); return MFI_STAT_FLASH_ALLOC_FAIL; } trace_megasas_dcmd_internal_submit(cmd->index, "PD get info vpd inquiry", lun); - len = scsi_req_enqueue(req); + len = scsi_req_enqueue(cmd->req); if (len > 0) { cmd->iov_size = len; - scsi_req_continue(req); + scsi_req_continue(cmd->req); } return MFI_STAT_INVALID_STATUS; } @@ -1217,7 +1219,6 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun, struct mfi_ld_info *info = cmd->iov_buf; size_t dcmd_size = sizeof(struct mfi_ld_info); uint8_t cdb[6]; - SCSIRequest *req; ssize_t len, resid; uint16_t sdev_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF); uint64_t ld_size; @@ -1226,8 +1227,8 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun, cmd->iov_buf = g_malloc0(dcmd_size); info = cmd->iov_buf; megasas_setup_inquiry(cdb, 0x83, sizeof(info->vpd_page83)); - req = scsi_req_new(sdev, cmd->index, lun, cdb, cmd); - if (!req) { + cmd->req = scsi_req_new(sdev, cmd->index, lun, cdb, cmd); + if (!cmd->req) { trace_megasas_dcmd_req_alloc_failed(cmd->index, "LD get info vpd inquiry"); g_free(cmd->iov_buf); @@ -1236,10 +1237,10 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun, } trace_megasas_dcmd_internal_submit(cmd->index, "LD get info vpd inquiry", lun); - len = scsi_req_enqueue(req); + len = scsi_req_enqueue(cmd->req); if (len > 0) { cmd->iov_size = len; - scsi_req_continue(req); + scsi_req_continue(cmd->req); } return MFI_STAT_INVALID_STATUS; } @@ -1851,7 +1852,7 @@ static void megasas_command_complete(SCSIRequest *req, uint32_t status, return; } - if (cmd->req == NULL) { + if (cmd->dcmd_opcode != -1) { /* * Internal command complete */ -- cgit v1.2.3 From f12c1ebddf79c965d4fdca752c6b316c889c7675 Mon Sep 17 00:00:00 2001 From: Felipe Franciosi Date: Thu, 2 Mar 2017 10:25:52 -0800 Subject: vhost-user-scsi: Introduce vhost-user-scsi host device This commit introduces a vhost-user device for SCSI. This is based on the existing vhost-scsi implementation, but done over vhost-user instead. It also uses a chardev to connect to the backend. Unlike vhost-scsi (today), VMs using vhost-user-scsi can be live migrated. To use it, start Qemu with a command line equivalent to: qemu-system-x86_64 \ -chardev socket,id=vus0,path=/tmp/vus.sock \ -device vhost-user-scsi-pci,chardev=vus0,bus=pci.0,addr=... A separate commit presents a sample application linked with libiscsi to provide a backend for vhost-user-scsi. Signed-off-by: Felipe Franciosi Message-Id: <1488479153-21203-4-git-send-email-felipe@nutanix.com> Signed-off-by: Paolo Bonzini --- hw/scsi/Makefile.objs | 1 + hw/scsi/vhost-scsi-common.c | 1 - hw/scsi/vhost-user-scsi.c | 205 ++++++++++++++++++++++++++++++++++++++++++++ hw/virtio/virtio-pci.c | 58 +++++++++++++ hw/virtio/virtio-pci.h | 11 +++ 5 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 hw/scsi/vhost-user-scsi.c (limited to 'hw') diff --git a/hw/scsi/Makefile.objs b/hw/scsi/Makefile.objs index 54d8754e9a..b188f7242b 100644 --- a/hw/scsi/Makefile.objs +++ b/hw/scsi/Makefile.objs @@ -11,4 +11,5 @@ obj-$(CONFIG_PSERIES) += spapr_vscsi.o ifeq ($(CONFIG_VIRTIO),y) obj-y += virtio-scsi.o virtio-scsi-dataplane.o obj-$(CONFIG_VHOST_SCSI) += vhost-scsi-common.o vhost-scsi.o +obj-$(CONFIG_VHOST_USER_SCSI) += vhost-scsi-common.o vhost-user-scsi.o endif diff --git a/hw/scsi/vhost-scsi-common.c b/hw/scsi/vhost-scsi-common.c index e41c0314db..d434b3e99a 100644 --- a/hw/scsi/vhost-scsi-common.c +++ b/hw/scsi/vhost-scsi-common.c @@ -16,7 +16,6 @@ */ #include "qemu/osdep.h" -#include #include "qapi/error.h" #include "qemu/error-report.h" #include "migration/migration.h" diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c new file mode 100644 index 0000000000..500fa6a067 --- /dev/null +++ b/hw/scsi/vhost-user-scsi.c @@ -0,0 +1,205 @@ +/* + * vhost-user-scsi host device + * + * Copyright (c) 2016 Nutanix Inc. All rights reserved. + * + * Author: + * Felipe Franciosi + * + * This work is largely based on the "vhost-scsi" implementation by: + * Stefan Hajnoczi + * Nicholas Bellinger + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/typedefs.h" +#include "qom/object.h" +#include "hw/fw-path-provider.h" +#include "hw/qdev-core.h" +#include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-backend.h" +#include "hw/virtio/vhost-user-scsi.h" +#include "hw/virtio/virtio.h" +#include "hw/virtio/virtio-access.h" +#include "chardev/char-fe.h" + +/* Features supported by the host application */ +static const int user_feature_bits[] = { + VIRTIO_F_NOTIFY_ON_EMPTY, + VIRTIO_RING_F_INDIRECT_DESC, + VIRTIO_RING_F_EVENT_IDX, + VIRTIO_SCSI_F_HOTPLUG, + VHOST_INVALID_FEATURE_BIT +}; + +static void vhost_user_scsi_set_status(VirtIODevice *vdev, uint8_t status) +{ + VHostUserSCSI *s = (VHostUserSCSI *)vdev; + VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); + bool start = (status & VIRTIO_CONFIG_S_DRIVER_OK) && vdev->vm_running; + + if (vsc->dev.started == start) { + return; + } + + if (start) { + int ret; + + ret = vhost_scsi_common_start(vsc); + if (ret < 0) { + error_report("unable to start vhost-user-scsi: %s", strerror(-ret)); + exit(1); + } + } else { + vhost_scsi_common_stop(vsc); + } +} + +static void vhost_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq) +{ +} + +static void vhost_user_scsi_realize(DeviceState *dev, Error **errp) +{ + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); + VHostUserSCSI *s = VHOST_USER_SCSI(dev); + VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); + Error *err = NULL; + int ret; + + if (!vs->conf.chardev.chr) { + error_setg(errp, "vhost-user-scsi: missing chardev"); + return; + } + + virtio_scsi_common_realize(dev, vhost_dummy_handle_output, + vhost_dummy_handle_output, + vhost_dummy_handle_output, &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + + vsc->dev.nvqs = 2 + vs->conf.num_queues; + vsc->dev.vqs = g_new(struct vhost_virtqueue, vsc->dev.nvqs); + vsc->dev.vq_index = 0; + vsc->dev.backend_features = 0; + + ret = vhost_dev_init(&vsc->dev, (void *)&vs->conf.chardev, + VHOST_BACKEND_TYPE_USER, 0); + if (ret < 0) { + error_setg(errp, "vhost-user-scsi: vhost initialization failed: %s", + strerror(-ret)); + return; + } + + /* Channel and lun both are 0 for bootable vhost-user-scsi disk */ + vsc->channel = 0; + vsc->lun = 0; + vsc->target = vs->conf.boot_tpgt; +} + +static void vhost_user_scsi_unrealize(DeviceState *dev, Error **errp) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserSCSI *s = VHOST_USER_SCSI(dev); + VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); + + /* This will stop the vhost backend. */ + vhost_user_scsi_set_status(vdev, 0); + + vhost_dev_cleanup(&vsc->dev); + g_free(vsc->dev.vqs); + + virtio_scsi_common_unrealize(dev, errp); +} + +static uint64_t vhost_user_scsi_get_features(VirtIODevice *vdev, + uint64_t features, Error **errp) +{ + VHostUserSCSI *s = VHOST_USER_SCSI(vdev); + + /* Turn on predefined features supported by this device */ + features |= s->host_features; + + return vhost_scsi_common_get_features(vdev, features, errp); +} + +static Property vhost_user_scsi_properties[] = { + DEFINE_PROP_CHR("chardev", VirtIOSCSICommon, conf.chardev), + DEFINE_PROP_UINT32("boot_tpgt", VirtIOSCSICommon, conf.boot_tpgt, 0), + DEFINE_PROP_UINT32("num_queues", VirtIOSCSICommon, conf.num_queues, 1), + DEFINE_PROP_UINT32("max_sectors", VirtIOSCSICommon, conf.max_sectors, + 0xFFFF), + DEFINE_PROP_UINT32("cmd_per_lun", VirtIOSCSICommon, conf.cmd_per_lun, 128), + DEFINE_PROP_BIT64("hotplug", VHostUserSCSI, host_features, + VIRTIO_SCSI_F_HOTPLUG, + true), + DEFINE_PROP_BIT64("param_change", VHostUserSCSI, host_features, + VIRTIO_SCSI_F_CHANGE, + true), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_vhost_scsi = { + .name = "virtio-scsi", + .minimum_version_id = 1, + .version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_VIRTIO_DEVICE, + VMSTATE_END_OF_LIST() + }, +}; + +static void vhost_user_scsi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(klass); + + dc->props = vhost_user_scsi_properties; + dc->vmsd = &vmstate_vhost_scsi; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + vdc->realize = vhost_user_scsi_realize; + vdc->unrealize = vhost_user_scsi_unrealize; + vdc->get_features = vhost_user_scsi_get_features; + vdc->set_config = vhost_scsi_common_set_config; + vdc->set_status = vhost_user_scsi_set_status; + fwc->get_dev_path = vhost_scsi_common_get_fw_dev_path; +} + +static void vhost_user_scsi_instance_init(Object *obj) +{ + VHostSCSICommon *vsc = VHOST_SCSI_COMMON(obj); + + vsc->feature_bits = user_feature_bits; + + /* Add the bootindex property for this object */ + device_add_bootindex_property(obj, &vsc->bootindex, "bootindex", NULL, + DEVICE(vsc), NULL); +} + +static const TypeInfo vhost_user_scsi_info = { + .name = TYPE_VHOST_USER_SCSI, + .parent = TYPE_VHOST_SCSI_COMMON, + .instance_size = sizeof(VHostUserSCSI), + .class_init = vhost_user_scsi_class_init, + .instance_init = vhost_user_scsi_instance_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_FW_PATH_PROVIDER }, + { } + }, +}; + +static void virtio_register_types(void) +{ + type_register_static(&vhost_user_scsi_info); +} + +type_init(virtio_register_types) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index f9b7244808..20d6a08616 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -2135,6 +2135,61 @@ static const TypeInfo vhost_scsi_pci_info = { }; #endif +#ifdef CONFIG_LINUX +/* vhost-user-scsi-pci */ +static Property vhost_user_scsi_pci_properties[] = { + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, + DEV_NVECTORS_UNSPECIFIED), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vhost_user_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VHostUserSCSIPCI *dev = VHOST_USER_SCSI_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); + + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { + vpci_dev->nvectors = vs->conf.num_queues + 3; + } + + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static void vhost_user_scsi_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + k->realize = vhost_user_scsi_pci_realize; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + dc->props = vhost_user_scsi_pci_properties; + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI; + pcidev_k->revision = 0x00; + pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI; +} + +static void vhost_user_scsi_pci_instance_init(Object *obj) +{ + VHostUserSCSIPCI *dev = VHOST_USER_SCSI_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VHOST_USER_SCSI); + object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), + "bootindex", &error_abort); +} + +static const TypeInfo vhost_user_scsi_pci_info = { + .name = TYPE_VHOST_USER_SCSI_PCI, + .parent = TYPE_VIRTIO_PCI, + .instance_size = sizeof(VHostUserSCSIPCI), + .instance_init = vhost_user_scsi_pci_instance_init, + .class_init = vhost_user_scsi_pci_class_init, +}; +#endif + /* vhost-vsock-pci */ #ifdef CONFIG_VHOST_VSOCK @@ -2612,6 +2667,9 @@ static void virtio_pci_register_types(void) #ifdef CONFIG_VHOST_SCSI type_register_static(&vhost_scsi_pci_info); #endif +#ifdef CONFIG_LINUX + type_register_static(&vhost_user_scsi_pci_info); +#endif #ifdef CONFIG_VHOST_VSOCK type_register_static(&vhost_vsock_pci_info); #endif diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h index b095dfc6d9..69f5959623 100644 --- a/hw/virtio/virtio-pci.h +++ b/hw/virtio/virtio-pci.h @@ -26,6 +26,7 @@ #include "hw/virtio/virtio-input.h" #include "hw/virtio/virtio-gpu.h" #include "hw/virtio/virtio-crypto.h" +#include "hw/virtio/vhost-user-scsi.h" #ifdef CONFIG_VIRTFS #include "hw/9pfs/virtio-9p.h" @@ -44,6 +45,7 @@ typedef struct VirtIOBalloonPCI VirtIOBalloonPCI; typedef struct VirtIOSerialPCI VirtIOSerialPCI; typedef struct VirtIONetPCI VirtIONetPCI; typedef struct VHostSCSIPCI VHostSCSIPCI; +typedef struct VHostUserSCSIPCI VHostUserSCSIPCI; typedef struct VirtIORngPCI VirtIORngPCI; typedef struct VirtIOInputPCI VirtIOInputPCI; typedef struct VirtIOInputHIDPCI VirtIOInputHIDPCI; @@ -230,6 +232,15 @@ struct VHostSCSIPCI { }; #endif +#define TYPE_VHOST_USER_SCSI_PCI "vhost-user-scsi-pci" +#define VHOST_USER_SCSI_PCI(obj) \ + OBJECT_CHECK(VHostUserSCSIPCI, (obj), TYPE_VHOST_USER_SCSI_PCI) + +struct VHostUserSCSIPCI { + VirtIOPCIProxy parent_obj; + VHostUserSCSI vdev; +}; + /* * virtio-blk-pci: This extends VirtioPCIProxy. */ -- cgit v1.2.3