201 lines
7.3 KiB
Diff
201 lines
7.3 KiB
Diff
|
From patchwork Thu Apr 25 07:33:19 2019
|
||
|
Content-Type: text/plain; charset="utf-8"
|
||
|
MIME-Version: 1.0
|
||
|
Content-Transfer-Encoding: 7bit
|
||
|
X-Patchwork-Submitter: Jason Wang <jasowang@redhat.com>
|
||
|
X-Patchwork-Id: 10916185
|
||
|
Return-Path: <kvm-owner@kernel.org>
|
||
|
Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org
|
||
|
[172.30.200.125])
|
||
|
by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E4F501575
|
||
|
for <patchwork-kvm@patchwork.kernel.org>;
|
||
|
Thu, 25 Apr 2019 07:33:33 +0000 (UTC)
|
||
|
Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1])
|
||
|
by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D276828BD7
|
||
|
for <patchwork-kvm@patchwork.kernel.org>;
|
||
|
Thu, 25 Apr 2019 07:33:33 +0000 (UTC)
|
||
|
Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486)
|
||
|
id C64AC28BE1; Thu, 25 Apr 2019 07:33:33 +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=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI,
|
||
|
RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1
|
||
|
Received: from vger.kernel.org (vger.kernel.org [209.132.180.67])
|
||
|
by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 590B228BD7
|
||
|
for <patchwork-kvm@patchwork.kernel.org>;
|
||
|
Thu, 25 Apr 2019 07:33:33 +0000 (UTC)
|
||
|
Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand
|
||
|
id S1726957AbfDYHd1 (ORCPT
|
||
|
<rfc822;patchwork-kvm@patchwork.kernel.org>);
|
||
|
Thu, 25 Apr 2019 03:33:27 -0400
|
||
|
Received: from mx1.redhat.com ([209.132.183.28]:60130 "EHLO mx1.redhat.com"
|
||
|
rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP
|
||
|
id S1726317AbfDYHd1 (ORCPT <rfc822;kvm@vger.kernel.org>);
|
||
|
Thu, 25 Apr 2019 03:33:27 -0400
|
||
|
Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com
|
||
|
[10.5.11.22])
|
||
|
(using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits))
|
||
|
(No client certificate requested)
|
||
|
by mx1.redhat.com (Postfix) with ESMTPS id C2BCE3002619;
|
||
|
Thu, 25 Apr 2019 07:33:26 +0000 (UTC)
|
||
|
Received: from hp-dl380pg8-02.lab.eng.pek2.redhat.com
|
||
|
(hp-dl380pg8-02.lab.eng.pek2.redhat.com [10.73.8.12])
|
||
|
by smtp.corp.redhat.com (Postfix) with ESMTP id 5DA021001DDB;
|
||
|
Thu, 25 Apr 2019 07:33:21 +0000 (UTC)
|
||
|
From: Jason Wang <jasowang@redhat.com>
|
||
|
To: mst@redhat.com, jasowang@redhat.com, kvm@vger.kernel.org,
|
||
|
virtualization@lists.linux-foundation.org, netdev@vger.kernel.org,
|
||
|
linux-kernel@vger.kernel.org
|
||
|
Cc: ppandit@redhat.com
|
||
|
Subject: [PATCH net] vhost_net: fix possible infinite loop
|
||
|
Date: Thu, 25 Apr 2019 03:33:19 -0400
|
||
|
Message-Id: <1556177599-56248-1-git-send-email-jasowang@redhat.com>
|
||
|
X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22
|
||
|
X-Greylist: Sender IP whitelisted,
|
||
|
not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.40]);
|
||
|
Thu, 25 Apr 2019 07:33:26 +0000 (UTC)
|
||
|
Sender: kvm-owner@vger.kernel.org
|
||
|
Precedence: bulk
|
||
|
List-ID: <kvm.vger.kernel.org>
|
||
|
X-Mailing-List: kvm@vger.kernel.org
|
||
|
X-Virus-Scanned: ClamAV using ClamSMTP
|
||
|
|
||
|
When the rx buffer is too small for a packet, we will discard the vq
|
||
|
descriptor and retry it for the next packet:
|
||
|
|
||
|
while ((sock_len = vhost_net_rx_peek_head_len(net, sock->sk,
|
||
|
&busyloop_intr))) {
|
||
|
...
|
||
|
/* On overrun, truncate and discard */
|
||
|
if (unlikely(headcount > UIO_MAXIOV)) {
|
||
|
iov_iter_init(&msg.msg_iter, READ, vq->iov, 1, 1);
|
||
|
err = sock->ops->recvmsg(sock, &msg,
|
||
|
1, MSG_DONTWAIT | MSG_TRUNC);
|
||
|
pr_debug("Discarded rx packet: len %zd\n", sock_len);
|
||
|
continue;
|
||
|
}
|
||
|
...
|
||
|
}
|
||
|
|
||
|
This makes it possible to trigger a infinite while..continue loop
|
||
|
through the co-opreation of two VMs like:
|
||
|
|
||
|
1) Malicious VM1 allocate 1 byte rx buffer and try to slow down the
|
||
|
vhost process as much as possible e.g using indirect descriptors or
|
||
|
other.
|
||
|
2) Malicious VM2 generate packets to VM1 as fast as possible
|
||
|
|
||
|
Fixing this by checking against weight at the end of RX and TX
|
||
|
loop. This also eliminate other similar cases when:
|
||
|
|
||
|
- userspace is consuming the packets in the meanwhile
|
||
|
- theoretical TOCTOU attack if guest moving avail index back and forth
|
||
|
to hit the continue after vhost find guest just add new buffers
|
||
|
|
||
|
This addresses CVE-2019-3900.
|
||
|
|
||
|
Fixes: d8316f3991d20 ("vhost: fix total length when packets are too short")
|
||
|
Fixes: 3a4d5c94e9593 ("vhost_net: a kernel-level virtio server")
|
||
|
Signed-off-by: Jason Wang <jasowang@redhat.com>
|
||
|
---
|
||
|
drivers/vhost/net.c | 41 +++++++++++++++++++++--------------------
|
||
|
1 file changed, 21 insertions(+), 20 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
|
||
|
index df51a35..fb46e6b 100644
|
||
|
--- a/drivers/vhost/net.c
|
||
|
+++ b/drivers/vhost/net.c
|
||
|
@@ -778,8 +778,9 @@ static void handle_tx_copy(struct vhost_net *net, struct socket *sock)
|
||
|
int err;
|
||
|
int sent_pkts = 0;
|
||
|
bool sock_can_batch = (sock->sk->sk_sndbuf == INT_MAX);
|
||
|
+ bool next_round = false;
|
||
|
|
||
|
- for (;;) {
|
||
|
+ do {
|
||
|
bool busyloop_intr = false;
|
||
|
|
||
|
if (nvq->done_idx == VHOST_NET_BATCH)
|
||
|
@@ -845,11 +846,10 @@ static void handle_tx_copy(struct vhost_net *net, struct socket *sock)
|
||
|
vq->heads[nvq->done_idx].id = cpu_to_vhost32(vq, head);
|
||
|
vq->heads[nvq->done_idx].len = 0;
|
||
|
++nvq->done_idx;
|
||
|
- if (vhost_exceeds_weight(++sent_pkts, total_len)) {
|
||
|
- vhost_poll_queue(&vq->poll);
|
||
|
- break;
|
||
|
- }
|
||
|
- }
|
||
|
+ } while (!(next_round = vhost_exceeds_weight(++sent_pkts, total_len)));
|
||
|
+
|
||
|
+ if (next_round)
|
||
|
+ vhost_poll_queue(&vq->poll);
|
||
|
|
||
|
vhost_tx_batch(net, nvq, sock, &msg);
|
||
|
}
|
||
|
@@ -873,8 +873,9 @@ static void handle_tx_zerocopy(struct vhost_net *net, struct socket *sock)
|
||
|
struct vhost_net_ubuf_ref *uninitialized_var(ubufs);
|
||
|
bool zcopy_used;
|
||
|
int sent_pkts = 0;
|
||
|
+ bool next_round = false;
|
||
|
|
||
|
- for (;;) {
|
||
|
+ do {
|
||
|
bool busyloop_intr;
|
||
|
|
||
|
/* Release DMAs done buffers first */
|
||
|
@@ -951,11 +952,10 @@ static void handle_tx_zerocopy(struct vhost_net *net, struct socket *sock)
|
||
|
else
|
||
|
vhost_zerocopy_signal_used(net, vq);
|
||
|
vhost_net_tx_packet(net);
|
||
|
- if (unlikely(vhost_exceeds_weight(++sent_pkts, total_len))) {
|
||
|
- vhost_poll_queue(&vq->poll);
|
||
|
- break;
|
||
|
- }
|
||
|
- }
|
||
|
+ } while (!(next_round = vhost_exceeds_weight(++sent_pkts, total_len)));
|
||
|
+
|
||
|
+ if (next_round)
|
||
|
+ vhost_poll_queue(&vq->poll);
|
||
|
}
|
||
|
|
||
|
/* Expects to be always run from workqueue - which acts as
|
||
|
@@ -1134,6 +1134,7 @@ static void handle_rx(struct vhost_net *net)
|
||
|
struct iov_iter fixup;
|
||
|
__virtio16 num_buffers;
|
||
|
int recv_pkts = 0;
|
||
|
+ bool next_round = false;
|
||
|
|
||
|
mutex_lock_nested(&vq->mutex, VHOST_NET_VQ_RX);
|
||
|
sock = vq->private_data;
|
||
|
@@ -1153,8 +1154,11 @@ static void handle_rx(struct vhost_net *net)
|
||
|
vq->log : NULL;
|
||
|
mergeable = vhost_has_feature(vq, VIRTIO_NET_F_MRG_RXBUF);
|
||
|
|
||
|
- while ((sock_len = vhost_net_rx_peek_head_len(net, sock->sk,
|
||
|
- &busyloop_intr))) {
|
||
|
+ do {
|
||
|
+ sock_len = vhost_net_rx_peek_head_len(net, sock->sk,
|
||
|
+ &busyloop_intr);
|
||
|
+ if (!sock_len)
|
||
|
+ break;
|
||
|
sock_len += sock_hlen;
|
||
|
vhost_len = sock_len + vhost_hlen;
|
||
|
headcount = get_rx_bufs(vq, vq->heads + nvq->done_idx,
|
||
|
@@ -1239,12 +1243,9 @@ static void handle_rx(struct vhost_net *net)
|
||
|
vhost_log_write(vq, vq_log, log, vhost_len,
|
||
|
vq->iov, in);
|
||
|
total_len += vhost_len;
|
||
|
- if (unlikely(vhost_exceeds_weight(++recv_pkts, total_len))) {
|
||
|
- vhost_poll_queue(&vq->poll);
|
||
|
- goto out;
|
||
|
- }
|
||
|
- }
|
||
|
- if (unlikely(busyloop_intr))
|
||
|
+ } while (!(next_round = vhost_exceeds_weight(++recv_pkts, total_len)));
|
||
|
+
|
||
|
+ if (unlikely(busyloop_intr || next_round))
|
||
|
vhost_poll_queue(&vq->poll);
|
||
|
else
|
||
|
vhost_net_enable_vq(net, vq);
|