From 2f9efa0cf1b34ba94d6657cc923a42b0bd9fce8b Mon Sep 17 00:00:00 2001 From: "Justin M. Forbes" Date: Thu, 25 Apr 2019 07:23:14 -0500 Subject: [PATCH] Fix CVE-2019-3900 (rhbz 1698757 1702940) --- kernel.spec | 6 + ...vhost_net-fix-possible-infinite-loop.patch | 200 ++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 net-vhost_net-fix-possible-infinite-loop.patch diff --git a/kernel.spec b/kernel.spec index 684e9cf25..f8a689136 100644 --- a/kernel.spec +++ b/kernel.spec @@ -587,6 +587,9 @@ Patch507: 0001-Drop-that-for-now.patch # Submitted upstream at https://lkml.org/lkml/2019/4/23/89 Patch508: KEYS-Make-use-of-platform-keyring-for-module-signature.patch +# CVE-2019-3900 rhbz 1698757 1702940 +Patch524: net-vhost_net-fix-possible-infinite-loop.patch + # END OF PATCH DEFINITIONS %endif @@ -1860,6 +1863,9 @@ fi # # %changelog +* Thu Apr 25 2019 Justin M. Forbes +- Fix CVE-2019-3900 (rhbz 1698757 1702940) + * Wed Apr 24 2019 Jeremy Cline - 5.1.0-0.rc6.git2.1 - Linux v5.1-rc6-15-gba25b50d582f diff --git a/net-vhost_net-fix-possible-infinite-loop.patch b/net-vhost_net-fix-possible-infinite-loop.patch new file mode 100644 index 000000000..f45d84bb2 --- /dev/null +++ b/net-vhost_net-fix-possible-infinite-loop.patch @@ -0,0 +1,200 @@ +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 +X-Patchwork-Id: 10916185 +Return-Path: +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 ; + 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 ; + 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 ; + Thu, 25 Apr 2019 07:33:33 +0000 (UTC) +Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand + id S1726957AbfDYHd1 (ORCPT + ); + 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 ); + 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 +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: +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 +--- + 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);