History

xen/netfront: force data bouncing when backend is untrusted

Bounce all data on the skbs to be transmitted into zeroed pages if the backend is untrusted. This avoids leaking data present in the pages shared with the backend but not part of the skb fragments. This requires introducing a new helper in order to allocate skbs with a size multiple of XEN_PAGE_SIZE so we don't leak contiguous data on the granted pages. Reporting whether the backend is to be trusted can be done using a module parameter, or from the xenstore frontend path as set by the toolstack when adding the device. This is CVE-2022-33741, part of XSA-403.

Roger Pau Monne committed 4 years ago

Commit 44910011 parent 307c8de1 file · +47 −2Browse files

Unified · Split

drivers/net/xen-netfront.c+47−2
@@ -66,6 +66,10 @@ module_param_named(max_queues, xennet_max_queues, uint, 0644); · 65 unmodified lines
6666 MODULE_PARM_DESC(max_queues,
6767 "Maximum number of queues per virtual interface");
6868
69+static bool __read_mostly xennet_trusted = true;
70+module_param_named(trusted, xennet_trusted, bool, 0644);
71+MODULE_PARM_DESC(trusted, "Is the backend trusted");
72+
6973 #define XENNET_TIMEOUT (5 * HZ)
7074
7175 static const struct ethtool_ops xennet_ethtool_ops;
@@ -173,6 +177,9 @@ struct netfront_info { · 101 unmodified lines
173177 /* Is device behaving sane? */
174178 bool broken;
175179
180+ /* Should skbs be bounced into a zeroed buffer? */
181+ bool bounce;
182+
176183 atomic_t rx_gso_checksum_fixup;
177184 };
178185
@@ -666,6 +673,33 @@ static int xennet_xdp_xmit(struct net_device *dev, int n, · 487 unmodified lines
666673 return nxmit;
667674 }
668675
676+struct sk_buff *bounce_skb(const struct sk_buff *skb)
677+{
678+ unsigned int headerlen = skb_headroom(skb);
679+ /* Align size to allocate full pages and avoid contiguous data leaks */
680+ unsigned int size = ALIGN(skb_end_offset(skb) + skb->data_len,
681+ XEN_PAGE_SIZE);
682+ struct sk_buff *n = alloc_skb(size, GFP_ATOMIC | __GFP_ZERO);
683+
684+ if (!n)
685+ return NULL;
686+
687+ if (!IS_ALIGNED((uintptr_t)n->head, XEN_PAGE_SIZE)) {
688+ WARN_ONCE(1, "misaligned skb allocated\n");
689+ kfree_skb(n);
690+ return NULL;
691+ }
692+
693+ /* Set the data pointer */
694+ skb_reserve(n, headerlen);
695+ /* Set the tail pointer and length */
696+ skb_put(n, skb->len);
697+
698+ BUG_ON(skb_copy_bits(skb, -headerlen, n->head, headerlen + skb->len));
699+
700+ skb_copy_header(n, skb);
701+ return n;
702+}
669703
670704 #define MAX_XEN_SKB_FRAGS (65536 / XEN_PAGE_SIZE + 1)
671705
@@ -719,9 +753,13 @@ static netdev_tx_t xennet_start_xmit(struct sk_buff *skb, struct net_device *dev · 47 unmodified lines
719753
720754 /* The first req should be at least ETH_HLEN size or the packet will be
721755 * dropped by netback.
756+ *
757+ * If the backend is not trusted bounce all data to zeroed pages to
758+ * avoid exposing contiguous data on the granted page not belonging to
759+ * the skb.
722760 */
723− if (unlikely(PAGE_SIZE - offset < ETH_HLEN)) {
724− nskb = skb_copy(skb, GFP_ATOMIC);
761+ if (np->bounce || unlikely(PAGE_SIZE - offset < ETH_HLEN)) {
762+ nskb = bounce_skb(skb);
725763 if (!nskb)
726764 goto drop;
727765 dev_consume_skb_any(skb);
@@ -2215,6 +2253,10 @@ static int talk_to_netback(struct xenbus_device *dev, · 1487 unmodified lines
22152253
22162254 info->netdev->irq = 0;
22172255
2256+ /* Check if backend is trusted. */
2257+ info->bounce = !xennet_trusted ||
2258+ !xenbus_read_unsigned(dev->nodename, "trusted", 1);
2259+
22182260 /* Check if backend supports multiple queues */
22192261 max_queues = xenbus_read_unsigned(info->xbdev->otherend,
22202262 "multi-queue-max-queues", 1);
@@ -2382,6 +2424,9 @@ static int xennet_connect(struct net_device *dev) · 161 unmodified lines
23822424 return err;
23832425 if (np->netback_has_xdp_headroom)
23842426 pr_info("backend supports XDP headroom\n");
2427+ if (np->bounce)
2428+ dev_info(&np->xbdev->dev,
2429+ "bouncing transmitted data to zeroed pages\n");
23852430
23862431 /* talk_to_netback() sets the correct number of queues */
23872432 num_queues = dev->real_num_tx_queues;