Xen Security Advisory 448 v2 (CVE-2023-46838) - Linux: netback processing of zero-length transmit fragment

Xen.org security team posted 1 patch 3 months, 1 week ago
Failed in applying to current master (apply log)
Xen Security Advisory 448 v2 (CVE-2023-46838) - Linux: netback processing of zero-length transmit fragment
Posted by Xen.org security team 3 months, 1 week ago
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

            Xen Security Advisory CVE-2023-46838 / XSA-448
                               version 2

      Linux: netback processing of zero-length transmit fragment

UPDATES IN VERSION 2
====================

Public release.

ISSUE DESCRIPTION
=================

Transmit requests in Xen's virtual network protocol can consist of
multiple parts.  While not really useful, except for the initial part
any of them may be of zero length, i.e. carry no data at all.  Besides a
certain initial portion of the to be transferred data, these parts are
directly translated into what Linux calls SKB fragments.  Such converted
request parts can, when for a particular SKB they are all of length
zero, lead to a de-reference of NULL in core networking code.

IMPACT
======

An unprivileged guest can cause Denial of Service (DoS) of the host by
sending network packets to the backend, causing the backend to crash.

Data corruption or privilege escalation have not been ruled out.

VULNERABLE SYSTEMS
==================

All systems using a Linux based network backend with kernel 4.14 and
newer are vulnerable.  Earlier versions may also be vulnerable.  Systems
using other network backends are not known to be vulnerable.

MITIGATION
==========

Using a userspace PV network backend (e.g. the qemu based "qnic" backend)
will mitigate the problem.

Using a dedicated network driver domain per guest will mitigate the
problem.

CREDITS
=======

This issue was discovered by Pratyush Yadav of Amazon.

RESOLUTION
==========

Applying the attached patch resolves this issue.

xsa448-linux.patch           Linux 6.7-rc - 6.5

$ sha256sum xsa448*
f8c87cf546c2bc70970ca151c0ec8c1940f969e29c4cb3d2ec37ff9e43ddfc36  xsa448-linux.patch
$

NOTE CONCERNING EARLY DISCLOSURE
================================

The embargo was intended to be 2024-01-23 12:00 UTC, but a downstream
had a mixup of days and published early.

DEPLOYMENT DURING EMBARGO
=========================

Deployment of the patches and/or mitigations described above (or
others which are substantially similar) is permitted during the
embargo, even on public-facing systems with untrusted guest users and
administrators.

But: Distribution of updated software is prohibited (except to other
members of the predisclosure list).

Predisclosure list members who wish to deploy significantly different
patches and/or mitigations, please contact the Xen Project Security
Team.

