]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.10-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 12 Aug 2013 04:31:25 +0000 (21:31 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 12 Aug 2013 04:31:25 +0000 (21:31 -0700)
added patches:
virtio-console-add-pipe_lock-unlock-for-splice_write.patch
virtio-console-clean-up-port-data-immediately-at-time-of-unplug.patch
virtio-console-fix-race-in-port_fops_open-and-port-unplug.patch
virtio-console-fix-race-with-port-unplug-and-open-close.patch
virtio-console-fix-raising-sigio-after-port-unplug.patch
virtio-console-quit-from-splice_write-if-pipe-nrbufs-is-0.patch
virtio-console-return-enodev-on-all-read-operations-after-unplug.patch

queue-3.10/series
queue-3.10/virtio-console-add-pipe_lock-unlock-for-splice_write.patch [new file with mode: 0644]
queue-3.10/virtio-console-clean-up-port-data-immediately-at-time-of-unplug.patch [new file with mode: 0644]
queue-3.10/virtio-console-fix-race-in-port_fops_open-and-port-unplug.patch [new file with mode: 0644]
queue-3.10/virtio-console-fix-race-with-port-unplug-and-open-close.patch [new file with mode: 0644]
queue-3.10/virtio-console-fix-raising-sigio-after-port-unplug.patch [new file with mode: 0644]
queue-3.10/virtio-console-quit-from-splice_write-if-pipe-nrbufs-is-0.patch [new file with mode: 0644]
queue-3.10/virtio-console-return-enodev-on-all-read-operations-after-unplug.patch [new file with mode: 0644]

index cc294a6de621cd47c1d58d2bb0ef64b2070c07d1..86eb0a2291014703f38028fd5359a87cd5b5e66d 100644 (file)
@@ -29,3 +29,10 @@ btrfs-release-both-paths-before-logging-dir-changed-extents.patch
 lockd-don-t-call-utsname-nodename-from-nlmclnt_setlockargs.patch
 sunrpc-don-t-auto-disconnect-from-the-local-rpcbind-socket.patch
 sunrpc-if-the-rpcbind-channel-is-disconnected-fail-the-call-to-unregister.patch
+virtio-console-quit-from-splice_write-if-pipe-nrbufs-is-0.patch
+virtio-console-add-pipe_lock-unlock-for-splice_write.patch
+virtio-console-fix-race-with-port-unplug-and-open-close.patch
+virtio-console-fix-race-in-port_fops_open-and-port-unplug.patch
+virtio-console-clean-up-port-data-immediately-at-time-of-unplug.patch
+virtio-console-fix-raising-sigio-after-port-unplug.patch
+virtio-console-return-enodev-on-all-read-operations-after-unplug.patch
diff --git a/queue-3.10/virtio-console-add-pipe_lock-unlock-for-splice_write.patch b/queue-3.10/virtio-console-add-pipe_lock-unlock-for-splice_write.patch
new file mode 100644 (file)
index 0000000..7b137cf
--- /dev/null
@@ -0,0 +1,141 @@
+From 2b4fbf029dff5a28d9bf646346dea891ec43398a Mon Sep 17 00:00:00 2001
+From: Yoshihiro YUNOMAE <yoshihiro.yunomae.ez@hitachi.com>
+Date: Tue, 23 Jul 2013 11:30:49 +0930
+Subject: virtio/console: Add pipe_lock/unlock for splice_write
+
+From: Yoshihiro YUNOMAE <yoshihiro.yunomae.ez@hitachi.com>
+
+commit 2b4fbf029dff5a28d9bf646346dea891ec43398a upstream.
+
+Add pipe_lock/unlock for splice_write to avoid oops by following competition:
+
+(1) An application gets fds of a trace buffer, virtio-serial, pipe.
+(2) The application does fork()
+(3) The processes execute splice_read(trace buffer) and
+    splice_write(virtio-serial) via same pipe.
+
+        <parent>                   <child>
+  get fds of a trace buffer,
+         virtio-serial, pipe
+          |
+        fork()----------create--------+
+          |                           |
+      splice(read)                    |           ---+
+      splice(write)                   |              +-- no competition
+          |                       splice(read)       |
+          |                       splice(write)   ---+
+          |                           |
+      splice(read)                    |
+      splice(write)               splice(read)    ------ competition
+          |                       splice(write)
+
+Two processes share a pipe_inode_info structure. If the child execute
+splice(read) when the parent tries to execute splice(write), the
+structure can be broken. Existing virtio-serial driver does not get
+lock for the structure in splice_write, so this competition will induce
+oops.
+
+<oops messages>
+ BUG: unable to handle kernel NULL pointer dereference at 0000000000000018
+ IP: [<ffffffff811a6b5f>] splice_from_pipe_feed+0x6f/0x130
+ PGD 7223e067 PUD 72391067 PMD 0
+ Oops: 0000 [#1] SMP
+ Modules linked in: lockd bnep bluetooth rfkill sunrpc ip6t_REJECT nf_conntrack_ipv6 nf_defrag_ipv6 xt_state nf_conntrack ip6table_filter ip6_tables snd_hda_intel snd_hda_codec snd_hwdep snd_pcm snd_page_alloc snd_timer snd soundcore pcspkr virtio_net virtio_balloon i2c_piix4 i2c_core microcode uinput floppy
+ CPU: 0 PID: 1072 Comm: compete-test Not tainted 3.10.0ws+ #55
+ Hardware name: Bochs Bochs, BIOS Bochs 01/01/2007
+ task: ffff880071b98000 ti: ffff88007b55e000 task.ti: ffff88007b55e000
+ RIP: 0010:[<ffffffff811a6b5f>]  [<ffffffff811a6b5f>] splice_from_pipe_feed+0x6f/0x130
+ RSP: 0018:ffff88007b55fd78  EFLAGS: 00010287
+ RAX: 0000000000000000 RBX: ffff88007b55fe20 RCX: 0000000000000000
+ RDX: 0000000000001000 RSI: ffff88007a95ba30 RDI: ffff880036f9e6c0
+ RBP: ffff88007b55fda8 R08: 00000000000006ec R09: ffff880077626708
+ R10: 0000000000000003 R11: ffffffff8139ca59 R12: ffff88007a95ba30
+ R13: 0000000000000000 R14: ffffffff8139dd00 R15: ffff880036f9e6c0
+ FS:  00007f2e2e3a0740(0000) GS:ffff88007fc00000(0000) knlGS:0000000000000000
+ CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
+ CR2: 0000000000000018 CR3: 0000000071bd1000 CR4: 00000000000006f0
+ DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
+ DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
+ Stack:
+  ffffffff8139ca59 ffff88007b55fe20 ffff880036f9e6c0 ffffffff8139dd00
+  ffff8800776266c0 ffff880077626708 ffff88007b55fde8 ffffffff811a6e8e
+  ffff88007b55fde8 ffffffff8139ca59 ffff880036f9e6c0 ffff88007b55fe20
+ Call Trace:
+  [<ffffffff8139ca59>] ? alloc_buf.isra.13+0x39/0xb0
+  [<ffffffff8139dd00>] ? virtcons_restore+0x100/0x100
+  [<ffffffff811a6e8e>] __splice_from_pipe+0x7e/0x90
+  [<ffffffff8139ca59>] ? alloc_buf.isra.13+0x39/0xb0
+  [<ffffffff8139d739>] port_fops_splice_write+0xe9/0x140
+  [<ffffffff8127a3f4>] ? selinux_file_permission+0xc4/0x120
+  [<ffffffff8139d650>] ? wait_port_writable+0x1b0/0x1b0
+  [<ffffffff811a6fe0>] do_splice_from+0xa0/0x110
+  [<ffffffff811a951f>] SyS_splice+0x5ff/0x6b0
+  [<ffffffff8161facf>] tracesys+0xdd/0xe2
+ Code: 49 8b 87 80 00 00 00 4c 8d 24 d0 8b 53 04 41 8b 44 24 0c 4d 8b 6c 24 10 39 d0 89 03 76 02 89 13 49 8b 44 24 10 4c 89 e6 4c 89 ff <ff> 50 18 85 c0 0f 85 aa 00 00 00 48 89 da 4c 89 e6 4c 89 ff 41
+ RIP  [<ffffffff811a6b5f>] splice_from_pipe_feed+0x6f/0x130
+  RSP <ffff88007b55fd78>
+ CR2: 0000000000000018
+ ---[ end trace 24572beb7764de59 ]---
+
+V2: Fix a locking problem for error
+V3: Add Reviewed-by lines and stable@ line in sign-off area
+
+Signed-off-by: Yoshihiro YUNOMAE <yoshihiro.yunomae.ez@hitachi.com>
+Reviewed-by: Amit Shah <amit.shah@redhat.com>
+Reviewed-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
+Cc: Amit Shah <amit.shah@redhat.com>
+Cc: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/char/virtio_console.c |   20 +++++++++++++++-----
+ 1 file changed, 15 insertions(+), 5 deletions(-)
+
+--- a/drivers/char/virtio_console.c
++++ b/drivers/char/virtio_console.c
+@@ -936,16 +936,21 @@ static ssize_t port_fops_splice_write(st
+        * pipe->nrbufs == 0 means there are no data to transfer,
+        * so this returns just 0 for no data.
+        */
+-      if (!pipe->nrbufs)
+-              return 0;
++      pipe_lock(pipe);
++      if (!pipe->nrbufs) {
++              ret = 0;
++              goto error_out;
++      }
+       ret = wait_port_writable(port, filp->f_flags & O_NONBLOCK);
+       if (ret < 0)
+-              return ret;
++              goto error_out;
+       buf = alloc_buf(port->out_vq, 0, pipe->nrbufs);
+-      if (!buf)
+-              return -ENOMEM;
++      if (!buf) {
++              ret = -ENOMEM;
++              goto error_out;
++      }
+       sgl.n = 0;
+       sgl.len = 0;
+@@ -953,12 +958,17 @@ static ssize_t port_fops_splice_write(st
+       sgl.sg = buf->sg;
+       sg_init_table(sgl.sg, sgl.size);
+       ret = __splice_from_pipe(pipe, &sd, pipe_to_sg);
++      pipe_unlock(pipe);
+       if (likely(ret > 0))
+               ret = __send_to_port(port, buf->sg, sgl.n, sgl.len, buf, true);
+       if (unlikely(ret <= 0))
+               free_buf(buf, true);
+       return ret;
++
++error_out:
++      pipe_unlock(pipe);
++      return ret;
+ }
+ static unsigned int port_fops_poll(struct file *filp, poll_table *wait)
diff --git a/queue-3.10/virtio-console-clean-up-port-data-immediately-at-time-of-unplug.patch b/queue-3.10/virtio-console-clean-up-port-data-immediately-at-time-of-unplug.patch
new file mode 100644 (file)
index 0000000..4210f58
--- /dev/null
@@ -0,0 +1,126 @@
+From ea3768b4386a8d1790f4cc9a35de4f55b92d6442 Mon Sep 17 00:00:00 2001
+From: Amit Shah <amit.shah@redhat.com>
+Date: Mon, 29 Jul 2013 14:20:29 +0930
+Subject: virtio: console: clean up port data immediately at time of unplug
+
+From: Amit Shah <amit.shah@redhat.com>
+
+commit ea3768b4386a8d1790f4cc9a35de4f55b92d6442 upstream.
+
+We used to keep the port's char device structs and the /sys entries
+around till the last reference to the port was dropped.  This is
+actually unnecessary, and resulted in buggy behaviour:
+
+1. Open port in guest
+2. Hot-unplug port
+3. Hot-plug a port with the same 'name' property as the unplugged one
+
+This resulted in hot-plug being unsuccessful, as a port with the same
+name already exists (even though it was unplugged).
+
+This behaviour resulted in a warning message like this one:
+
+-------------------8<---------------------------------------
+WARNING: at fs/sysfs/dir.c:512 sysfs_add_one+0xc9/0x130() (Not tainted)
+Hardware name: KVM
+sysfs: cannot create duplicate filename
+'/devices/pci0000:00/0000:00:04.0/virtio0/virtio-ports/vport0p1'
+
+Call Trace:
+ [<ffffffff8106b607>] ? warn_slowpath_common+0x87/0xc0
+ [<ffffffff8106b6f6>] ? warn_slowpath_fmt+0x46/0x50
+ [<ffffffff811f2319>] ? sysfs_add_one+0xc9/0x130
+ [<ffffffff811f23e8>] ? create_dir+0x68/0xb0
+ [<ffffffff811f2469>] ? sysfs_create_dir+0x39/0x50
+ [<ffffffff81273129>] ? kobject_add_internal+0xb9/0x260
+ [<ffffffff812733d8>] ? kobject_add_varg+0x38/0x60
+ [<ffffffff812734b4>] ? kobject_add+0x44/0x70
+ [<ffffffff81349de4>] ? get_device_parent+0xf4/0x1d0
+ [<ffffffff8134b389>] ? device_add+0xc9/0x650
+
+-------------------8<---------------------------------------
+
+Instead of relying on guest applications to release all references to
+the ports, we should go ahead and unregister the port from all the core
+layers.  Any open/read calls on the port will then just return errors,
+and an unplug/plug operation on the host will succeed as expected.
+
+This also caused buggy behaviour in case of the device removal (not just
+a port): when the device was removed (which means all ports on that
+device are removed automatically as well), the ports with active
+users would clean up only when the last references were dropped -- and
+it would be too late then to be referencing char device pointers,
+resulting in oopses:
+
+-------------------8<---------------------------------------
+PID: 6162   TASK: ffff8801147ad500  CPU: 0   COMMAND: "cat"
+ #0 [ffff88011b9d5a90] machine_kexec at ffffffff8103232b
+ #1 [ffff88011b9d5af0] crash_kexec at ffffffff810b9322
+ #2 [ffff88011b9d5bc0] oops_end at ffffffff814f4a50
+ #3 [ffff88011b9d5bf0] die at ffffffff8100f26b
+ #4 [ffff88011b9d5c20] do_general_protection at ffffffff814f45e2
+ #5 [ffff88011b9d5c50] general_protection at ffffffff814f3db5
+    [exception RIP: strlen+2]
+    RIP: ffffffff81272ae2  RSP: ffff88011b9d5d00  RFLAGS: 00010246
+    RAX: 0000000000000000  RBX: ffff880118901c18  RCX: 0000000000000000
+    RDX: ffff88011799982c  RSI: 00000000000000d0  RDI: 3a303030302f3030
+    RBP: ffff88011b9d5d38   R8: 0000000000000006   R9: ffffffffa0134500
+    R10: 0000000000001000  R11: 0000000000001000  R12: ffff880117a1cc10
+    R13: 00000000000000d0  R14: 0000000000000017  R15: ffffffff81aff700
+    ORIG_RAX: ffffffffffffffff  CS: 0010  SS: 0018
+ #6 [ffff88011b9d5d00] kobject_get_path at ffffffff8126dc5d
+ #7 [ffff88011b9d5d40] kobject_uevent_env at ffffffff8126e551
+ #8 [ffff88011b9d5dd0] kobject_uevent at ffffffff8126e9eb
+ #9 [ffff88011b9d5de0] device_del at ffffffff813440c7
+
+-------------------8<---------------------------------------
+
+So clean up when we have all the context, and all that's left to do when
+the references to the port have dropped is to free up the port struct
+itself.
+
+Reported-by: chayang <chayang@redhat.com>
+Reported-by: YOGANANTH SUBRAMANIAN <anantyog@in.ibm.com>
+Reported-by: FuXiangChun <xfu@redhat.com>
+Reported-by: Qunfang Zhang <qzhang@redhat.com>
+Reported-by: Sibiao Luo <sluo@redhat.com>
+Signed-off-by: Amit Shah <amit.shah@redhat.com>
+Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/char/virtio_console.c |   16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+--- a/drivers/char/virtio_console.c
++++ b/drivers/char/virtio_console.c
+@@ -1518,14 +1518,6 @@ static void remove_port(struct kref *kre
+       port = container_of(kref, struct port, kref);
+-      sysfs_remove_group(&port->dev->kobj, &port_attribute_group);
+-      device_destroy(pdrvdata.class, port->dev->devt);
+-      cdev_del(port->cdev);
+-
+-      kfree(port->name);
+-
+-      debugfs_remove(port->debugfs_file);
+-
+       kfree(port);
+ }
+@@ -1583,6 +1575,14 @@ static void unplug_port(struct port *por
+        */
+       port->portdev = NULL;
++      sysfs_remove_group(&port->dev->kobj, &port_attribute_group);
++      device_destroy(pdrvdata.class, port->dev->devt);
++      cdev_del(port->cdev);
++
++      kfree(port->name);
++
++      debugfs_remove(port->debugfs_file);
++
+       /*
+        * Locks around here are not necessary - a port can't be
+        * opened after we removed the port struct from ports_list
diff --git a/queue-3.10/virtio-console-fix-race-in-port_fops_open-and-port-unplug.patch b/queue-3.10/virtio-console-fix-race-in-port_fops_open-and-port-unplug.patch
new file mode 100644 (file)
index 0000000..4c4488e
--- /dev/null
@@ -0,0 +1,40 @@
+From 671bdea2b9f210566610603ecbb6584c8a201c8c Mon Sep 17 00:00:00 2001
+From: Amit Shah <amit.shah@redhat.com>
+Date: Mon, 29 Jul 2013 14:17:13 +0930
+Subject: virtio: console: fix race in port_fops_open() and port unplug
+
+From: Amit Shah <amit.shah@redhat.com>
+
+commit 671bdea2b9f210566610603ecbb6584c8a201c8c upstream.
+
+Between open() being called and processed, the port can be unplugged.
+Check if this happened, and bail out.
+
+A simple test script to reproduce this is:
+
+while true; do for i in $(seq 1 100); do echo $i > /dev/vport0p3; done; done;
+
+This opens and closes the port a lot of times; unplugging the port while
+this is happening triggers the bug.
+
+Signed-off-by: Amit Shah <amit.shah@redhat.com>
+Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/char/virtio_console.c |    4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/char/virtio_console.c
++++ b/drivers/char/virtio_console.c
+@@ -1041,6 +1041,10 @@ static int port_fops_open(struct inode *
+       /* We get the port with a kref here */
+       port = find_port_by_devt(cdev->dev);
++      if (!port) {
++              /* Port was unplugged before we could proceed */
++              return -ENXIO;
++      }
+       filp->private_data = port;
+       /*
diff --git a/queue-3.10/virtio-console-fix-race-with-port-unplug-and-open-close.patch b/queue-3.10/virtio-console-fix-race-with-port-unplug-and-open-close.patch
new file mode 100644 (file)
index 0000000..b1f3f86
--- /dev/null
@@ -0,0 +1,57 @@
+From 057b82be3ca3d066478e43b162fc082930a746c9 Mon Sep 17 00:00:00 2001
+From: Amit Shah <amit.shah@redhat.com>
+Date: Mon, 29 Jul 2013 14:16:13 +0930
+Subject: virtio: console: fix race with port unplug and open/close
+
+From: Amit Shah <amit.shah@redhat.com>
+
+commit 057b82be3ca3d066478e43b162fc082930a746c9 upstream.
+
+There's a window between find_port_by_devt() returning a port and us
+taking a kref on the port, where the port could get unplugged.  Fix it
+by taking the reference in find_port_by_devt() itself.
+
+Problem reported and analyzed by Mateusz Guzik.
+
+Reported-by: Mateusz Guzik <mguzik@redhat.com>
+Signed-off-by: Amit Shah <amit.shah@redhat.com>
+Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/char/virtio_console.c |   13 ++++++-------
+ 1 file changed, 6 insertions(+), 7 deletions(-)
+
+--- a/drivers/char/virtio_console.c
++++ b/drivers/char/virtio_console.c
+@@ -272,9 +272,12 @@ static struct port *find_port_by_devt_in
+       unsigned long flags;
+       spin_lock_irqsave(&portdev->ports_lock, flags);
+-      list_for_each_entry(port, &portdev->ports, list)
+-              if (port->cdev->dev == dev)
++      list_for_each_entry(port, &portdev->ports, list) {
++              if (port->cdev->dev == dev) {
++                      kref_get(&port->kref);
+                       goto out;
++              }
++      }
+       port = NULL;
+ out:
+       spin_unlock_irqrestore(&portdev->ports_lock, flags);
+@@ -1036,14 +1039,10 @@ static int port_fops_open(struct inode *
+       struct port *port;
+       int ret;
++      /* We get the port with a kref here */
+       port = find_port_by_devt(cdev->dev);
+       filp->private_data = port;
+-      /* Prevent against a port getting hot-unplugged at the same time */
+-      spin_lock_irq(&port->portdev->ports_lock);
+-      kref_get(&port->kref);
+-      spin_unlock_irq(&port->portdev->ports_lock);
+-
+       /*
+        * Don't allow opening of console port devices -- that's done
+        * via /dev/hvc
diff --git a/queue-3.10/virtio-console-fix-raising-sigio-after-port-unplug.patch b/queue-3.10/virtio-console-fix-raising-sigio-after-port-unplug.patch
new file mode 100644 (file)
index 0000000..6437f2e
--- /dev/null
@@ -0,0 +1,46 @@
+From 92d3453815fbe74d539c86b60dab39ecdf01bb99 Mon Sep 17 00:00:00 2001
+From: Amit Shah <amit.shah@redhat.com>
+Date: Mon, 29 Jul 2013 14:21:32 +0930
+Subject: virtio: console: fix raising SIGIO after port unplug
+
+From: Amit Shah <amit.shah@redhat.com>
+
+commit 92d3453815fbe74d539c86b60dab39ecdf01bb99 upstream.
+
+SIGIO should be sent when a port gets unplugged.  It should only be sent
+to prcesses that have the port opened, and have asked for SIGIO to be
+delivered.  We were clearing out guest_connected before calling
+send_sigio_to_port(), resulting in a sigio not getting sent to
+processes.
+
+Fix by setting guest_connected to false after invoking the sigio
+function.
+
+Signed-off-by: Amit Shah <amit.shah@redhat.com>
+Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/char/virtio_console.c |    8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/drivers/char/virtio_console.c
++++ b/drivers/char/virtio_console.c
+@@ -1551,12 +1551,14 @@ static void unplug_port(struct port *por
+       spin_unlock_irq(&port->portdev->ports_lock);
+       if (port->guest_connected) {
++              /* Let the app know the port is going down. */
++              send_sigio_to_port(port);
++
++              /* Do this after sigio is actually sent */
+               port->guest_connected = false;
+               port->host_connected = false;
+-              wake_up_interruptible(&port->waitqueue);
+-              /* Let the app know the port is going down. */
+-              send_sigio_to_port(port);
++              wake_up_interruptible(&port->waitqueue);
+       }
+       if (is_console_port(port)) {
diff --git a/queue-3.10/virtio-console-quit-from-splice_write-if-pipe-nrbufs-is-0.patch b/queue-3.10/virtio-console-quit-from-splice_write-if-pipe-nrbufs-is-0.patch
new file mode 100644 (file)
index 0000000..45e77ea
--- /dev/null
@@ -0,0 +1,96 @@
+From 68c034fefe20eaf7d5569aae84584b07987ce50a Mon Sep 17 00:00:00 2001
+From: Yoshihiro YUNOMAE <yoshihiro.yunomae.ez@hitachi.com>
+Date: Tue, 23 Jul 2013 11:30:49 +0930
+Subject: virtio/console: Quit from splice_write if pipe->nrbufs is 0
+
+From: Yoshihiro YUNOMAE <yoshihiro.yunomae.ez@hitachi.com>
+
+commit 68c034fefe20eaf7d5569aae84584b07987ce50a upstream.
+
+Quit from splice_write if pipe->nrbufs is 0 for avoiding oops in virtio-serial.
+
+When an application was doing splice from a kernel buffer to virtio-serial on
+a guest, the application received signal(SIGINT). This situation will normally
+happen, but the kernel executed a kernel panic by oops as follows:
+
+ BUG: unable to handle kernel paging request at ffff882071c8ef28
+ IP: [<ffffffff812de48f>] sg_init_table+0x2f/0x50
+ PGD 1fac067 PUD 0
+ Oops: 0000 [#1] SMP
+ Modules linked in: lockd sunrpc bnep bluetooth rfkill ip6t_REJECT nf_conntrack_ipv6 nf_defrag_ipv6 xt_state nf_conntrack ip6table_filter ip6_tables snd_hda_intel snd_hda_codec snd_hwdep snd_pcm snd_page_alloc snd_timer snd microcode virtio_balloon virtio_net pcspkr soundcore i2c_piix4 i2c_core uinput floppy
+ CPU: 1 PID: 908 Comm: trace-cmd Not tainted 3.10.0+ #49
+ Hardware name: Bochs Bochs, BIOS Bochs 01/01/2007
+ task: ffff880071c64650 ti: ffff88007bf24000 task.ti: ffff88007bf24000
+ RIP: 0010:[<ffffffff812de48f>]  [<ffffffff812de48f>] sg_init_table+0x2f/0x50
+ RSP: 0018:ffff88007bf25dd8  EFLAGS: 00010286
+ RAX: 0000001fffffffe0 RBX: ffff882071c8ef28 RCX: 0000000000000000
+ RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff880071c8ef48
+ RBP: ffff88007bf25de8 R08: ffff88007fd15d40 R09: ffff880071c8ef48
+ R10: ffffea0001c71040 R11: ffffffff8139c555 R12: 0000000000000000
+ R13: ffff88007506a3c0 R14: ffff88007c862500 R15: ffff880071c8ef00
+ FS:  00007f0a3646c740(0000) GS:ffff88007fd00000(0000) knlGS:0000000000000000
+ CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
+ CR2: ffff882071c8ef28 CR3: 000000007acbb000 CR4: 00000000000006e0
+ DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
+ DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
+ Stack:
+  ffff880071c8ef48 ffff88007bf25e20 ffff88007bf25e88 ffffffff8139d6fa
+  ffff88007bf25e28 ffffffff8127a3f4 0000000000000000 0000000000000000
+  ffff880071c8ef48 0000100000000000 0000000000000003 ffff88007bf25e08
+ Call Trace:
+  [<ffffffff8139d6fa>] port_fops_splice_write+0xaa/0x130
+  [<ffffffff8127a3f4>] ? selinux_file_permission+0xc4/0x120
+  [<ffffffff8139d650>] ? wait_port_writable+0x1b0/0x1b0
+  [<ffffffff811a6fe0>] do_splice_from+0xa0/0x110
+  [<ffffffff811a951f>] SyS_splice+0x5ff/0x6b0
+  [<ffffffff8161f8c2>] system_call_fastpath+0x16/0x1b
+ Code: c1 e2 05 48 89 e5 48 83 ec 10 4c 89 65 f8 41 89 f4 31 f6 48 89 5d f0 48 89 fb e8 8d ce ff ff 41 8d 44 24 ff 48 c1 e0 05 48 01 c3 <48> 8b 03 48 83 e0 fe 48 83 c8 02 48 89 03 48 8b 5d f0 4c 8b 65
+ RIP  [<ffffffff812de48f>] sg_init_table+0x2f/0x50
+  RSP <ffff88007bf25dd8>
+ CR2: ffff882071c8ef28
+ ---[ end trace 86323505eb42ea8f ]---
+
+It seems to induce pagefault in sg_init_tabel() when pipe->nrbufs is equal to
+zero. This may happen in a following situation:
+
+(1) The application normally does splice(read) from a kernel buffer, then does
+    splice(write) to virtio-serial.
+(2) The application receives SIGINT when is doing splice(read), so splice(read)
+    is failed by EINTR. However, the application does not finish the operation.
+(3) The application tries to do splice(write) without pipe->nrbufs.
+(4) The virtio-console driver tries to touch scatterlist structure sgl in
+    sg_init_table(), but the region is out of bound.
+
+To avoid the case, a kernel should check whether pipe->nrbufs is empty or not
+when splice_write is executed in the virtio-console driver.
+
+V3: Add Reviewed-by lines and stable@ line in sign-off area.
+
+Signed-off-by: Yoshihiro YUNOMAE <yoshihiro.yunomae.ez@hitachi.com>
+Reviewed-by: Amit Shah <amit.shah@redhat.com>
+Reviewed-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
+Cc: Amit Shah <amit.shah@redhat.com>
+Cc: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/char/virtio_console.c |    7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/drivers/char/virtio_console.c
++++ b/drivers/char/virtio_console.c
+@@ -932,6 +932,13 @@ static ssize_t port_fops_splice_write(st
+       if (is_rproc_serial(port->out_vq->vdev))
+               return -EINVAL;
++      /*
++       * pipe->nrbufs == 0 means there are no data to transfer,
++       * so this returns just 0 for no data.
++       */
++      if (!pipe->nrbufs)
++              return 0;
++
+       ret = wait_port_writable(port, filp->f_flags & O_NONBLOCK);
+       if (ret < 0)
+               return ret;
diff --git a/queue-3.10/virtio-console-return-enodev-on-all-read-operations-after-unplug.patch b/queue-3.10/virtio-console-return-enodev-on-all-read-operations-after-unplug.patch
new file mode 100644 (file)
index 0000000..2fe2694
--- /dev/null
@@ -0,0 +1,50 @@
+From 96f97a83910cdb9d89d127c5ee523f8fc040a804 Mon Sep 17 00:00:00 2001
+From: Amit Shah <amit.shah@redhat.com>
+Date: Mon, 29 Jul 2013 14:23:21 +0930
+Subject: virtio: console: return -ENODEV on all read operations after unplug
+
+From: Amit Shah <amit.shah@redhat.com>
+
+commit 96f97a83910cdb9d89d127c5ee523f8fc040a804 upstream.
+
+If a port gets unplugged while a user is blocked on read(), -ENODEV is
+returned.  However, subsequent read()s returned 0, indicating there's no
+host-side connection (but not indicating the device went away).
+
+This also happened when a port was unplugged and the user didn't have
+any blocking operation pending.  If the user didn't monitor the SIGIO
+signal, they won't have a chance to find out if the port went away.
+
+Fix by returning -ENODEV on all read()s after the port gets unplugged.
+write() already behaves this way.
+
+Signed-off-by: Amit Shah <amit.shah@redhat.com>
+Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/char/virtio_console.c |    6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/char/virtio_console.c
++++ b/drivers/char/virtio_console.c
+@@ -749,6 +749,10 @@ static ssize_t port_fops_read(struct fil
+       port = filp->private_data;
++      /* Port is hot-unplugged. */
++      if (!port->guest_connected)
++              return -ENODEV;
++
+       if (!port_has_data(port)) {
+               /*
+                * If nothing's connected on the host just return 0 in
+@@ -765,7 +769,7 @@ static ssize_t port_fops_read(struct fil
+               if (ret < 0)
+                       return ret;
+       }
+-      /* Port got hot-unplugged. */
++      /* Port got hot-unplugged while we were waiting above. */
+       if (!port->guest_connected)
+               return -ENODEV;
+       /*