merge CVE-2017-17381 fix and backup race condition fix
* CVE-2017-17381: virtio: divide by zero exception while updating rings * race condition when issuing a 'backup-stop' command Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
		
							parent
							
								
									d2494dd2ef
								
							
						
					
					
						commit
						c25a222062
					
				
							
								
								
									
										68
									
								
								debian/patches/extra/0024-virtio-check-VirtQueue-Vring-object-is-set.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								debian/patches/extra/0024-virtio-check-VirtQueue-Vring-object-is-set.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,68 @@ | ||||
| From 537048fe17ab94242908536adcb638ec274a3f53 Mon Sep 17 00:00:00 2001 | ||||
| From: Prasad J Pandit <pjp@fedoraproject.org> | ||||
| Date: Wed, 29 Nov 2017 23:14:27 +0530 | ||||
| Subject: [PATCH 1/2] virtio: check VirtQueue Vring object is set | ||||
| 
 | ||||
| A guest could attempt to use an uninitialised VirtQueue object | ||||
| or unset Vring.align leading to a arithmetic exception. Add check | ||||
| to avoid it. | ||||
| 
 | ||||
| Reported-by: Zhangboxian <zhangboxian@huawei.com> | ||||
| Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org> | ||||
| Reviewed-by: Michael S. Tsirkin <mst@redhat.com> | ||||
| Signed-off-by: Michael S. Tsirkin <mst@redhat.com> | ||||
| Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| Reviewed-by: Cornelia Huck <cohuck@redhat.com> | ||||
| ---
 | ||||
|  hw/virtio/virtio.c | 14 +++++++++++--- | ||||
|  1 file changed, 11 insertions(+), 3 deletions(-) | ||||
| 
 | ||||
| diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
 | ||||
| index 33bb770177..76b9a9907c 100644
 | ||||
| --- a/hw/virtio/virtio.c
 | ||||
| +++ b/hw/virtio/virtio.c
 | ||||
| @@ -183,7 +183,7 @@ void virtio_queue_update_rings(VirtIODevice *vdev, int n)
 | ||||