(Note: this during-embargo deployment notice is retained in
post-embargo publicly released Xen Project advisories, even though it
is then no longer applicable.  This is to enable the community to have
oversight of the Xen Project Security Team's decisionmaking.)

For more information about permissible uses of embargoed information,
consult the Xen Project community's agreed Security Policy:
  http://www.xenproject.org/security-policy.html
-----BEGIN PGP SIGNATURE-----

iQFABAEBCAAqFiEEI+MiLBRfRHX6gGCng/4UyVfoK9kFAmWutGMMHHBncEB4ZW4u
b3JnAAoJEIP+FMlX6CvZ9h0H/26sgfTHO0vnTZ8cnisn3aC5VTvrx9nY5fcCe2cJ
/KgN3q3mtb3w41/2LD/rR0Zpw4SkeTaFp69Mz2hQa37gLVDSK5lDJDR61lwhiwrQ
MSsdPHs91EDJhF6aX/S7wsQkBZYPq1S9aOuIxJbDYN3D9WsTUWvuocXNxeqTx5q9
iWVSJTH5NkRSAaIVldyNVkQ7pWaSrwqmBzolnrZIsDUjYU1Lk/j0u6GFbkOF9SIg
onFiFbJhCOaIZOIP2Tfz7nHGBnxucI4cjjwy4BWM+Va35Pg4mbHaBuVGnQsaBtVF
UdY6/jw6Qk4ktV34il3+jlgGfAFC6GILJoraASjaFCEQ7jM=
=IPLz
-----END PGP SIGNATURE-----
From: Jan Beulich <jbeulich@suse.com>
Subject: xen-netback: don't produce zero-size SKB frags

While frontends may submit zero-size requests (wasting a precious slot),
core networking code as of at least 3ece782693c4b ("sock: skb_copy_ubufs
support for compound pages") can't deal with SKBs when they have all
zero-size fragments. Respond to empty requests right when populating
fragments; all further processing is fragment based and hence won't
encounter these empty requests anymore.

In a way this should have been that way from the beginning: When no data
is to be transferred for a particular request, there's not even a point
in validating the respective grant ref. That's no different from e.g.
passing NULL into memcpy() when at the same time the size is 0.

This is XSA-448 / CVE-2023-46838.

Reported-by: Pratyush Yadav <ptyadav@amazon.de>
Cc: stable@vger.kernel.org
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Paul Durrant <paul@xen.org>
Acked-by: Pratyush Yadav <ptyadav@amazon.de>

--- a/drivers/net/xen-netback/netback.c
+++ b/drivers/net/xen-netback/netback.c
@@ -463,12 +463,25 @@ static void xenvif_get_requests(struct x
 	}
 
 	for (shinfo->nr_frags = 0; nr_slots > 0 && shinfo->nr_frags < MAX_SKB_FRAGS;
-	     shinfo->nr_frags++, gop++, nr_slots--) {
+	     nr_slots--) {
+		if (unlikely(!txp->size)) {
+			unsigned long flags;
+
+			spin_lock_irqsave(&queue->response_lock, flags);
+			make_tx_response(queue, txp, 0, XEN_NETIF_RSP_OKAY);
+			push_tx_responses(queue);
+			spin_unlock_irqrestore(&queue->response_lock, flags);
+			++txp;
+			continue;
+		}
+
 		index = pending_index(queue->pending_cons++);
 		pending_idx = queue->pending_ring[index];
 		xenvif_tx_create_map_op(queue, pending_idx, txp,
 				        txp == first ? extra_count : 0, gop);
 		frag_set_pending_idx(&frags[shinfo->nr_frags], pending_idx);
+		++shinfo->nr_frags;
+		++gop;
 
 		if (txp == first)
 			txp = txfrags;
@@ -481,20 +494,39 @@ static void xenvif_get_requests(struct x
 		shinfo = skb_shinfo(nskb);
 		frags = shinfo->frags;
 
-		for (shinfo->nr_frags = 0; shinfo->nr_frags < nr_slots;
-		     shinfo->nr_frags++, txp++, gop++) {
+		for (shinfo->nr_frags = 0; shinfo->nr_frags < nr_slots; ++txp) {
+			if (unlikely(!txp->size)) {
+				unsigned long flags;
+
+				spin_lock_irqsave(&queue->response_lock, flags);
+				make_tx_response(queue, txp, 0,
+						 XEN_NETIF_RSP_OKAY);
+				push_tx_responses(queue);
+				spin_unlock_irqrestore(&queue->response_lock,
+						       flags);
+				continue;
+			}
+
 			index = pending_index(queue->pending_cons++);
 			pending_idx = queue->pending_ring[index];
 			xenvif_tx_create_map_op(queue, pending_idx, txp, 0,
 						gop);
 			frag_set_pending_idx(&frags[shinfo->nr_frags],
 					     pending_idx);
+			++shinfo->nr_frags;
+			++gop;
 		}
 
-		skb_shinfo(skb)->frag_list = nskb;
-	} else if (nskb) {
+		if (shinfo->nr_frags) {
+			skb_shinfo(skb)->frag_list = nskb;
+			nskb = NULL;
+		}
+	}
+
+	if (nskb) {
 		/* A frag_list skb was allocated but it is no longer needed
-		 * because enough slots were converted to copy ops above.
+		 * because enough slots were converted to copy ops above or some
+		 * were empty.
 		 */
 		kfree_skb(nskb);
 	}