From 399df8250122b6463d1f67ea69fbdd16c2155aba Mon Sep 17 00:00:00 2001 From: Brett Mastbergen Date: Thu, 1 May 2025 15:40:48 -0400 Subject: [PATCH 1/6] xen/blkfront: fix leaking data in shared pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit jira VULN-1437 cve CVE-2022-26365 commit-author Roger Pau Monne commit 2f446ffe9d737e9a844b97887919c4fda18246e7 upstream-diff One of the alloc_page calls has not been switched to GFP_KERNEL yet, so __GFP_ZERO is or'd with GFP_NOIO instead When allocating pages to be used for shared communication with the backend always zero them, this avoids leaking unintended data present on the pages. This is CVE-2022-26365, part of XSA-403. Signed-off-by: Roger Pau Monné Reviewed-by: Jan Beulich Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross (cherry picked from commit 2f446ffe9d737e9a844b97887919c4fda18246e7) Signed-off-by: Brett Mastbergen --- drivers/block/xen-blkfront.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index 5cb25b62d3a84..f1223dc30bd28 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -201,7 +201,7 @@ static int fill_grant_buffer(struct blkfront_info *info, int num) goto out_of_memory; if (info->feature_persistent) { - granted_page = alloc_page(GFP_NOIO); + granted_page = alloc_page(GFP_NOIO | __GFP_ZERO); if (!granted_page) { kfree(gnt_list_entry); goto out_of_memory; @@ -1707,7 +1707,8 @@ static int blkfront_setup_indirect(struct blkfront_info *info) BUG_ON(!list_empty(&info->indirect_pages)); for (i = 0; i < num; i++) { - struct page *indirect_page = alloc_page(GFP_NOIO); + struct page *indirect_page = alloc_page(GFP_NOIO | + __GFP_ZERO); if (!indirect_page) goto out_of_memory; list_add(&indirect_page->lru, &info->indirect_pages); From 7d898dfd9d15d80a40d76b07c5e9fae31b2a8b4d Mon Sep 17 00:00:00 2001 From: Brett Mastbergen Date: Thu, 1 May 2025 15:43:37 -0400 Subject: [PATCH 2/6] xen/netfront: fix leaking data in shared pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit jira VULN-1438 cve CVE-2022-33740 commit-author Roger Pau Monne commit 307c8de2b02344805ebead3440d8feed28f2f010 upstream-diff This kernel hasn't switched to page_pool_dev_alloc_pages so we are simply adding __GFP_ZERO to the existing alloc_page call. When allocating pages to be used for shared communication with the backend always zero them, this avoids leaking unintended data present on the pages. This is CVE-2022-33740, part of XSA-403. Signed-off-by: Roger Pau Monné Reviewed-by: Jan Beulich Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross (cherry picked from commit 307c8de2b02344805ebead3440d8feed28f2f010) Signed-off-by: Brett Mastbergen --- drivers/net/xen-netfront.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 3a824e40a2d7d..99d690fbcf581 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -266,7 +266,7 @@ static struct sk_buff *xennet_alloc_one_rx_buffer(struct netfront_queue *queue) if (unlikely(!skb)) return NULL; - page = alloc_page(GFP_ATOMIC | __GFP_NOWARN); + page = alloc_page(GFP_ATOMIC | __GFP_NOWARN | __GFP_ZERO); if (!page) { kfree_skb(skb); return NULL; From e3d43eb4ba4fa88a5f371891d7a9a0b16482adaf Mon Sep 17 00:00:00 2001 From: Brett Mastbergen Date: Thu, 1 May 2025 15:52:39 -0400 Subject: [PATCH 3/6] xen: introduce xenbus_read_unsigned() jira VULN-1439 cve-prereq CVE-2022-33741 commit-author Juergen Gross commit 9c53a1792a5e6c708897d0cb17f2a4509e499a52 There are multiple instances of code reading an optional unsigned parameter from Xenstore via xenbus_scanf(). Instead of repeating the same code over and over add a service function doing the job. Signed-off-by: Juergen Gross Reviewed-by: David Vrabel (cherry picked from commit 9c53a1792a5e6c708897d0cb17f2a4509e499a52) Signed-off-by: Brett Mastbergen --- drivers/xen/xenbus/xenbus_xs.c | 15 +++++++++++++++ include/xen/xenbus.h | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/drivers/xen/xenbus/xenbus_xs.c b/drivers/xen/xenbus/xenbus_xs.c index b6d5fff43d16b..0971c8c70f91c 100644 --- a/drivers/xen/xenbus/xenbus_xs.c +++ b/drivers/xen/xenbus/xenbus_xs.c @@ -526,6 +526,21 @@ int xenbus_scanf(struct xenbus_transaction t, } EXPORT_SYMBOL_GPL(xenbus_scanf); +/* Read an (optional) unsigned value. */ +unsigned int xenbus_read_unsigned(const char *dir, const char *node, + unsigned int default_val) +{ + unsigned int val; + int ret; + + ret = xenbus_scanf(XBT_NIL, dir, node, "%u", &val); + if (ret <= 0) + val = default_val; + + return val; +} +EXPORT_SYMBOL_GPL(xenbus_read_unsigned); + /* Single printf and write: returns -errno or 0. */ int xenbus_printf(struct xenbus_transaction t, const char *dir, const char *node, const char *fmt, ...) diff --git a/include/xen/xenbus.h b/include/xen/xenbus.h index 569c07f2e3446..8f9d6607df8aa 100644 --- a/include/xen/xenbus.h +++ b/include/xen/xenbus.h @@ -144,6 +144,10 @@ __scanf(4, 5) int xenbus_scanf(struct xenbus_transaction t, const char *dir, const char *node, const char *fmt, ...); +/* Read an (optional) unsigned value. */ +unsigned int xenbus_read_unsigned(const char *dir, const char *node, + unsigned int default_val); + /* Single printf and write: returns -errno or 0. */ __printf(4, 5) int xenbus_printf(struct xenbus_transaction t, From 224be9b8279a4c6f0fb1e4ece634f98e86c6ec72 Mon Sep 17 00:00:00 2001 From: Brett Mastbergen Date: Fri, 2 May 2025 12:28:25 -0400 Subject: [PATCH 4/6] net: Rename and export copy_skb_header jira VULN-1439 cve-prereq CVE-2022-33741 commit-author Ilya Lesokhin commit 08303c189581c985e60f588ad92a041e46b6e307 copy_skb_header is renamed to skb_copy_header and exported. Exposing this function give more flexibility in copying SKBs. skb_copy and skb_copy_expand do not give enough control over which parts are copied. Signed-off-by: Ilya Lesokhin Signed-off-by: Boris Pismenny Signed-off-by: David S. Miller (cherry picked from commit 08303c189581c985e60f588ad92a041e46b6e307) Signed-off-by: Brett Mastbergen --- include/linux/skbuff.h | 1 + net/core/skbuff.c | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 9d1e1ecf53272..e52660cd71326 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1030,6 +1030,7 @@ static inline struct sk_buff *alloc_skb_head(gfp_t priority) struct sk_buff *skb_morph(struct sk_buff *dst, struct sk_buff *src); int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask); struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t priority); +void skb_copy_header(struct sk_buff *new, const struct sk_buff *old); struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t priority); struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, gfp_t gfp_mask); diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 4bc9606eac4af..eb2579614eba9 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -1115,7 +1115,7 @@ static void skb_headers_offset_update(struct sk_buff *skb, int off) skb->inner_mac_header += off; } -static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old) +void skb_copy_header(struct sk_buff *new, const struct sk_buff *old) { __copy_skb_header(new, old); @@ -1123,6 +1123,7 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old) skb_shinfo(new)->gso_segs = skb_shinfo(old)->gso_segs; skb_shinfo(new)->gso_type = skb_shinfo(old)->gso_type; } +EXPORT_SYMBOL(skb_copy_header); static inline int skb_alloc_rx_flag(const struct sk_buff *skb) { @@ -1166,7 +1167,7 @@ struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask) if (skb_copy_bits(skb, -headerlen, n->head, headerlen + skb->len)) BUG(); - copy_skb_header(n, skb); + skb_copy_header(n, skb); return n; } EXPORT_SYMBOL(skb_copy); @@ -1225,7 +1226,7 @@ struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, gfp_t gfp_mask) skb_clone_fraglist(n); } - copy_skb_header(n, skb); + skb_copy_header(n, skb); out: return n; } @@ -1396,7 +1397,7 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb, skb->len + head_copy_len)) BUG(); - copy_skb_header(n, skb); + skb_copy_header(n, skb); skb_headers_offset_update(n, newheadroom - oldheadroom); From beb7c0c3d5082686d9618540f4ce8bb0dc52168a Mon Sep 17 00:00:00 2001 From: Brett Mastbergen Date: Thu, 1 May 2025 16:02:10 -0400 Subject: [PATCH 5/6] xen/netfront: force data bouncing when backend is untrusted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit jira VULN-1439 cve CVE-2022-33741 commit-author Roger Pau Monne commit 4491001c2e0fa69efbb748c96ec96b100a5cdb7e upstream-diff Some merge conflicts were fixed up mainly due to the fact that this version of the driver does not have xdp support. The content of the added code itself is identical to the upstream change with the exception of PAGE_SIZE replacing XEN_PAGE_SIZE. In this kernel there is no difference between the two where as in future kernels the ARM kernel page size might be 64k while the xen page size stays 4k. 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. Signed-off-by: Roger Pau Monné Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross (cherry picked from commit 4491001c2e0fa69efbb748c96ec96b100a5cdb7e) Signed-off-by: Brett Mastbergen --- drivers/net/xen-netfront.c | 51 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 99d690fbcf581..9b9296705d353 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -64,6 +64,10 @@ module_param_named(max_queues, xennet_max_queues, uint, 0644); MODULE_PARM_DESC(max_queues, "Maximum number of queues per virtual interface"); +static bool __read_mostly xennet_trusted = true; +module_param_named(trusted, xennet_trusted, bool, 0644); +MODULE_PARM_DESC(trusted, "Is the backend trusted"); + static const struct ethtool_ops xennet_ethtool_ops; struct netfront_cb { @@ -160,6 +164,9 @@ struct netfront_info { struct netfront_stats __percpu *rx_stats; struct netfront_stats __percpu *tx_stats; + /* Should skbs be bounced into a zeroed buffer? */ + bool bounce; + atomic_t rx_gso_checksum_fixup; }; @@ -516,6 +523,34 @@ static u16 xennet_select_queue(struct net_device *dev, struct sk_buff *skb, return queue_idx; } +struct sk_buff *bounce_skb(const struct sk_buff *skb) +{ + unsigned int headerlen = skb_headroom(skb); + /* Align size to allocate full pages and avoid contiguous data leaks */ + unsigned int size = ALIGN(skb_end_offset(skb) + skb->data_len, + PAGE_SIZE); + struct sk_buff *n = alloc_skb(size, GFP_ATOMIC | __GFP_ZERO); + + if (!n) + return NULL; + + if (!IS_ALIGNED((uintptr_t)n->head, PAGE_SIZE)) { + WARN_ONCE(1, "misaligned skb allocated\n"); + kfree_skb(n); + return NULL; + } + + /* Set the data pointer */ + skb_reserve(n, headerlen); + /* Set the tail pointer and length */ + skb_put(n, skb->len); + + BUG_ON(skb_copy_bits(skb, -headerlen, n->head, headerlen + skb->len)); + + skb_copy_header(n, skb); + return n; +} + static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct netfront_info *np = netdev_priv(dev); @@ -563,9 +598,13 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev) /* The first req should be at least ETH_HLEN size or the packet will be * dropped by netback. + * + * If the backend is not trusted bounce all data to zeroed pages to + * avoid exposing contiguous data on the granted page not belonging to + * the skb. */ - if (unlikely(PAGE_SIZE - offset < ETH_HLEN)) { - nskb = skb_copy(skb, GFP_ATOMIC); + if (np->bounce || unlikely(PAGE_SIZE - offset < ETH_HLEN)) { + nskb = bounce_skb(skb); if (!nskb) goto drop; dev_kfree_skb_any(skb); @@ -1774,6 +1813,10 @@ static int talk_to_netback(struct xenbus_device *dev, info->netdev->irq = 0; + /* Check if backend is trusted. */ + info->bounce = !xennet_trusted || + !xenbus_read_unsigned(dev->nodename, "trusted", 1); + /* Check if backend supports multiple queues */ err = xenbus_scanf(XBT_NIL, info->xbdev->otherend, "multi-queue-max-queues", "%u", &max_queues); @@ -1936,6 +1979,10 @@ static int xennet_connect(struct net_device *dev) if (err) return err; + if (np->bounce) + dev_info(&np->xbdev->dev, + "bouncing transmitted data to zeroed pages\n"); + /* talk_to_netback() sets the correct number of queues */ num_queues = dev->real_num_tx_queues; From 4944724aab0d2840d8da4e4111bd6c2522f540a4 Mon Sep 17 00:00:00 2001 From: Brett Mastbergen Date: Thu, 1 May 2025 16:40:08 -0400 Subject: [PATCH 6/6] xen/blkfront: force data bouncing when backend is untrusted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit jira VULN-1039 cve CVE-2022-33742 commit-author Roger Pau Monne commit 2400617da7eebf9167d71a46122828bc479d64c9 upstream-diff There were a lot of merge conflicts with this change as the upstream version of this file has had a lot of development. Still there is pretty much a 1:1 mapping of the changes against the upstream and the changes against this kernel Split the current bounce buffering logic used with persistent grants into it's own option, and allow enabling it independently of persistent grants. This allows to reuse the same code paths to perform the bounce buffering required to avoid leaking contiguous data in shared pages not part of the request fragments. 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-33742, part of XSA-403. Signed-off-by: Roger Pau Monné Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross (cherry picked from commit 2400617da7eebf9167d71a46122828bc479d64c9) Signed-off-by: Brett Mastbergen --- drivers/block/xen-blkfront.c | 41 ++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index f1223dc30bd28..eb68946df5b16 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -98,6 +98,10 @@ static unsigned int xen_blkif_max_segments = 32; module_param_named(max, xen_blkif_max_segments, int, S_IRUGO); MODULE_PARM_DESC(max, "Maximum amount of segments in indirect requests (default is 32)"); +static bool __read_mostly xen_blkif_trusted = true; +module_param_named(trusted, xen_blkif_trusted, bool, 0644); +MODULE_PARM_DESC(trusted, "Is the backend trusted"); + #define BLK_RING_SIZE __CONST_RING_SIZE(blkif, PAGE_SIZE) /* @@ -131,6 +135,7 @@ struct blkfront_info unsigned int discard_granularity; unsigned int discard_alignment; unsigned int feature_persistent:1; + unsigned int bounce:1; unsigned int max_indirect_segments; int is_ready; }; @@ -200,7 +205,7 @@ static int fill_grant_buffer(struct blkfront_info *info, int num) if (!gnt_list_entry) goto out_of_memory; - if (info->feature_persistent) { + if (info->bounce) { granted_page = alloc_page(GFP_NOIO | __GFP_ZERO); if (!granted_page) { kfree(gnt_list_entry); @@ -220,7 +225,7 @@ static int fill_grant_buffer(struct blkfront_info *info, int num) list_for_each_entry_safe(gnt_list_entry, n, &info->grants, node) { list_del(&gnt_list_entry->node); - if (info->feature_persistent) + if (info->bounce) __free_page(pfn_to_page(gnt_list_entry->pfn)); kfree(gnt_list_entry); i--; @@ -249,7 +254,7 @@ static struct grant *get_grant(grant_ref_t *gref_head, /* Assign a gref to this page */ gnt_list_entry->gref = gnttab_claim_grant_reference(gref_head); BUG_ON(gnt_list_entry->gref == -ENOSPC); - if (!info->feature_persistent) { + if (!info->bounce) { BUG_ON(!pfn); gnt_list_entry->pfn = pfn; } @@ -506,7 +511,7 @@ static int blkif_queue_request(struct request *req) kunmap_atomic(segments); n = i / SEGS_PER_INDIRECT_FRAME; - if (!info->feature_persistent) { + if (!info->bounce) { struct page *indirect_page; /* Fetch a pre-allocated page to use for indirect grefs */ @@ -527,7 +532,7 @@ static int blkif_queue_request(struct request *req) info->shadow[id].grants_used[i] = gnt_list_entry; - if (rq_data_dir(req) && info->feature_persistent) { + if (rq_data_dir(req) && info->bounce) { char *bvec_data; void *shared_data; @@ -711,11 +716,12 @@ static const char *flush_info(unsigned int feature_flush) static void xlvbd_flush(struct blkfront_info *info) { blk_queue_flush(info->rq, info->feature_flush); - pr_info("blkfront: %s: %s %s %s %s %s\n", + pr_info("blkfront: %s: %s %s %s %s %s %s %s\n", info->gd->disk_name, flush_info(info->feature_flush), "persistent grants:", info->feature_persistent ? "enabled;" : "disabled;", "indirect descriptors:", - info->max_indirect_segments ? "enabled;" : "disabled;"); + info->max_indirect_segments ? "enabled;" : "disabled;", + "bounce buffer:", info->bounce ? "enabled" : "disabled;"); } static int xen_translate_vdev(int vdevice, int *minor, unsigned int *offset) @@ -962,7 +968,7 @@ static void blkif_free(struct blkfront_info *info, int suspend) 0, 0UL); info->persistent_gnts_c--; } - if (info->feature_persistent) + if (info->bounce) __free_page(pfn_to_page(persistent_gnt->pfn)); kfree(persistent_gnt); } @@ -976,7 +982,7 @@ static void blkif_free(struct blkfront_info *info, int suspend) if (!list_empty(&info->indirect_pages)) { struct page *indirect_page, *n; - BUG_ON(info->feature_persistent); + BUG_ON(info->bounce); list_for_each_entry_safe(indirect_page, n, &info->indirect_pages, lru) { list_del(&indirect_page->lru); __free_page(indirect_page); @@ -997,7 +1003,7 @@ static void blkif_free(struct blkfront_info *info, int suspend) for (j = 0; j < segs; j++) { persistent_gnt = info->shadow[i].grants_used[j]; gnttab_end_foreign_access(persistent_gnt->gref, 0, 0UL); - if (info->feature_persistent) + if (info->bounce) __free_page(pfn_to_page(persistent_gnt->pfn)); kfree(persistent_gnt); } @@ -1057,7 +1063,7 @@ static void blkif_completion(struct blk_shadow *s, struct blkfront_info *info, nseg = s->req.operation == BLKIF_OP_INDIRECT ? s->req.u.indirect.nr_segments : s->req.u.rw.nr_segments; - if (bret->operation == BLKIF_OP_READ && info->feature_persistent) { + if (bret->operation == BLKIF_OP_READ && info->bounce) { /* * Copy the data received from the backend into the bvec. * Since bv_offset can be different than 0, and bv_len different @@ -1293,6 +1299,10 @@ static int talk_to_blkback(struct xenbus_device *dev, struct xenbus_transaction xbt; int err; + /* Check if backend is trusted. */ + info->bounce = !xen_blkif_trusted || + !xenbus_read_unsigned(dev->nodename, "trusted", 1); + /* Create shared ring, alloc event channel. */ err = setup_blkring(dev, info); if (err) @@ -1697,10 +1707,10 @@ static int blkfront_setup_indirect(struct blkfront_info *info) if (err) goto out_of_memory; - if (!info->feature_persistent && info->max_indirect_segments) { + if (!info->bounce && info->max_indirect_segments) { /* - * We are using indirect descriptors but not persistent - * grants, we need to allocate a set of pages that can be + * We are using indirect descriptors but don't have a bounce + * buffer, we need to allocate a set of pages that can be * used for mapping indirect grefs */ int num = INDIRECT_GREFS(segs) * BLK_RING_SIZE; @@ -1864,6 +1874,9 @@ static void blkfront_connect(struct blkfront_info *info) else info->feature_persistent = persistent; + if (info->feature_persistent) + info->bounce = true; + err = blkfront_setup_indirect(info); if (err) { xenbus_dev_fatal(info->xbdev, err, "setup_indirect at %s",