|  { | ||||
|      VRing *vring = &vdev->vq[n].vring; | ||||
|   | ||||
| -    if (!vring->desc) {
 | ||||
| +    if (!vring->num || !vring->desc || !vring->align) {
 | ||||
|          /* not yet setup -> nothing to do */ | ||||
|          return; | ||||
|      } | ||||
| @@ -1416,6 +1416,9 @@ void virtio_config_modern_writel(VirtIODevice *vdev,
 | ||||
|   | ||||
|  void virtio_queue_set_addr(VirtIODevice *vdev, int n, hwaddr addr) | ||||
|  { | ||||
| +    if (!vdev->vq[n].vring.num) {
 | ||||
| +        return;
 | ||||
| +    }
 | ||||
|      vdev->vq[n].vring.desc = addr; | ||||
|      virtio_queue_update_rings(vdev, n); | ||||
|  } | ||||
| @@ -1428,6 +1431,9 @@ hwaddr virtio_queue_get_addr(VirtIODevice *vdev, int n)
 | ||||
|  void virtio_queue_set_rings(VirtIODevice *vdev, int n, hwaddr desc, | ||||
|                              hwaddr avail, hwaddr used) | ||||
|  { | ||||
| +    if (!vdev->vq[n].vring.num) {
 | ||||
| +        return;
 | ||||
| +    }
 | ||||
|      vdev->vq[n].vring.desc = desc; | ||||
|      vdev->vq[n].vring.avail = avail; | ||||
|      vdev->vq[n].vring.used = used; | ||||
| @@ -1496,8 +1502,10 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, int align)
 | ||||
|       */ | ||||
|      assert(k->has_variable_vring_alignment); | ||||
|   | ||||
| -    vdev->vq[n].vring.align = align;
 | ||||
| -    virtio_queue_update_rings(vdev, n);
 | ||||
| +    if (align) {
 | ||||
| +        vdev->vq[n].vring.align = align;
 | ||||
| +        virtio_queue_update_rings(vdev, n);
 | ||||
| +    }
 | ||||
|  } | ||||
|   | ||||
|  static bool virtio_queue_notify_aio_vq(VirtQueue *vq) | ||||
| -- 
 | ||||
| 2.11.0 | ||||
| 
 | ||||
							
								
								
									
										253
									
								
								debian/patches/pve/0029-backup-fix-race-in-backup-stop-command.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										253
									
								
								debian/patches/pve/0029-backup-fix-race-in-backup-stop-command.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,253 @@ | ||||
| From 3cff3cff0dda09201386dff3c86c5e4fdb0b41b7 Mon Sep 17 00:00:00 2001 | ||||
| From: Wolfgang Bumiller <w.bumiller@proxmox.com> | ||||
| Date: Tue, 5 Dec 2017 12:12:15 +0100 | ||||
| Subject: [PATCH 2/2] backup: fix race in backup-stop command | ||||
| 
 | ||||
| Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com> | ||||
| ---
 | ||||
|  blockdev.c | 90 +++++++++++++++++++++++++++++++++----------------------------- | ||||
|  blockjob.c |  8 +++--- | ||||
|  2 files changed, 52 insertions(+), 46 deletions(-) | ||||
| 
 | ||||
| diff --git a/blockdev.c b/blockdev.c
 | ||||
| index 19a82e8774..d3490936d8 100644
 | ||||
| --- a/blockdev.c
 | ||||
| +++ b/blockdev.c
 | ||||
| @@ -2934,31 +2934,6 @@ out:
 | ||||
|      aio_context_release(aio_context); | ||||
|  } | ||||
|   | ||||
| -void block_job_event_cancelled(BlockJob *job);
 | ||||
| -void block_job_event_completed(BlockJob *job, const char *msg);
 | ||||
| -static void block_job_cb(void *opaque, int ret)
 | ||||
| -{
 | ||||
| -    /* Note that this function may be executed from another AioContext besides
 | ||||
| -     * the QEMU main loop.  If you need to access anything that assumes the
 | ||||
| -     * QEMU global mutex, use a BH or introduce a mutex.
 | ||||
| -     */
 | ||||
| -
 | ||||
| -    BlockDriverState *bs = opaque;
 | ||||
| -    const char *msg = NULL;
 | ||||
| -
 | ||||
| -    assert(bs->job);
 | ||||
| -
 | ||||
| -    if (ret < 0) {
 | ||||
| -        msg = strerror(-ret);
 | ||||
| -    }
 | ||||
| -
 | ||||
| -    if (block_job_is_cancelled(bs->job)) {
 | ||||
| -        block_job_event_cancelled(bs->job);
 | ||||
| -    } else {
 | ||||
| -        block_job_event_completed(bs->job, msg);
 | ||||
| -    }
 | ||||
| -}
 | ||||
| -
 | ||||
|  /* PVE backup related function */ | ||||
|   | ||||
|  static struct PVEBackupState { | ||||
| @@ -2975,6 +2950,8 @@ static struct PVEBackupState {
 | ||||
|      size_t total; | ||||
|      size_t transferred; | ||||
|      size_t zero_bytes; | ||||
| +    QemuMutex backup_mutex;
 | ||||
| +    bool      backup_mutex_initialized;
 | ||||
|  } backup_state; | ||||
|   | ||||
|  typedef struct PVEBackupDevInfo { | ||||
| @@ -3055,6 +3032,13 @@ static int pvebackup_dump_cb(void *opaque, BlockBackend *target,
 | ||||
|   | ||||
|  static void pvebackup_cleanup(void) | ||||
|  { | ||||
| +    qemu_mutex_lock(&backup_state.backup_mutex);
 | ||||
| +    // Avoid race between block jobs and backup-cancel command:
 | ||||
| +    if (!backup_state.vmaw) {
 | ||||
| +        qemu_mutex_unlock(&backup_state.backup_mutex);
 | ||||
| +        return;
 | ||||
| +    }
 | ||||
| +
 | ||||
|      backup_state.end_time = time(NULL); | ||||
|   | ||||
|      if (backup_state.vmaw) { | ||||
| @@ -3064,16 +3048,9 @@ static void pvebackup_cleanup(void)
 | ||||
|          backup_state.vmaw = NULL; | ||||
|      } | ||||
|   | ||||
| -    if (backup_state.di_list) {
 | ||||
| -        GList *l = backup_state.di_list;
 | ||||
| -        while (l) {
 | ||||
| -            PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
 | ||||
| -            l = g_list_next(l);
 | ||||
| -            g_free(di);
 | ||||
| -        }
 | ||||
| -        g_list_free(backup_state.di_list);
 | ||||
| -        backup_state.di_list = NULL;
 | ||||
| -    }
 | ||||
| +    g_list_free(backup_state.di_list);
 | ||||
| +    backup_state.di_list = NULL;
 | ||||
| +    qemu_mutex_unlock(&backup_state.backup_mutex);
 | ||||
|  } | ||||
|   | ||||
|  static void coroutine_fn backup_close_vma_stream(void *opaque) | ||||
| @@ -3085,6 +3062,8 @@ static void coroutine_fn backup_close_vma_stream(void *opaque)
 | ||||
|   | ||||
|  static void pvebackup_complete_cb(void *opaque, int ret) | ||||
|  { | ||||
| +    // This always runs in the main loop
 | ||||
| +
 | ||||
|      PVEBackupDevInfo *di = opaque; | ||||
|   | ||||
|      di->completed = true; | ||||
| @@ -3094,8 +3073,6 @@ static void pvebackup_complete_cb(void *opaque, int ret)
 | ||||
|                     ret, strerror(-ret)); | ||||
|      } | ||||
|   | ||||
| -    BlockDriverState *bs = di->bs;
 | ||||
| -
 | ||||
|      di->bs = NULL; | ||||
|      di->target = NULL; | ||||
|   | ||||
| @@ -3104,7 +3081,11 @@ static void pvebackup_complete_cb(void *opaque, int ret)
 | ||||
|          qemu_coroutine_enter(co); | ||||
|      } | ||||
|   | ||||
| -    block_job_cb(bs, ret);
 | ||||
| +    // remove self from job queue
 | ||||
| +    qemu_mutex_lock(&backup_state.backup_mutex);
 | ||||
| +    backup_state.di_list = g_list_remove(backup_state.di_list, di);
 | ||||
| +    g_free(di);
 | ||||
| +    qemu_mutex_unlock(&backup_state.backup_mutex);
 | ||||
|   | ||||
|      if (!backup_state.cancel) { | ||||
|          pvebackup_run_next_job(); | ||||
| @@ -3114,6 +3095,12 @@ static void pvebackup_complete_cb(void *opaque, int ret)
 | ||||
|  static void pvebackup_cancel(void *opaque) | ||||
|  { | ||||
|      backup_state.cancel = true; | ||||
| +    qemu_mutex_lock(&backup_state.backup_mutex);
 | ||||
| +    // Avoid race between block jobs and backup-cancel command:
 | ||||
| +    if (!backup_state.vmaw) {
 | ||||
| +        qemu_mutex_unlock(&backup_state.backup_mutex);
 | ||||
| +        return;
 | ||||
| +    }
 | ||||
|   | ||||
|      if (!backup_state.error) { | ||||
|          error_setg(&backup_state.error, "backup cancelled"); | ||||
| @@ -3131,13 +3118,17 @@ static void pvebackup_cancel(void *opaque)
 | ||||
|          if (!di->completed && di->bs) { | ||||
|              BlockJob *job = di->bs->job; | ||||
|              if (job) { | ||||
| +                AioContext *aio_context = blk_get_aio_context(job->blk);
 | ||||
| +                aio_context_acquire(aio_context);
 | ||||
|                  if (!di->completed) { | ||||
| -                    block_job_cancel_sync(job);
 | ||||
| +                    block_job_cancel(job);
 | ||||
|                  } | ||||
| +                aio_context_release(aio_context);
 | ||||
|              } | ||||
|          } | ||||
|      } | ||||
|   | ||||
| +    qemu_mutex_unlock(&backup_state.backup_mutex);
 | ||||
|      pvebackup_cleanup(); | ||||
|  } | ||||
|   | ||||
| @@ -3193,24 +3184,31 @@ static int config_to_vma(const char *file, BackupFormat format,
 | ||||
|  bool block_job_should_pause(BlockJob *job); | ||||
|  static void pvebackup_run_next_job(void) | ||||
|  { | ||||
| +    qemu_mutex_lock(&backup_state.backup_mutex);
 | ||||
| +
 | ||||
|      GList *l = backup_state.di_list; | ||||
|      while (l) { | ||||
|          PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; | ||||
|          l = g_list_next(l); | ||||
|          if (!di->completed && di->bs && di->bs->job) { | ||||
|              BlockJob *job = di->bs->job; | ||||
| +            AioContext *aio_context = blk_get_aio_context(job->blk);
 | ||||
| +            aio_context_acquire(aio_context);
 | ||||
| +            qemu_mutex_unlock(&backup_state.backup_mutex);
 | ||||
|              if (block_job_should_pause(job)) { | ||||
| -                bool cancel = backup_state.error || backup_state.cancel;
 | ||||
| -                if (cancel) {
 | ||||
| -                    block_job_cancel(job);
 | ||||
| +                if (backup_state.error || backup_state.cancel) {
 | ||||
| +                    block_job_cancel_sync(job);
 | ||||
|                  } else { | ||||
|                      block_job_resume(job); | ||||
|                  } | ||||
|              } | ||||
| +            aio_context_release(aio_context);
 | ||||
|              return; | ||||
|          } | ||||
|      } | ||||
| +    qemu_mutex_unlock(&backup_state.backup_mutex);
 | ||||
|   | ||||
| +    // no more jobs, run the cleanup
 | ||||
|      pvebackup_cleanup(); | ||||
|  } | ||||
|   | ||||
| @@ -3233,6 +3231,11 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
 | ||||
|      UuidInfo *uuid_info; | ||||
|      BlockJob *job; | ||||
|   | ||||
| +    if (!backup_state.backup_mutex_initialized) {
 | ||||
| +        qemu_mutex_init(&backup_state.backup_mutex);
 | ||||
| +        backup_state.backup_mutex_initialized = true;
 | ||||
| +    }
 | ||||
| +
 | ||||
|      if (backup_state.di_list) { | ||||
|          error_set(errp, ERROR_CLASS_GENERIC_ERROR, | ||||
|                    "previous backup not finished"); | ||||
| @@ -3405,6 +3408,7 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
 | ||||
|      uuid_copy(backup_state.uuid, uuid); | ||||
|      uuid_unparse_lower(uuid, backup_state.uuid_str); | ||||
|   | ||||
| +    qemu_mutex_lock(&backup_state.backup_mutex);
 | ||||
|      backup_state.di_list = di_list; | ||||
|   | ||||
|      backup_state.total = total; | ||||
| @@ -3428,6 +3432,8 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
 | ||||
|          block_job_start(job); | ||||
|      } | ||||
|   | ||||
| +    qemu_mutex_unlock(&backup_state.backup_mutex);
 | ||||
| +
 | ||||
|      if (!backup_state.error) { | ||||
|          pvebackup_run_next_job(); // run one job | ||||
|      } | ||||
| diff --git a/blockjob.c b/blockjob.c
 | ||||
| index cb3741f6dd..199d5852db 100644
 | ||||
| --- a/blockjob.c
 | ||||
| +++ b/blockjob.c
 | ||||
| @@ -37,8 +37,8 @@
 | ||||
|  #include "qemu/timer.h" | ||||
|  #include "qapi-event.h" | ||||
|   | ||||
| -void block_job_event_cancelled(BlockJob *job);
 | ||||
| -void block_job_event_completed(BlockJob *job, const char *msg);
 | ||||
| +static void block_job_event_cancelled(BlockJob *job);
 | ||||
| +static void block_job_event_completed(BlockJob *job, const char *msg);
 | ||||
|   | ||||
|  /* Transactional group of block jobs */ | ||||
|  struct BlockJobTxn { | ||||
| @@ -688,7 +688,7 @@ static void block_job_iostatus_set_err(BlockJob *job, int error)
 | ||||
|      } | ||||
|  } | ||||
|   | ||||
| -void block_job_event_cancelled(BlockJob *job)
 | ||||
| +static void block_job_event_cancelled(BlockJob *job)
 | ||||
|  { | ||||
|      if (block_job_is_internal(job)) { | ||||
|          return; | ||||
| @@ -702,7 +702,7 @@ void block_job_event_cancelled(BlockJob *job)
 | ||||
|                                          &error_abort); | ||||
|  } | ||||
|   | ||||
| -void block_job_event_completed(BlockJob *job, const char *msg)
 | ||||
| +static void block_job_event_completed(BlockJob *job, const char *msg)
 | ||||
|  { | ||||
|      if (block_job_is_internal(job)) { | ||||
|          return; | ||||
| -- 
 | ||||
| 2.11.0 | ||||
| 
 | ||||
							
								
								
									
										2
									
								
								debian/patches/series
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/patches/series
									
									
									
									
										vendored
									
									
								
							| @ -26,6 +26,7 @@ pve/0025-qemu-img-dd-add-osize-and-read-from-to-stdin-stdout.patch | ||||
| pve/0026-backup-modify-job-api.patch | ||||
| pve/0027-backup-introduce-vma-archive-format.patch | ||||
| pve/0028-adding-old-vma-files.patch | ||||
| pve/0029-backup-fix-race-in-backup-stop-command.patch | ||||
| extra/0001-Revert-target-i386-disable-LINT0-after-reset.patch | ||||
| extra/0002-virtio-serial-fix-segfault-on-disconnect.patch | ||||
| extra/0003-megasas-always-store-SCSIRequest-into-MegasasCmd.patch | ||||
| @ -49,3 +50,4 @@ extra/0020-vga-fix-region-checks-in-wraparound-case.patch | ||||
| extra/0021-io-monitor-encoutput-buffer-size-from-websocket-GSou.patch | ||||
| extra/0022-9pfs-use-g_malloc0-to-allocate-space-for-xattr.patch | ||||
| extra/0023-cirrus-fix-oob-access-in-mode4and5-write-functions.patch | ||||
| extra/0024-virtio-check-VirtQueue-Vring-object-is-set.patch | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Wolfgang Bumiller
						Wolfgang Bumiller