From patchwork Thu Jan 18 16:04:05 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yongji Xie X-Patchwork-Id: 10173641 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id B852660230 for ; Thu, 18 Jan 2018 16:06:24 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id ACF5F27E5A for ; Thu, 18 Jan 2018 16:06:24 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A0E6528174; Thu, 18 Jan 2018 16:06:24 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id EAF9827E5A for ; Thu, 18 Jan 2018 16:06:23 +0000 (UTC) Received: from localhost ([::1]:38982 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ecChq-0003JB-SS for patchwork-qemu-devel@patchwork.kernel.org; Thu, 18 Jan 2018 11:06:22 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:44426) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ecCgn-0002Zm-O9 for qemu-devel@nongnu.org; Thu, 18 Jan 2018 11:05:23 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ecCgi-0004WR-Oy for qemu-devel@nongnu.org; Thu, 18 Jan 2018 11:05:17 -0500 Received: from mail-pg0-x244.google.com ([2607:f8b0:400e:c05::244]:41079) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1ecCgi-0004VV-Gg for qemu-devel@nongnu.org; Thu, 18 Jan 2018 11:05:12 -0500 Received: by mail-pg0-x244.google.com with SMTP id 136so14046725pgd.8 for ; Thu, 18 Jan 2018 08:05:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=esrSewjVAyKeDuksStzDzPc2IEh0N3C64P6LkVKhDHY=; b=RfilMwwL+4IZLvmoffij8fesU4/carIw7p3BiV1S7A0ToUVkBCc5+4SK4jz2ZMkTCH dIRKBxw26YgdB32PN2KXf6qEhJVkKws1lbtMERV+v5qgTTpKel1LJnqPepYNLBdWPohi 8+4lNSu+3SDiBQahmXH/gxlCTEKxGS0rJHD3AwaoS17oQqryhhFnVP1gGzB7FG/RdYdM tX/q9xrswj3v+9aAkfz8KjQD4tlkeDNJDIUn5m0rnXZ23PcrwaBMKWSKIxOnGOhBXxo7 KctcBgrHy+YQAYJBgh9MLA6igoGssDDHHaPYVy9oAKfsJaHWncP4X3AO5Ac7ixiqMz9J CwTg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=esrSewjVAyKeDuksStzDzPc2IEh0N3C64P6LkVKhDHY=; b=lEQazak20K2T4n5su4mvvZOP2nml9zFMRuTVr1y7LIu7U7vlYiNKViK5v5ref0ehQ+ 29V3MggX6n3IQl+laqKs1+Lvgpp3f97l7Xgi+I6pwFPfEQkBNNJGY+aq6Vrytjf0yMTs sSTXcXKbxe6SlnPA29eZ/mPP2cw/JsTzAhDYU8VJx4Pe2BLvRg736QfF0nUWIfpRd3HB 06rFQ9UpQNtGVJQladANsBAOp5E0NB/dHYGNtqHQmX71izUUi7nGhPReaDb7DYeWF7+E 0IgzXCgpVoM8BxB+D7dZ9iLyU0xeUIYTlXa8y/ZCAH7L7KBdhWuzCZoBO3Sf7emVS4xm 0dDg== X-Gm-Message-State: AKGB3mIykdWUdmNoXkf1TkcReuqED6BPSxIb2a7lR4uJj07nMx9Ualhn Yai1oS+0zknhGLyapV+hwT8= X-Google-Smtp-Source: ACJfBosBP0Y63yCExhjWpX6D/aLbqnMSbQBe6Xlsnpwbin43/ptpJcROETUmZxMPXz8m2AfTtDGWSg== X-Received: by 10.99.112.70 with SMTP id a6mr28557128pgn.152.1516291511435; Thu, 18 Jan 2018 08:05:11 -0800 (PST) Received: from localhost ([58.35.38.61]) by smtp.gmail.com with ESMTPSA id t21sm14441950pfe.122.2018.01.18.08.05.10 (version=TLS1_2 cipher=AES128-SHA bits=128/128); Thu, 18 Jan 2018 08:05:10 -0800 (PST) From: Yongji Xie X-Google-Original-From: Yongji Xie To: mst@redhat.com, marcandre.lureau@redhat.com Date: Fri, 19 Jan 2018 00:04:05 +0800 Message-Id: <1516291445-4572-1-git-send-email-xieyongji@baidu.com> X-Mailer: git-send-email 1.7.9.5 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2607:f8b0:400e:c05::244 Subject: [Qemu-devel] [PATCH] libvhost-user: Support across-memory-boundary access X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: qemu-devel@nongnu.org, maxime.coquelin@redhat.com, dgilbert@redhat.com, f4bug@amsat.org Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP The sg list/indirect descriptor table may be contigious in GPA but not in HVA address space. But libvhost-user wasn't aware of that. This would cause out-of-bounds access. Even a malicious guest could use it to get information from the vhost-user backend. Introduce a plen parameter in vu_gpa_to_va() so we can handle this case, returning the actual mapped length. Signed-off-by: Yongji Xie Reviewed-by: Maxime Coquelin --- contrib/libvhost-user/libvhost-user.c | 133 +++++++++++++++++++++++++++++---- contrib/libvhost-user/libvhost-user.h | 3 +- 2 files changed, 122 insertions(+), 14 deletions(-) diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/libvhost-user.c index cadced7..6ec31c6 100644 --- a/contrib/libvhost-user/libvhost-user.c +++ b/contrib/libvhost-user/libvhost-user.c @@ -116,15 +116,22 @@ vu_panic(VuDev *dev, const char *msg, ...) /* Translate guest physical address to our virtual address. */ void * -vu_gpa_to_va(VuDev *dev, uint64_t guest_addr) +vu_gpa_to_va(VuDev *dev, uint64_t *plen, uint64_t guest_addr) { int i; + if (*plen == 0) { + return NULL; + } + /* Find matching memory region. */ for (i = 0; i < dev->nregions; i++) { VuDevRegion *r = &dev->regions[i]; if ((guest_addr >= r->gpa) && (guest_addr < (r->gpa + r->size))) { + if ((guest_addr + *plen) > (r->gpa + r->size)) { + *plen = r->gpa + r->size - guest_addr; + } return (void *)(uintptr_t) guest_addr - r->gpa + r->mmap_addr + r->mmap_offset; } @@ -1074,6 +1081,37 @@ virtqueue_get_head(VuDev *dev, VuVirtq *vq, return true; } +static int +virtqueue_read_indirect_desc(VuDev *dev, struct vring_desc *desc, + uint64_t addr, size_t len) +{ + struct vring_desc *ori_desc; + uint64_t read_len; + + if (len > (VIRTQUEUE_MAX_SIZE * sizeof(struct vring_desc))) { + return -1; + } + + if (len == 0) { + return -1; + } + + while (len) { + read_len = len; + ori_desc = vu_gpa_to_va(dev, &read_len, addr); + if (!ori_desc) { + return -1; + } + + memcpy(desc, ori_desc, read_len); + len -= read_len; + addr += read_len; + desc += read_len; + } + + return 0; +} + enum { VIRTQUEUE_READ_DESC_ERROR = -1, VIRTQUEUE_READ_DESC_DONE = 0, /* end of chain */ @@ -1120,8 +1158,10 @@ vu_queue_get_avail_bytes(VuDev *dev, VuVirtq *vq, unsigned int *in_bytes, } while ((rc = virtqueue_num_heads(dev, vq, idx)) > 0) { - unsigned int max, num_bufs, indirect = 0; + unsigned int max, desc_len, num_bufs, indirect = 0; + uint64_t desc_addr, read_len; struct vring_desc *desc; + struct vring_desc desc_buf[VIRTQUEUE_MAX_SIZE]; unsigned int i; max = vq->vring.num; @@ -1145,8 +1185,24 @@ vu_queue_get_avail_bytes(VuDev *dev, VuVirtq *vq, unsigned int *in_bytes, /* loop over the indirect descriptor table */ indirect = 1; - max = desc[i].len / sizeof(struct vring_desc); - desc = vu_gpa_to_va(dev, desc[i].addr); + desc_addr = desc[i].addr; + desc_len = desc[i].len; + max = desc_len / sizeof(struct vring_desc); + read_len = desc_len; + desc = vu_gpa_to_va(dev, &read_len, desc_addr); + if (unlikely(desc && read_len != desc_len)) { + /* Failed to use zero copy */ + desc = NULL; + if (!virtqueue_read_indirect_desc(dev, desc_buf, + desc_addr, + desc_len)) { + desc = desc_buf; + } + } + if (!desc) { + vu_panic(dev, "Invalid indirect buffer table"); + goto err; + } num_bufs = i = 0; } @@ -1344,9 +1400,24 @@ virtqueue_map_desc(VuDev *dev, return; } - iov[num_sg].iov_base = vu_gpa_to_va(dev, pa); - iov[num_sg].iov_len = sz; - num_sg++; + while (sz) { + uint64_t len = sz; + + if (num_sg == max_num_sg) { + vu_panic(dev, "virtio: too many descriptors in indirect table"); + return; + } + + iov[num_sg].iov_base = vu_gpa_to_va(dev, &len, pa); + if (iov[num_sg].iov_base == NULL) { + vu_panic(dev, "virtio: invalid address for buffers"); + return; + } + iov[num_sg].iov_len = len; + num_sg++; + sz -= len; + pa += len; + } *p_num_sg = num_sg; } @@ -1378,10 +1449,12 @@ virtqueue_alloc_element(size_t sz, void * vu_queue_pop(VuDev *dev, VuVirtq *vq, size_t sz) { - unsigned int i, head, max; + unsigned int i, head, max, desc_len; + uint64_t desc_addr, read_len; VuVirtqElement *elem; unsigned out_num, in_num; struct iovec iov[VIRTQUEUE_MAX_SIZE]; + struct vring_desc desc_buf[VIRTQUEUE_MAX_SIZE]; struct vring_desc *desc; int rc; @@ -1422,8 +1495,24 @@ vu_queue_pop(VuDev *dev, VuVirtq *vq, size_t sz) } /* loop over the indirect descriptor table */ - max = desc[i].len / sizeof(struct vring_desc); - desc = vu_gpa_to_va(dev, desc[i].addr); + desc_addr = desc[i].addr; + desc_len = desc[i].len; + max = desc_len / sizeof(struct vring_desc); + read_len = desc_len; + desc = vu_gpa_to_va(dev, &read_len, desc_addr); + if (unlikely(desc && read_len != desc_len)) { + /* Failed to use zero copy */ + desc = NULL; + if (!virtqueue_read_indirect_desc(dev, desc_buf, + desc_addr, + desc_len)) { + desc = desc_buf; + } + } + if (!desc) { + vu_panic(dev, "Invalid indirect buffer table"); + return NULL; + } i = 0; } @@ -1499,7 +1588,9 @@ vu_log_queue_fill(VuDev *dev, VuVirtq *vq, unsigned int len) { struct vring_desc *desc = vq->vring.desc; - unsigned int i, max, min; + unsigned int i, max, min, desc_len; + uint64_t desc_addr, read_len; + struct vring_desc desc_buf[VIRTQUEUE_MAX_SIZE]; unsigned num_bufs = 0; max = vq->vring.num; @@ -1511,8 +1602,24 @@ vu_log_queue_fill(VuDev *dev, VuVirtq *vq, } /* loop over the indirect descriptor table */ - max = desc[i].len / sizeof(struct vring_desc); - desc = vu_gpa_to_va(dev, desc[i].addr); + desc_addr = desc[i].addr; + desc_len = desc[i].len; + max = desc_len / sizeof(struct vring_desc); + read_len = desc_len; + desc = vu_gpa_to_va(dev, &read_len, desc_addr); + if (unlikely(desc && read_len != desc_len)) { + /* Failed to use zero copy */ + desc = NULL; + if (!virtqueue_read_indirect_desc(dev, desc_buf, + desc_addr, + desc_len)) { + desc = desc_buf; + } + } + if (!desc) { + vu_panic(dev, "Invalid indirect buffer table"); + return; + } i = 0; } diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h index 2f5864b..4e7ecd8 100644 --- a/contrib/libvhost-user/libvhost-user.h +++ b/contrib/libvhost-user/libvhost-user.h @@ -294,11 +294,12 @@ bool vu_dispatch(VuDev *dev); /** * vu_gpa_to_va: * @dev: a VuDev context + * @plen: guest memory size * @guest_addr: guest address * * Translate a guest address to a pointer. Returns NULL on failure. */ -void *vu_gpa_to_va(VuDev *dev, uint64_t guest_addr); +void *vu_gpa_to_va(VuDev *dev, uint64_t *plen, uint64_t guest_addr); /** * vu_get_queue: