45e84a0
From 45d6cdff48356dc8974497ec0524f971b646dd70 Mon Sep 17 00:00:00 2001
45e84a0
From: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
45e84a0
Date: Wed, 21 Dec 2011 12:37:22 +0530
45e84a0
Subject: [PATCH 08/25] hw/9pfs: replace iovec manipulation with QEMUIOVector
45e84a0
45e84a0
The v9fs_read() and v9fs_write() functions rely on iovec[] manipulation
45e84a0
code should be replaced with QEMUIOVector to avoid duplicating code.
45e84a0
In the future it may be possible to make the code even more concise by
45e84a0
using QEMUIOVector consistently across virtio and 9pfs.
45e84a0
45e84a0
The "v" format specifier for pdu_marshal() and pdu_unmarshal() is
45e84a0
dropped since it does not actually pack/unpack anything.  The specifier
45e84a0
was also not implemented to update the offset variable and could only be
45e84a0
used at the end of a format string, another sign that this shouldn't
45e84a0
really be a format specifier.  Instead, see the new
45e84a0
v9fs_init_qiov_from_pdu() function.
45e84a0
45e84a0
This change avoids a possible iovec[] buffer overflow when indirect
45e84a0
vrings are used since the number of vectors is now limited by the
45e84a0
underlying VirtQueueElement and cannot be out-of-bounds.
45e84a0
45e84a0
Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
45e84a0
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
45e84a0
---
45e84a0
 hw/9pfs/virtio-9p.c |  162 +++++++++++++++++++--------------------------------
45e84a0
 1 files changed, 60 insertions(+), 102 deletions(-)
45e84a0
45e84a0
diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c
45e84a0
index dd43209..c018916 100644
45e84a0
--- a/hw/9pfs/virtio-9p.c
45e84a0
+++ b/hw/9pfs/virtio-9p.c
45e84a0
@@ -674,40 +674,6 @@ static size_t pdu_pack(V9fsPDU *pdu, size_t offset, const void *src,
45e84a0
                              offset, size, 1);
45e84a0
 }
45e84a0
45e84a0
-static int pdu_copy_sg(V9fsPDU *pdu, size_t offset, int rx, struct iovec *sg)
45e84a0
-{
45e84a0
-    size_t pos = 0;
45e84a0
-    int i, j;
45e84a0
-    struct iovec *src_sg;
45e84a0
-    unsigned int num;
45e84a0
-
45e84a0
-    if (rx) {
45e84a0
-        src_sg = pdu->elem.in_sg;
45e84a0
-        num = pdu->elem.in_num;
45e84a0
-    } else {
45e84a0
-        src_sg = pdu->elem.out_sg;
45e84a0
-        num = pdu->elem.out_num;
45e84a0
-    }
45e84a0
-
45e84a0
-    j = 0;
45e84a0
-    for (i = 0; i < num; i++) {
45e84a0
-        if (offset <= pos) {
45e84a0
-            sg[j].iov_base = src_sg[i].iov_base;
45e84a0
-            sg[j].iov_len = src_sg[i].iov_len;
45e84a0
-            j++;
45e84a0
-        } else if (offset < (src_sg[i].iov_len + pos)) {
45e84a0
-            sg[j].iov_base = src_sg[i].iov_base;
45e84a0
-            sg[j].iov_len = src_sg[i].iov_len;
45e84a0
-            sg[j].iov_base += (offset - pos);
45e84a0
-            sg[j].iov_len -= (offset - pos);
45e84a0
-            j++;
45e84a0
-        }
45e84a0
-        pos += src_sg[i].iov_len;
45e84a0
-    }
45e84a0
-
45e84a0
-    return j;
45e84a0
-}
45e84a0
-
45e84a0
 static size_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
45e84a0
 {
45e84a0
     size_t old_offset = offset;
45e84a0
@@ -743,12 +709,6 @@ static size_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
45e84a0
             *valp = le64_to_cpu(val);
45e84a0
             break;
45e84a0
         }
