diff --git a/0001-vmw_pvscsi-check-message-ring-page-count-at-initiali.patch b/0001-vmw_pvscsi-check-message-ring-page-count-at-initiali.patch new file mode 100644 index 0000000..a005b86 --- /dev/null +++ b/0001-vmw_pvscsi-check-message-ring-page-count-at-initiali.patch @@ -0,0 +1,30 @@ +From: P J P +Date: Tue, 25 Apr 2017 18:36:23 +0530 +Subject: [PATCH] vmw_pvscsi: check message ring page count at initialisation + +A guest could set the message ring page count to zero, resulting in +infinite loop. Add check to avoid it. + +Reported-by: YY Z +Signed-off-by: P J P +Message-Id: <20170425130623.3649-1-ppandit@redhat.com> +Reviewed-by: Dmitry Fleytman +Signed-off-by: Paolo Bonzini +(cherry picked from commit f68826989cd4d1217797251339579c57b3c0934e) +--- + hw/scsi/vmw_pvscsi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c +index 75575461e2..4a106da856 100644 +--- a/hw/scsi/vmw_pvscsi.c ++++ b/hw/scsi/vmw_pvscsi.c +@@ -202,7 +202,7 @@ pvscsi_ring_init_msg(PVSCSIRingInfo *m, PVSCSICmdDescSetupMsgRing *ri) + uint32_t len_log2; + uint32_t ring_size; + +- if (ri->numPages > PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES) { ++ if (!ri->numPages || ri->numPages > PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES) { + return -1; + } + ring_size = ri->numPages * PVSCSI_MAX_NUM_MSG_ENTRIES_PER_PAGE; diff --git a/0002-audio-release-capture-buffers.patch b/0002-audio-release-capture-buffers.patch new file mode 100644 index 0000000..143defb --- /dev/null +++ b/0002-audio-release-capture-buffers.patch @@ -0,0 +1,35 @@ +From: Gerd Hoffmann +Date: Fri, 28 Apr 2017 09:56:12 +0200 +Subject: [PATCH] audio: release capture buffers + +AUD_add_capture() allocates two buffers which are never released. +Add the missing calls to AUD_del_capture(). + +Impact: Allows vnc clients to exhaust host memory by repeatedly +starting and stopping audio capture. + +Fixes: CVE-2017-8309 +Cc: P J P +Cc: Huawei PSIRT +Reported-by: "Jiangxin (hunter, SCC)" +Signed-off-by: Gerd Hoffmann +Reviewed-by: Prasad J Pandit +Message-id: 20170428075612.9997-1-kraxel@redhat.com +(cherry picked from commit 3268a845f41253fb55852a8429c32b50f36f349a) +--- + audio/audio.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/audio/audio.c b/audio/audio.c +index c8898d8422..beafed209b 100644 +--- a/audio/audio.c ++++ b/audio/audio.c +@@ -2028,6 +2028,8 @@ void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque) + sw = sw1; + } + QLIST_REMOVE (cap, entries); ++ g_free (cap->hw.mix_buf); ++ g_free (cap->buf); + g_free (cap); + } + return; diff --git a/0003-input-limit-kbd-queue-depth.patch b/0003-input-limit-kbd-queue-depth.patch new file mode 100644 index 0000000..ca9e610 --- /dev/null +++ b/0003-input-limit-kbd-queue-depth.patch @@ -0,0 +1,87 @@ +From: Gerd Hoffmann +Date: Fri, 28 Apr 2017 10:42:37 +0200 +Subject: [PATCH] input: limit kbd queue depth + +Apply a limit to the number of items we accept into the keyboard queue. + +Impact: Without this limit vnc clients can exhaust host memory by +sending keyboard events faster than qemu feeds them to the guest. + +Fixes: CVE-2017-8379 +Cc: P J P +Cc: Huawei PSIRT +Reported-by: jiangxin1@huawei.com +Signed-off-by: Gerd Hoffmann +Message-id: 20170428084237.23960-1-kraxel@redhat.com +(cherry picked from commit fa18f36a461984eae50ab957e47ec78dae3c14fc) +--- + ui/input.c | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +diff --git a/ui/input.c b/ui/input.c +index ed88cda6d6..fb1f404095 100644 +--- a/ui/input.c ++++ b/ui/input.c +@@ -41,6 +41,8 @@ static QTAILQ_HEAD(QemuInputEventQueueHead, QemuInputEventQueue) kbd_queue = + QTAILQ_HEAD_INITIALIZER(kbd_queue); + static QEMUTimer *kbd_timer; + static uint32_t kbd_default_delay_ms = 10; ++static uint32_t queue_count; ++static uint32_t queue_limit = 1024; + + QemuInputHandlerState *qemu_input_handler_register(DeviceState *dev, + QemuInputHandler *handler) +@@ -268,6 +270,7 @@ static void qemu_input_queue_process(void *opaque) + break; + } + QTAILQ_REMOVE(queue, item, node); ++ queue_count--; + g_free(item); + } + } +@@ -282,6 +285,7 @@ static void qemu_input_queue_delay(struct QemuInputEventQueueHead *queue, + item->delay_ms = delay_ms; + item->timer = timer; + QTAILQ_INSERT_TAIL(queue, item, node); ++ queue_count++; + + if (start_timer) { + timer_mod(item->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +@@ -298,6 +302,7 @@ static void qemu_input_queue_event(struct QemuInputEventQueueHead *queue, + item->src = src; + item->evt = evt; + QTAILQ_INSERT_TAIL(queue, item, node); ++ queue_count++; + } + + static void qemu_input_queue_sync(struct QemuInputEventQueueHead *queue) +@@ -306,6 +311,7 @@ static void qemu_input_queue_sync(struct QemuInputEventQueueHead *queue) + + item->type = QEMU_INPUT_QUEUE_SYNC; + QTAILQ_INSERT_TAIL(queue, item, node); ++ queue_count++; + } + + void qemu_input_event_send_impl(QemuConsole *src, InputEvent *evt) +@@ -381,7 +387,7 @@ void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down) + qemu_input_event_send(src, evt); + qemu_input_event_sync(); + qapi_free_InputEvent(evt); +- } else { ++ } else if (queue_count < queue_limit) { + qemu_input_queue_event(&kbd_queue, src, evt); + qemu_input_queue_sync(&kbd_queue); + } +@@ -409,8 +415,10 @@ void qemu_input_event_send_key_delay(uint32_t delay_ms) + kbd_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, qemu_input_queue_process, + &kbd_queue); + } +- qemu_input_queue_delay(&kbd_queue, kbd_timer, +- delay_ms ? delay_ms : kbd_default_delay_ms); ++ if (queue_count < queue_limit) { ++ qemu_input_queue_delay(&kbd_queue, kbd_timer, ++ delay_ms ? delay_ms : kbd_default_delay_ms); ++ } + } + + InputEvent *qemu_input_event_new_btn(InputButton btn, bool down) diff --git a/0004-scsi-avoid-an-off-by-one-error-in-megasas_mmio_write.patch b/0004-scsi-avoid-an-off-by-one-error-in-megasas_mmio_write.patch new file mode 100644 index 0000000..77c7023 --- /dev/null +++ b/0004-scsi-avoid-an-off-by-one-error-in-megasas_mmio_write.patch @@ -0,0 +1,42 @@ +From: Prasad J Pandit +Date: Mon, 24 Apr 2017 17:36:34 +0530 +Subject: [PATCH] scsi: avoid an off-by-one error in megasas_mmio_write + +While reading magic sequence(MFI_SEQ) in megasas_mmio_write, +an off-by-one error could occur as 's->adp_reset' index is not +reset after reading the last sequence. + +Reported-by: YY Z +Signed-off-by: Prasad J Pandit +Message-Id: <20170424120634.12268-1-ppandit@redhat.com> +Signed-off-by: Paolo Bonzini +(cherry picked from commit 24dfa9fa2f90a95ac33c7372de4f4f2c8a2c141f) +--- + hw/scsi/megasas.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c +index 84b8caf901..804122ab05 100644 +--- a/hw/scsi/megasas.c ++++ b/hw/scsi/megasas.c +@@ -2138,15 +2138,15 @@ static void megasas_mmio_write(void *opaque, hwaddr addr, + case MFI_SEQ: + trace_megasas_mmio_writel("MFI_SEQ", val); + /* Magic sequence to start ADP reset */ +- if (adp_reset_seq[s->adp_reset] == val) { +- s->adp_reset++; ++ if (adp_reset_seq[s->adp_reset++] == val) { ++ if (s->adp_reset == 6) { ++ s->adp_reset = 0; ++ s->diag = MFI_DIAG_WRITE_ENABLE; ++ } + } else { + s->adp_reset = 0; + s->diag = 0; + } +- if (s->adp_reset == 6) { +- s->diag = MFI_DIAG_WRITE_ENABLE; +- } + break; + case MFI_DIAG: + trace_megasas_mmio_writel("MFI_DIAG", val); diff --git a/0005-9pfs-local-forbid-client-access-to-metadata-CVE-2017.patch b/0005-9pfs-local-forbid-client-access-to-metadata-CVE-2017.patch new file mode 100644 index 0000000..c8af0b5 --- /dev/null +++ b/0005-9pfs-local-forbid-client-access-to-metadata-CVE-2017.patch @@ -0,0 +1,171 @@ +From: Greg Kurz +Date: Fri, 5 May 2017 14:48:08 +0200 +Subject: [PATCH] 9pfs: local: forbid client access to metadata (CVE-2017-7493) + +When using the mapped-file security mode, we shouldn't let the client mess +with the metadata. The current code already tries to hide the metadata dir +from the client by skipping it in local_readdir(). But the client can still +access or modify it through several other operations. This can be used to +escalate privileges in the guest. + +Affected backend operations are: +- local_mknod() +- local_mkdir() +- local_open2() +- local_symlink() +- local_link() +- local_unlinkat() +- local_renameat() +- local_rename() +- local_name_to_path() + +Other operations are safe because they are only passed a fid path, which +is computed internally in local_name_to_path(). + +This patch converts all the functions listed above to fail and return +EINVAL when being passed the name of the metadata dir. This may look +like a poor choice for errno, but there's no such thing as an illegal +path name on Linux and I could not think of anything better. + +This fixes CVE-2017-7493. + +Reported-by: Leo Gaspard +Signed-off-by: Greg Kurz +Reviewed-by: Eric Blake +(cherry picked from commit 7a95434e0ca8a037fd8aa1a2e2461f92585eb77b) +--- + hw/9pfs/9p-local.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 56 insertions(+), 2 deletions(-) + +diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c +index f3ebca4f7a..a2486566af 100644 +--- a/hw/9pfs/9p-local.c ++++ b/hw/9pfs/9p-local.c +@@ -452,6 +452,11 @@ static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs) + return telldir(fs->dir.stream); + } + ++static bool local_is_mapped_file_metadata(FsContext *fs_ctx, const char *name) ++{ ++ return !strcmp(name, VIRTFS_META_DIR); ++} ++ + static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs) + { + struct dirent *entry; +@@ -465,8 +470,8 @@ again: + if (ctx->export_flags & V9FS_SM_MAPPED) { + entry->d_type = DT_UNKNOWN; + } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { +- if (!strcmp(entry->d_name, VIRTFS_META_DIR)) { +- /* skp the meta data directory */ ++ if (local_is_mapped_file_metadata(ctx, entry->d_name)) { ++ /* skip the meta data directory */ + goto again; + } + entry->d_type = DT_UNKNOWN; +@@ -559,6 +564,12 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, + int err = -1; + int dirfd; + ++ if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && ++ local_is_mapped_file_metadata(fs_ctx, name)) { ++ errno = EINVAL; ++ return -1; ++ } ++ + dirfd = local_opendir_nofollow(fs_ctx, dir_path->data); + if (dirfd == -1) { + return -1; +@@ -605,6 +616,12 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, + int err = -1; + int dirfd; + ++ if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && ++ local_is_mapped_file_metadata(fs_ctx, name)) { ++ errno = EINVAL; ++ return -1; ++ } ++ + dirfd = local_opendir_nofollow(fs_ctx, dir_path->data); + if (dirfd == -1) { + return -1; +@@ -694,6 +711,12 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, + int err = -1; + int dirfd; + ++ if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && ++ local_is_mapped_file_metadata(fs_ctx, name)) { ++ errno = EINVAL; ++ return -1; ++ } ++ + /* + * Mark all the open to not follow symlinks + */ +@@ -752,6 +775,12 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath, + int err = -1; + int dirfd; + ++ if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && ++ local_is_mapped_file_metadata(fs_ctx, name)) { ++ errno = EINVAL; ++ return -1; ++ } ++ + dirfd = local_opendir_nofollow(fs_ctx, dir_path->data); + if (dirfd == -1) { + return -1; +@@ -826,6 +855,12 @@ static int local_link(FsContext *ctx, V9fsPath *oldpath, + int ret = -1; + int odirfd, ndirfd; + ++ if (ctx->export_flags & V9FS_SM_MAPPED_FILE && ++ local_is_mapped_file_metadata(ctx, name)) { ++ errno = EINVAL; ++ return -1; ++ } ++ + odirfd = local_opendir_nofollow(ctx, odirpath); + if (odirfd == -1) { + goto out; +@@ -1096,6 +1131,12 @@ static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path, + static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path, + const char *name, V9fsPath *target) + { ++ if (ctx->export_flags & V9FS_SM_MAPPED_FILE && ++ local_is_mapped_file_metadata(ctx, name)) { ++ errno = EINVAL; ++ return -1; ++ } ++ + if (dir_path) { + v9fs_path_sprintf(target, "%s/%s", dir_path->data, name); + } else if (strcmp(name, "/")) { +@@ -1116,6 +1157,13 @@ static int local_renameat(FsContext *ctx, V9fsPath *olddir, + int ret; + int odirfd, ndirfd; + ++ if (ctx->export_flags & V9FS_SM_MAPPED_FILE && ++ (local_is_mapped_file_metadata(ctx, old_name) || ++ local_is_mapped_file_metadata(ctx, new_name))) { ++ errno = EINVAL; ++ return -1; ++ } ++ + odirfd = local_opendir_nofollow(ctx, olddir->data); + if (odirfd == -1) { + return -1; +@@ -1206,6 +1254,12 @@ static int local_unlinkat(FsContext *ctx, V9fsPath *dir, + int ret; + int dirfd; + ++ if (ctx->export_flags & V9FS_SM_MAPPED_FILE && ++ local_is_mapped_file_metadata(ctx, name)) { ++ errno = EINVAL; ++ return -1; ++ } ++ + dirfd = local_opendir_nofollow(ctx, dir->data); + if (dirfd == -1) { + return -1; diff --git a/0006-megasas-do-not-read-sense-length-more-than-once-from.patch b/0006-megasas-do-not-read-sense-length-more-than-once-from.patch new file mode 100644 index 0000000..b2a2e6d --- /dev/null +++ b/0006-megasas-do-not-read-sense-length-more-than-once-from.patch @@ -0,0 +1,30 @@ +From: Paolo Bonzini +Date: Thu, 1 Jun 2017 17:18:39 +0200 +Subject: [PATCH] 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 +(cherry picked from commit 134550bf81a026e18cf58b81e2c2cceaf516f92e) +--- + hw/scsi/megasas.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +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); diff --git a/0007-megasas-do-not-read-iovec-count-more-than-once-from-.patch b/0007-megasas-do-not-read-iovec-count-more-than-once-from-.patch new file mode 100644 index 0000000..62eda4d --- /dev/null +++ b/0007-megasas-do-not-read-iovec-count-more-than-once-from-.patch @@ -0,0 +1,37 @@ +From: Paolo Bonzini +Date: Thu, 1 Jun 2017 17:18:57 +0200 +Subject: [PATCH] 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 +(cherry picked from commit 24c0c77af515acbf0f9705e8096f33ef24d37430) +--- + hw/scsi/megasas.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +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; + } diff --git a/0008-megasas-do-not-read-DCMD-opcode-more-than-once-from-.patch b/0008-megasas-do-not-read-DCMD-opcode-more-than-once-from-.patch new file mode 100644 index 0000000..816b86b --- /dev/null +++ b/0008-megasas-do-not-read-DCMD-opcode-more-than-once-from-.patch @@ -0,0 +1,111 @@ +From: Paolo Bonzini +Date: Thu, 1 Jun 2017 17:18:23 +0200 +Subject: [PATCH] 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 +(cherry picked from commit 5104fac8539eaf155fc6de93e164be43e1e62242) +--- + hw/scsi/megasas.c | 25 +++++++++++-------------- + 1 file changed, 11 insertions(+), 14 deletions(-) + +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) { diff --git a/0009-megasas-do-not-read-command-more-than-once-from-fram.patch b/0009-megasas-do-not-read-command-more-than-once-from-fram.patch new file mode 100644 index 0000000..82e7e25 --- /dev/null +++ b/0009-megasas-do-not-read-command-more-than-once-from-fram.patch @@ -0,0 +1,218 @@ +From: Paolo Bonzini +Date: Thu, 1 Jun 2017 17:23:13 +0200 +Subject: [PATCH] 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 +(cherry picked from commit 36c327a69d723571f02a7691631667cdb1865ee1) +--- + hw/scsi/megasas.c | 60 +++++++++++++++++++++++-------------------------------- + 1 file changed, 25 insertions(+), 35 deletions(-) + +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; + } diff --git a/0010-megasas-do-not-read-SCSI-req-parameters-more-than-on.patch b/0010-megasas-do-not-read-SCSI-req-parameters-more-than-on.patch new file mode 100644 index 0000000..b3a606c --- /dev/null +++ b/0010-megasas-do-not-read-SCSI-req-parameters-more-than-on.patch @@ -0,0 +1,145 @@ +From: Paolo Bonzini +Date: Thu, 1 Jun 2017 17:25:03 +0200 +Subject: [PATCH] megasas: do not read SCSI req parameters more than once from + frame + +Signed-off-by: Paolo Bonzini +(cherry picked from commit b356807fcdfc45583c437f761fc579ab2a8eab11) +--- + hw/scsi/megasas.c | 60 ++++++++++++++++++++++++------------------------------- + 1 file changed, 26 insertions(+), 34 deletions(-) + +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++; diff --git a/0011-megasas-always-store-SCSIRequest-into-MegasasCmd.patch b/0011-megasas-always-store-SCSIRequest-into-MegasasCmd.patch new file mode 100644 index 0000000..3a74932 --- /dev/null +++ b/0011-megasas-always-store-SCSIRequest-into-MegasasCmd.patch @@ -0,0 +1,122 @@ +From: Paolo Bonzini +Date: Thu, 1 Jun 2017 17:26:14 +0200 +Subject: [PATCH] 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 +(cherry picked from commit 87e459a810d7b1ec1638085b5a80ea3d9b43119a) +--- + hw/scsi/megasas.c | 31 ++++++++++++++++--------------- + 1 file changed, 16 insertions(+), 15 deletions(-) + +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 + */ diff --git a/0101-usb-redir-fix-stack-overflow-in-usbredir_log_data.patch b/0101-usb-redir-fix-stack-overflow-in-usbredir_log_data.patch new file mode 100644 index 0000000..5101ab0 --- /dev/null +++ b/0101-usb-redir-fix-stack-overflow-in-usbredir_log_data.patch @@ -0,0 +1,47 @@ +From: Gerd Hoffmann +Date: Tue, 9 May 2017 13:01:28 +0200 +Subject: [PATCH] usb-redir: fix stack overflow in usbredir_log_data +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Don't reinvent a broken wheel, just use the hexdump function we have. + +Impact: low, broken code doesn't run unless you have debug logging +enabled. + +Reported-by: 李强 +Signed-off-by: Gerd Hoffmann +Message-id: 20170509110128.27261-1-kraxel@redhat.com +(cherry picked from commit bd4a683505b27adc1ac809f71e918e58573d851d) +--- + hw/usb/redirect.c | 13 +------------ + 1 file changed, 1 insertion(+), 12 deletions(-) + +diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c +index 0efe62f725..eb70dc7218 100644 +--- a/hw/usb/redirect.c ++++ b/hw/usb/redirect.c +@@ -229,21 +229,10 @@ static void usbredir_log(void *priv, int level, const char *msg) + static void usbredir_log_data(USBRedirDevice *dev, const char *desc, + const uint8_t *data, int len) + { +- int i, j, n; +- + if (dev->debug < usbredirparser_debug_data) { + return; + } +- +- for (i = 0; i < len; i += j) { +- char buf[128]; +- +- n = sprintf(buf, "%s", desc); +- for (j = 0; j < 8 && i + j < len; j++) { +- n += sprintf(buf + n, " %02X", data[i + j]); +- } +- error_report("%s", buf); +- } ++ qemu_hexdump((char *)data, stderr, desc, len); + } + + /* diff --git a/0102-nbd-Fully-initialize-client-in-case-of-failed-negoti.patch b/0102-nbd-Fully-initialize-client-in-case-of-failed-negoti.patch new file mode 100644 index 0000000..3ec2c28 --- /dev/null +++ b/0102-nbd-Fully-initialize-client-in-case-of-failed-negoti.patch @@ -0,0 +1,77 @@ +From: Eric Blake +Date: Fri, 26 May 2017 22:04:21 -0500 +Subject: [PATCH] nbd: Fully initialize client in case of failed negotiation + +If a non-NBD client connects to qemu-nbd, we would end up with +a SIGSEGV in nbd_client_put() because we were trying to +unregister the client's association to the export, even though +we skipped inserting the client into that list. Easy trigger +in two terminals: + +$ qemu-nbd -p 30001 --format=raw file +$ nmap 127.0.0.1 -p 30001 + +nmap claims that it thinks it connected to a pago-services1 +server (which probably means nmap could be updated to learn the +NBD protocol and give a more accurate diagnosis of the open +port - but that's not our problem), then terminates immediately, +so our call to nbd_negotiate() fails. The fix is to reorder +nbd_co_client_start() to ensure that all initialization occurs +before we ever try talking to a client in nbd_negotiate(), so +that the teardown sequence on negotiation failure doesn't fault +while dereferencing a half-initialized object. + +While debugging this, I also noticed that nbd_update_server_watch() +called by nbd_client_closed() was still adding a channel to accept +the next client, even when the state was no longer RUNNING. That +is fixed by making nbd_can_accept() pay attention to the current +state. + +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1451614 + +Signed-off-by: Eric Blake +Message-Id: <20170527030421.28366-1-eblake@redhat.com> +Signed-off-by: Paolo Bonzini +(cherry picked from commit df8ad9f128c15aa0a0ebc7b24e9a22c9775b67af) +--- + nbd/server.c | 8 +++----- + qemu-nbd.c | 2 +- + 2 files changed, 4 insertions(+), 6 deletions(-) + +diff --git a/nbd/server.c b/nbd/server.c +index 924a1fe2db..edfda84d43 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -1376,16 +1376,14 @@ static coroutine_fn void nbd_co_client_start(void *opaque) + + if (exp) { + nbd_export_get(exp); ++ QTAILQ_INSERT_TAIL(&exp->clients, client, next); + } ++ qemu_co_mutex_init(&client->send_lock); ++ + if (nbd_negotiate(data)) { + client_close(client); + goto out; + } +- qemu_co_mutex_init(&client->send_lock); +- +- if (exp) { +- QTAILQ_INSERT_TAIL(&exp->clients, client, next); +- } + + nbd_client_receive_next_request(client); + +diff --git a/qemu-nbd.c b/qemu-nbd.c +index e080fb7c75..b44764eb87 100644 +--- a/qemu-nbd.c ++++ b/qemu-nbd.c +@@ -324,7 +324,7 @@ out: + + static int nbd_can_accept(void) + { +- return nb_fds < shared; ++ return state == RUNNING && nb_fds < shared; + } + + static void nbd_export_closed(NBDExport *exp) diff --git a/0103-nbd-Fix-regression-on-resiliency-to-port-scan.patch b/0103-nbd-Fix-regression-on-resiliency-to-port-scan.patch new file mode 100644 index 0000000..9d6eb73 --- /dev/null +++ b/0103-nbd-Fix-regression-on-resiliency-to-port-scan.patch @@ -0,0 +1,194 @@ +From: Eric Blake +Date: Thu, 8 Jun 2017 17:26:17 -0500 +Subject: [PATCH] nbd: Fix regression on resiliency to port scan + +Back in qemu 2.5, qemu-nbd was immune to port probes (a transient +server would not quit, regardless of how many probe connections +came and went, until a connection actually negotiated). But we +broke that in commit ee7d7aa when removing the return value to +nbd_client_new(), although that patch also introduced a bug causing +an assertion failure on a client that fails negotiation. We then +made it worse during refactoring in commit 1a6245a (a segfault +before we could even assert); the (masked) assertion was cleaned +up in d3780c2 (still in 2.6), and just recently we finally fixed +the segfault ("nbd: Fully intialize client in case of failed +negotiation"). But that still means that ever since we added +TLS support to qemu-nbd, we have been vulnerable to an ill-timed +port-scan being able to cause a denial of service by taking down +qemu-nbd before a real client has a chance to connect. + +Since negotiation is now handled asynchronously via coroutines, +we no longer have a synchronous point of return by re-adding a +return value to nbd_client_new(). So this patch instead wires +things up to pass the negotiation status through the close_fn +callback function. + +Simple test across two terminals: +$ qemu-nbd -f raw -p 30001 file +$ nmap 127.0.0.1 -p 30001 && \ + qemu-io -c 'r 0 512' -f raw nbd://localhost:30001 + +Note that this patch does not change what constitutes successful +negotiation (thus, a client must enter transmission phase before +that client can be considered as a reason to terminate the server +when the connection ends). Perhaps we may want to tweak things +in a later patch to also treat a client that uses NBD_OPT_ABORT +as being a 'successful' negotiation (the client correctly talked +the NBD protocol, and informed us it was not going to use our +export after all), but that's a discussion for another day. + +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1451614 + +Signed-off-by: Eric Blake +Message-Id: <20170608222617.20376-1-eblake@redhat.com> +Signed-off-by: Paolo Bonzini +(cherry picked from commit 0c9390d978cbf61e8f16c9f580fa96b305c43568) +--- + blockdev-nbd.c | 6 +++++- + include/block/nbd.h | 2 +- + nbd/server.c | 24 +++++++++++++++--------- + qemu-nbd.c | 4 ++-- + 4 files changed, 23 insertions(+), 13 deletions(-) + +diff --git a/blockdev-nbd.c b/blockdev-nbd.c +index 8a11807df3..8d7284ac56 100644 +--- a/blockdev-nbd.c ++++ b/blockdev-nbd.c +@@ -27,6 +27,10 @@ typedef struct NBDServerData { + + static NBDServerData *nbd_server; + ++static void nbd_blockdev_client_closed(NBDClient *client, bool ignored) ++{ ++ nbd_client_put(client); ++} + + static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition, + gpointer opaque) +@@ -46,7 +50,7 @@ static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition, + qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server"); + nbd_client_new(NULL, cioc, + nbd_server->tlscreds, NULL, +- nbd_client_put); ++ nbd_blockdev_client_closed); + object_unref(OBJECT(cioc)); + return TRUE; + } +diff --git a/include/block/nbd.h b/include/block/nbd.h +index 3e373f0498..b69c30d063 100644 +--- a/include/block/nbd.h ++++ b/include/block/nbd.h +@@ -160,7 +160,7 @@ void nbd_client_new(NBDExport *exp, + QIOChannelSocket *sioc, + QCryptoTLSCreds *tlscreds, + const char *tlsaclname, +- void (*close)(NBDClient *)); ++ void (*close_fn)(NBDClient *, bool)); + void nbd_client_get(NBDClient *client); + void nbd_client_put(NBDClient *client); + +diff --git a/nbd/server.c b/nbd/server.c +index edfda84d43..a98bb21a0a 100644 +--- a/nbd/server.c ++++ b/nbd/server.c +@@ -81,7 +81,7 @@ static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); + + struct NBDClient { + int refcount; +- void (*close)(NBDClient *client); ++ void (*close_fn)(NBDClient *client, bool negotiated); + + bool no_zeroes; + NBDExport *exp; +@@ -796,7 +796,7 @@ void nbd_client_put(NBDClient *client) + } + } + +-static void client_close(NBDClient *client) ++static void client_close(NBDClient *client, bool negotiated) + { + if (client->closing) { + return; +@@ -811,8 +811,8 @@ static void client_close(NBDClient *client) + NULL); + + /* Also tell the client, so that they release their reference. */ +- if (client->close) { +- client->close(client); ++ if (client->close_fn) { ++ client->close_fn(client, negotiated); + } + } + +@@ -993,7 +993,7 @@ void nbd_export_close(NBDExport *exp) + + nbd_export_get(exp); + QTAILQ_FOREACH_SAFE(client, &exp->clients, next, next) { +- client_close(client); ++ client_close(client, true); + } + nbd_export_set_name(exp, NULL); + nbd_export_set_description(exp, NULL); +@@ -1355,7 +1355,7 @@ done: + + out: + nbd_request_put(req); +- client_close(client); ++ client_close(client, true); + nbd_client_put(client); + } + +@@ -1381,7 +1381,7 @@ static coroutine_fn void nbd_co_client_start(void *opaque) + qemu_co_mutex_init(&client->send_lock); + + if (nbd_negotiate(data)) { +- client_close(client); ++ client_close(client, false); + goto out; + } + +@@ -1391,11 +1391,17 @@ out: + g_free(data); + } + ++/* ++ * Create a new client listener on the given export @exp, using the ++ * given channel @sioc. Begin servicing it in a coroutine. When the ++ * connection closes, call @close_fn with an indication of whether the ++ * client completed negotiation. ++ */ + void nbd_client_new(NBDExport *exp, + QIOChannelSocket *sioc, + QCryptoTLSCreds *tlscreds, + const char *tlsaclname, +- void (*close_fn)(NBDClient *)) ++ void (*close_fn)(NBDClient *, bool)) + { + NBDClient *client; + NBDClientNewData *data = g_new(NBDClientNewData, 1); +@@ -1412,7 +1418,7 @@ void nbd_client_new(NBDExport *exp, + object_ref(OBJECT(client->sioc)); + client->ioc = QIO_CHANNEL(sioc); + object_ref(OBJECT(client->ioc)); +- client->close = close_fn; ++ client->close_fn = close_fn; + + data->client = client; + data->co = qemu_coroutine_create(nbd_co_client_start, data); +diff --git a/qemu-nbd.c b/qemu-nbd.c +index b44764eb87..483dd77a77 100644 +--- a/qemu-nbd.c ++++ b/qemu-nbd.c +@@ -335,10 +335,10 @@ static void nbd_export_closed(NBDExport *exp) + + static void nbd_update_server_watch(void); + +-static void nbd_client_closed(NBDClient *client) ++static void nbd_client_closed(NBDClient *client, bool negotiated) + { + nb_fds--; +- if (nb_fds == 0 && !persistent && state == RUNNING) { ++ if (negotiated && nb_fds == 0 && !persistent && state == RUNNING) { + state = TERMINATE; + } + nbd_update_server_watch(); diff --git a/0104-qemu-nbd-Ignore-SIGPIPE.patch b/0104-qemu-nbd-Ignore-SIGPIPE.patch new file mode 100644 index 0000000..40b5737 --- /dev/null +++ b/0104-qemu-nbd-Ignore-SIGPIPE.patch @@ -0,0 +1,44 @@ +From: Max Reitz +Date: Sun, 11 Jun 2017 14:37:14 +0200 +Subject: [PATCH] qemu-nbd: Ignore SIGPIPE + +qemu proper has done so for 13 years +(8a7ddc38a60648257dc0645ab4a05b33d6040063), qemu-img and qemu-io have +done so for four years (526eda14a68d5b3596be715505289b541288ef2a). +Ignoring this signal is especially important in qemu-nbd because +otherwise a client can easily take down the qemu-nbd server by dropping +the connection when the server wants to send something, for example: + +$ qemu-nbd -x foo -f raw -t null-co:// & +[1] 12726 +$ qemu-io -c quit nbd://localhost/bar +can't open device nbd://localhost/bar: No export with name 'bar' available +[1] + 12726 broken pipe qemu-nbd -x foo -f raw -t null-co:// + +In this case, the client sends an NBD_OPT_ABORT and closes the +connection (because it is not required to wait for a reply), but the +server replies with an NBD_REP_ACK (because it is required to reply). + +Signed-off-by: Max Reitz +Message-Id: <20170611123714.31292-1-mreitz@redhat.com> +Signed-off-by: Paolo Bonzini +(cherry picked from commit 041e32b8d9d076980b4e35317c0339e57ab888f1) +--- + qemu-nbd.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/qemu-nbd.c b/qemu-nbd.c +index 483dd77a77..5deb37e03e 100644 +--- a/qemu-nbd.c ++++ b/qemu-nbd.c +@@ -581,6 +581,10 @@ int main(int argc, char **argv) + sa_sigterm.sa_handler = termsig_handler; + sigaction(SIGTERM, &sa_sigterm, NULL); + ++#ifdef CONFIG_POSIX ++ signal(SIGPIPE, SIG_IGN); ++#endif ++ + module_call_init(MODULE_INIT_TRACE); + qcrypto_init(&error_fatal); + diff --git a/qemu.spec b/qemu.spec index 22060e6..a3d0237 100644 --- a/qemu.spec +++ b/qemu.spec @@ -92,7 +92,7 @@ Requires: %{name}-block-ssh = %{epoch}:%{version}-%{release} Summary: QEMU is a FAST! processor emulator Name: qemu Version: 2.9.0 -Release: 2%{?rcrel}%{?dist} +Release: 3%{?rcrel}%{?dist} Epoch: 2 License: GPLv2+ and LGPLv2+ and BSD Group: Development/Tools @@ -126,6 +126,36 @@ Source21: 50-kvm-s390x.conf # /etc/security/limits.d/95-kvm-ppc64-memlock.conf Source22: 95-kvm-ppc64-memlock.conf +# CVE-2017-8112: vmw_pvscsi: infinite loop in pvscsi_log2 (bz #1445622) +Patch0001: 0001-vmw_pvscsi-check-message-ring-page-count-at-initiali.patch +# CVE-2017-8309: audio: host memory lekage via capture buffer (bz #1446520) +Patch0002: 0002-audio-release-capture-buffers.patch +# CVE-2017-8379: input: host memory lekage via keyboard events (bz #1446560) +Patch0003: 0003-input-limit-kbd-queue-depth.patch +# CVE-2017-8380: scsi: megasas: out-of-bounds read in megasas_mmio_write (bz +# #1446578) +Patch0004: 0004-scsi-avoid-an-off-by-one-error-in-megasas_mmio_write.patch +# CVE-2017-7493: 9pfs: guest privilege escalation in virtfs mapped-file mode +# (bz #1451711) +Patch0005: 0005-9pfs-local-forbid-client-access-to-metadata-CVE-2017.patch +# CVE-2017-9503: megasas: null pointer dereference while processing megasas +# command (bz #1459478) +Patch0006: 0006-megasas-do-not-read-sense-length-more-than-once-from.patch +Patch0007: 0007-megasas-do-not-read-iovec-count-more-than-once-from-.patch +Patch0008: 0008-megasas-do-not-read-DCMD-opcode-more-than-once-from-.patch +Patch0009: 0009-megasas-do-not-read-command-more-than-once-from-fram.patch +Patch0010: 0010-megasas-do-not-read-SCSI-req-parameters-more-than-on.patch +Patch0011: 0011-megasas-always-store-SCSIRequest-into-MegasasCmd.patch + +# CVE-2017-10806: usb-redirect: stack buffer overflow in debug logging (bz +# #1468497) +Patch0101: 0101-usb-redir-fix-stack-overflow-in-usbredir_log_data.patch +# CVE-2017-9524: nbd: segfault due to client non-negotiation (bz #1460172) +Patch0102: 0102-nbd-Fully-initialize-client-in-case-of-failed-negoti.patch +Patch0103: 0103-nbd-Fix-regression-on-resiliency-to-port-scan.patch +# CVE-2017-10664: qemu-nbd: server breaks with SIGPIPE upon client abort (bz +# #1466192) +Patch0104: 0104-qemu-nbd-Ignore-SIGPIPE.patch # documentation deps BuildRequires: texinfo @@ -2000,6 +2030,22 @@ getent passwd qemu >/dev/null || \ %changelog +* Wed Jul 12 2017 Cole Robinson - 2:2.9.0-3 +- CVE-2017-8112: vmw_pvscsi: infinite loop in pvscsi_log2 (bz #1445622) +- CVE-2017-8309: audio: host memory lekage via capture buffer (bz #1446520) +- CVE-2017-8379: input: host memory lekage via keyboard events (bz #1446560) +- CVE-2017-8380: scsi: megasas: out-of-bounds read in megasas_mmio_write (bz + #1446578) +- CVE-2017-7493: 9pfs: guest privilege escalation in virtfs mapped-file mode + (bz #1451711) +- CVE-2017-9503: megasas: null pointer dereference while processing megasas + command (bz #1459478) +- CVE-2017-10806: usb-redirect: stack buffer overflow in debug logging (bz + #1468497) +- CVE-2017-9524: nbd: segfault due to client non-negotiation (bz #1460172) +- CVE-2017-10664: qemu-nbd: server breaks with SIGPIPE upon client abort (bz + #1466192) + * Mon May 22 2017 Richard W.M. Jones - 2:2.9.0-2 - Bump release and rebuild to try to fix _ZdlPvm symbol (see RHBZ#1452813).