45e84a0
-        case 'v': {
45e84a0
-            struct iovec *iov = va_arg(ap, struct iovec *);
45e84a0
-            int *iovcnt = va_arg(ap, int *);
45e84a0
-            *iovcnt = pdu_copy_sg(pdu, offset, 0, iov);
45e84a0
-            break;
45e84a0
-        }
45e84a0
         case 's': {
45e84a0
             V9fsString *str = va_arg(ap, V9fsString *);
45e84a0
             offset += pdu_unmarshal(pdu, offset, "w", &str->size);
45e84a0
@@ -827,12 +787,6 @@ static size_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
45e84a0
             offset += pdu_pack(pdu, offset, &val, sizeof(val));
45e84a0
             break;
45e84a0
         }
45e84a0
-        case 'v': {
45e84a0
-            struct iovec *iov = va_arg(ap, struct iovec *);
45e84a0
-            int *iovcnt = va_arg(ap, int *);
45e84a0
-            *iovcnt = pdu_copy_sg(pdu, offset, 1, iov);
45e84a0
-            break;
45e84a0
-        }
45e84a0
         case 's': {
45e84a0
             V9fsString *str = va_arg(ap, V9fsString *);
45e84a0
             offset += pdu_marshal(pdu, offset, "w", str->size);
45e84a0
@@ -1143,42 +1097,6 @@ static void stat_to_v9stat_dotl(V9fsState *s, const struct stat *stbuf,
45e84a0
     stat_to_qid(stbuf, &v9lstat->qid);
45e84a0
 }
45e84a0
45e84a0
-static struct iovec *adjust_sg(struct iovec *sg, int len, int *iovcnt)
45e84a0
-{
45e84a0
-    while (len && *iovcnt) {
45e84a0
-        if (len < sg->iov_len) {
45e84a0
-            sg->iov_len -= len;
45e84a0
-            sg->iov_base += len;
45e84a0
-            len = 0;
45e84a0
-        } else {
45e84a0
-            len -= sg->iov_len;
45e84a0
-            sg++;
45e84a0
-            *iovcnt -= 1;
45e84a0
-        }
45e84a0
-    }
45e84a0
-
45e84a0
-    return sg;
45e84a0
-}
45e84a0
-
45e84a0
-static struct iovec *cap_sg(struct iovec *sg, int cap, int *cnt)
45e84a0
-{
45e84a0
-    int i;
45e84a0
-    int total = 0;
45e84a0
-
45e84a0
-    for (i = 0; i < *cnt; i++) {
45e84a0
-        if ((total + sg[i].iov_len) > cap) {
45e84a0
-            sg[i].iov_len -= ((total + sg[i].iov_len) - cap);
45e84a0
-            i++;
45e84a0
-            break;
45e84a0
-        }
45e84a0
-        total += sg[i].iov_len;
45e84a0
-    }
45e84a0
-
45e84a0
-    *cnt = i;
45e84a0
-
45e84a0
-    return sg;
45e84a0
-}
45e84a0
-
45e84a0
 static void print_sg(struct iovec *sg, int cnt)
45e84a0
 {
45e84a0
     int i;
45e84a0
@@ -1861,6 +1779,38 @@ out:
45e84a0
     return count;
45e84a0
 }
45e84a0
45e84a0
+/*
45e84a0
+ * Create a QEMUIOVector for a sub-region of PDU iovecs
45e84a0
+ *
45e84a0
+ * @qiov:       uninitialized QEMUIOVector
45e84a0
+ * @skip:       number of bytes to skip from beginning of PDU
45e84a0
+ * @size:       number of bytes to include
45e84a0
+ * @is_write:   true - write, false - read
45e84a0
+ *
45e84a0
+ * The resulting QEMUIOVector has heap-allocated iovecs and must be cleaned up
45e84a0
+ * with qemu_iovec_destroy().
45e84a0
+ */
45e84a0
+static void v9fs_init_qiov_from_pdu(QEMUIOVector *qiov, V9fsPDU *pdu,
45e84a0
+                                    uint64_t skip, size_t size,
45e84a0
+                                    bool is_write)
45e84a0
+{
45e84a0
+    QEMUIOVector elem;
45e84a0
+    struct iovec *iov;
45e84a0
+    unsigned int niov;
45e84a0
+
45e84a0
+    if (is_write) {
45e84a0
+        iov = pdu->elem.out_sg;
45e84a0
+        niov = pdu->elem.out_num;
45e84a0
+    } else {
45e84a0
+        iov = pdu->elem.in_sg;
45e84a0
+        niov = pdu->elem.in_num;
45e84a0
+    }
45e84a0
+
45e84a0
+    qemu_iovec_init_external(&elem, iov, niov);
45e84a0
+    qemu_iovec_init(qiov, niov);
45e84a0
+    qemu_iovec_copy(qiov, &elem, skip, size);
45e84a0
+}
45e84a0
+
45e84a0
 static void v9fs_read(void *opaque)
45e84a0
 {
45e84a0
     int32_t fid;
45e84a0
@@ -1895,21 +1845,21 @@ static void v9fs_read(void *opaque)
45e84a0
         err += pdu_marshal(pdu, offset, "d", count);
45e84a0
         err += count;
45e84a0
     } else if (fidp->fid_type == P9_FID_FILE) {
45e84a0
-        int32_t cnt;
45e84a0
+        QEMUIOVector qiov_full;
45e84a0
+        QEMUIOVector qiov;
45e84a0
         int32_t len;
45e84a0
-        struct iovec *sg;
45e84a0
-        struct iovec iov[128]; /* FIXME: bad, bad, bad */
45e84a0
45e84a0
-        sg = iov;
45e84a0
-        pdu_marshal(pdu, offset + 4, "v", sg, &cnt);
45e84a0
-        sg = cap_sg(sg, max_count, &cnt);
45e84a0
+        v9fs_init_qiov_from_pdu(&qiov_full, pdu, offset + 4, max_count, false);
45e84a0
+        qemu_iovec_init(&qiov, qiov_full.niov);
45e84a0
         do {
45e84a0
+            qemu_iovec_reset(&qiov);
45e84a0
+            qemu_iovec_copy(&qiov, &qiov_full, count, qiov_full.size - count);
45e84a0
             if (0) {
45e84a0
-                print_sg(sg, cnt);
45e84a0
+                print_sg(qiov.iov, qiov.niov);
45e84a0
             }
45e84a0
             /* Loop in case of EINTR */
45e84a0
             do {
45e84a0
-                len = v9fs_co_preadv(pdu, fidp, sg, cnt, off);
45e84a0
+                len = v9fs_co_preadv(pdu, fidp, qiov.iov, qiov.niov, off);
45e84a0
                 if (len >= 0) {
45e84a0
                     off   += len;
45e84a0
                     count += len;
45e84a0
@@ -1920,11 +1870,12 @@ static void v9fs_read(void *opaque)
45e84a0
                 err = len;
45e84a0
                 goto out;
45e84a0
             }
45e84a0
-            sg = adjust_sg(sg, len, &cnt);
45e84a0
         } while (count < max_count && len > 0);
45e84a0
         err = offset;
45e84a0
         err += pdu_marshal(pdu, offset, "d", count);
45e84a0
         err += count;
45e84a0
+        qemu_iovec_destroy(&qiov);
45e84a0
+        qemu_iovec_destroy(&qiov_full);
45e84a0
     } else if (fidp->fid_type == P9_FID_XATTR) {
45e84a0
         err = v9fs_xattr_read(s, pdu, fidp, off, max_count);
45e84a0
     } else {
45e84a0
@@ -2095,7 +2046,6 @@ out:
45e84a0
45e84a0
 static void v9fs_write(void *opaque)
45e84a0
 {
45e84a0
-    int cnt;
45e84a0
     ssize_t err;
45e84a0
     int32_t fid;
45e84a0
     int64_t off;
45e84a0
@@ -2104,13 +2054,14 @@ static void v9fs_write(void *opaque)
45e84a0
     int32_t total = 0;
45e84a0
     size_t offset = 7;
45e84a0
     V9fsFidState *fidp;
45e84a0
-    struct iovec iov[128]; /* FIXME: bad, bad, bad */
45e84a0
-    struct iovec *sg = iov;
45e84a0
     V9fsPDU *pdu = opaque;
45e84a0
     V9fsState *s = pdu->s;
45e84a0
+    QEMUIOVector qiov_full;
45e84a0
+    QEMUIOVector qiov;
45e84a0
45e84a0
-    pdu_unmarshal(pdu, offset, "dqdv", &fid, &off, &count, sg, &cnt);
45e84a0
-    trace_v9fs_write(pdu->tag, pdu->id, fid, off, count, cnt);
45e84a0
+    offset += pdu_unmarshal(pdu, offset, "dqd", &fid, &off, &count);
45e84a0
+    v9fs_init_qiov_from_pdu(&qiov_full, pdu, offset, count, true);
45e84a0
+    trace_v9fs_write(pdu->tag, pdu->id, fid, off, count, qiov_full.niov);
45e84a0
45e84a0
     fidp = get_fid(pdu, fid);
45e84a0
     if (fidp == NULL) {
45e84a0
@@ -2126,20 +2077,23 @@ static void v9fs_write(void *opaque)
45e84a0
         /*
45e84a0
          * setxattr operation
45e84a0
          */
45e84a0
-        err = v9fs_xattr_write(s, pdu, fidp, off, count, sg, cnt);
45e84a0
+        err = v9fs_xattr_write(s, pdu, fidp, off, count,
45e84a0
+                               qiov_full.iov, qiov_full.niov);
45e84a0
         goto out;
45e84a0
     } else {
45e84a0
         err = -EINVAL;
45e84a0
         goto out;
45e84a0
     }
45e84a0
-    sg = cap_sg(sg, count, &cnt);
45e84a0
+    qemu_iovec_init(&qiov, qiov_full.niov);
45e84a0
     do {
45e84a0
+        qemu_iovec_reset(&qiov);
45e84a0
+        qemu_iovec_copy(&qiov, &qiov_full, total, qiov_full.size - total);
45e84a0
         if (0) {
45e84a0
-            print_sg(sg, cnt);
45e84a0
+            print_sg(qiov.iov, qiov.niov);
45e84a0
         }
45e84a0
         /* Loop in case of EINTR */
45e84a0
         do {
45e84a0
-            len = v9fs_co_pwritev(pdu, fidp, sg, cnt, off);
45e84a0
+            len = v9fs_co_pwritev(pdu, fidp, qiov.iov, qiov.niov, off);
45e84a0
             if (len >= 0) {
45e84a0
                 off   += len;
45e84a0
                 total += len;
45e84a0
@@ -2148,16 +2102,20 @@ static void v9fs_write(void *opaque)
45e84a0
         if (len < 0) {
45e84a0
             /* IO error return the error */
45e84a0
             err = len;
45e84a0
-            goto out;
45e84a0
+            goto out_qiov;
45e84a0
         }
45e84a0
-        sg = adjust_sg(sg, len, &cnt);
45e84a0
     } while (total < count && len > 0);
45e84a0
+
45e84a0
+    offset = 7;
45e84a0
     offset += pdu_marshal(pdu, offset, "d", total);
45e84a0
     err = offset;
45e84a0
     trace_v9fs_write_return(pdu->tag, pdu->id, total, err);
45e84a0
+out_qiov:
45e84a0
+    qemu_iovec_destroy(&qiov);
45e84a0
 out:
45e84a0
     put_fid(pdu, fidp);
45e84a0
 out_nofid:
45e84a0
+    qemu_iovec_destroy(&qiov_full);
45e84a0
     complete_pdu(s, pdu, err);
45e84a0
 }
45e84a0
45e84a0
-- 
45e84a0
1.7.7.5
45e84a0