FAILED: patch "[PATCH] VDUSE: avoid leaking information to userspace" failed to apply to 6.6-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.6-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.6.y
git checkout FETCH_HEAD
git cherry-pick -x 9c1523803445ee0348f62b77793266dd981596e0
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026072041-rectal-mammogram-740b@gregkh' --subject-prefix 'PATCH 6.6.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 9c1523803445ee0348f62b77793266dd981596e0 Mon Sep 17 00:00:00 2001
From: Jason Wang <[email protected]>
Date: Fri, 30 Jan 2026 13:07:50 +0800
Subject: [PATCH] VDUSE: avoid leaking information to userspace
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The bounceing is not necessarily page aligned, so current VDUSE can leak kernel information through mapping bounce pages to userspace. Allocate bounce pages with __GFP_ZERO to avoid leaking information to userspace.
Message-ID: <[email protected]>
drivers/vdpa/vdpa_user/iova_domain.c +1 -1
@@ -124,7 +124,7 @@ static int vduse_domain_map_bounce_page(struct vduse_iova_domain *domain,if (!map->bounce_page) { head_map = &domain->bounce_maps[(iova &PAGE_MASK ) >>BOUNCE_MAP_SHIFT ];if (!head_map->bounce_page) { - tmp_page =alloc_page (GFP_ATOMIC ); + tmp_page =alloc_page (GFP_ATOMIC | __GFP_ZERO);if (!tmp_page)return -ENOMEM ;if (cmpxchg (&head_map->bounce_page,NULL , tmp_page))
drivers/vdpa/vdpa_user/vduse_dev.c +1 -1
@@ -999,7 +999,7 @@ static void *vduse_dev_alloc_coherent(union virtio_map token, size_t size,if (!token.group)return NULL ; - addr =alloc_pages_exact (size, flag); + addr =alloc_pages_exact (size, flag | __GFP_ZERO);if (!addr)return NULL ;
Sasha Levin
maintainerPATCH 1/4 vduse: Use fixed 4KB bounce pages for non-4KB page size
From: Sheng Zhao <[email protected]>
[ Upstream commit 3fc3068e7247c94dec08e93fea422a1bb649bfe5 ]
The allocation granularity of bounce pages is PAGE_SIZE. This may cause even small IO requests to occupy an entire bounce page exclusively. The kind of memory waste will be more significant when PAGE_SIZE is larger than 4KB (e.g. arm64 with 64KB pages).
So, optimize it by using fixed 4KB bounce maps and iova allocation granularity. A single IO request occupies at least a 4KB bounce page instead of the entire memory page of PAGE_SIZE.
Message-Id: <[email protected]>
Stable-dep-of: 9c1523803445 ("VDUSE: avoid leaking information to userspace")
--- drivers/vdpa/vdpa_user/iova_domain.c | 136 +++++++++++++++++++-------- drivers/vdpa/vdpa_user/iova_domain.h | 5 + 2 files changed, 102 insertions(+), 39 deletions(-)
drivers/vdpa/vdpa_user/iova_domain.c +97 -39
@@ -103,19 +103,38 @@ void vduse_domain_clear_map(struct vduse_iova_domain *domain,static int vduse_domain_map_bounce_page (struct vduse_iova_domain *domain,u64 iova,u64 size,u64 paddr) { -struct vduse_bounce_map *map; +struct vduse_bounce_map *map, *head_map; +struct page *tmp_page;u64 last = iova + size -1 ;while (iova <= last) { - map = &domain->bounce_maps[iova >>PAGE_SHIFT ]; +/* +* When PAGE_SIZE is larger than 4KB, multiple adjacent bounce_maps will +* point to the same memory page of PAGE_SIZE. Since bounce_maps originate +* from IO requests, we may not be able to guarantee that the orig_phys +* values of all IO requests within the same 64KB memory page are contiguous. +* Therefore, we need to store them separately. +* +* Bounce pages are allocated on demand. As a result, it may occur that +* multiple bounce pages corresponding to the same 64KB memory page attempt +* to allocate memory simultaneously, so we use cmpxchg to handle this +* concurrency. +*/ + map = &domain->bounce_maps[iova >>BOUNCE_MAP_SHIFT ];if (!map->bounce_page) { - map->bounce_page =alloc_page (GFP_ATOMIC ); -if (!map->bounce_page) -return -ENOMEM ; + head_map = &domain->bounce_maps[(iova &PAGE_MASK ) >>BOUNCE_MAP_SHIFT ]; +if (!head_map->bounce_page) { + tmp_page =alloc_page (GFP_ATOMIC ); +if (!tmp_page) +return -ENOMEM ; +if (cmpxchg (&head_map->bounce_page,NULL , tmp_page)) +__free_page (tmp_page); + } + map->bounce_page = head_map->bounce_page; } map->orig_phys = paddr; - paddr +=PAGE_SIZE ; - iova +=PAGE_SIZE ; + paddr +=BOUNCE_MAP_SIZE ; + iova +=BOUNCE_MAP_SIZE ; }return 0 ; }
@@ -127,12 +146,17 @@ static void vduse_domain_unmap_bounce_page(struct vduse_iova_domain *domain,u64 last = iova + size -1 ;while (iova <= last) { - map = &domain->bounce_maps[iova >>PAGE_SHIFT ]; + map = &domain->bounce_maps[iova >>BOUNCE_MAP_SHIFT ]; map->orig_phys =INVALID_PHYS_ADDR ; - iova +=PAGE_SIZE ; + iova +=BOUNCE_MAP_SIZE ; } } +static unsigned int offset_in_bounce_page (dma_addr_t addr) +{ +return (addr & ~BOUNCE_MAP_MASK ); +} +static void do_bounce (phys_addr_t orig,void *addr,size_t size,enum dma_data_direction dir) {
@@ -162,7 +186,7 @@ static void vduse_domain_bounce(struct vduse_iova_domain *domain, enum dma_data_direction dir) {struct vduse_bounce_map *map; -unsigned int offset; +unsigned int offset, head_offset;void *addr;size_t sz;
@@ -170,16 +194,17 @@ static void vduse_domain_bounce(struct vduse_iova_domain *domain,return ;while (size) { - map = &domain->bounce_maps[iova >>PAGE_SHIFT ]; - offset =offset_in_page (iova); - sz =min_t (size_t,PAGE_SIZE - offset, size); + map = &domain->bounce_maps[iova >>BOUNCE_MAP_SHIFT ]; + head_offset =offset_in_page (iova); + offset =offset_in_bounce_page (iova); + sz =min_t (size_t,BOUNCE_MAP_SIZE - offset, size);if (WARN_ON (!map->bounce_page || map->orig_phys ==INVALID_PHYS_ADDR ))return ; addr =kmap_local_page (map->bounce_page); -do_bounce (map->orig_phys + offset, addr + offset, sz, dir); +do_bounce (map->orig_phys + offset, addr + head_offset, sz, dir);kunmap_local (addr); size -= sz; iova += sz;
@@ -214,7 +239,7 @@ vduse_domain_get_bounce_page(struct vduse_iova_domain *domain, u64 iova)struct page *page =NULL ;read_lock (&domain->bounce_lock); - map = &domain->bounce_maps[iova >>PAGE_SHIFT ]; + map = &domain->bounce_maps[iova >>BOUNCE_MAP_SHIFT ];if (domain->user_bounce_pages || !map->bounce_page) goto out;
@@ -232,7 +257,7 @@ vduse_domain_free_kernel_bounce_pages(struct vduse_iova_domain *domain)struct vduse_bounce_map *map;unsigned long pfn, bounce_pfns; - bounce_pfns = domain->bounce_size >>PAGE_SHIFT ; + bounce_pfns = domain->bounce_size >>BOUNCE_MAP_SHIFT ;for (pfn =0 ; pfn < bounce_pfns; pfn++) { map = &domain->bounce_maps[pfn];
@@ -242,7 +267,8 @@ vduse_domain_free_kernel_bounce_pages(struct vduse_iova_domain *domain)if (!map->bounce_page)continue ; -__free_page (map->bounce_page); +if (!((pfn <<BOUNCE_MAP_SHIFT ) & ~PAGE_MASK )) +__free_page (map->bounce_page); map->bounce_page =NULL ; } }
@@ -250,8 +276,12 @@ vduse_domain_free_kernel_bounce_pages(struct vduse_iova_domain *domain)int vduse_domain_add_user_bounce_pages (struct vduse_iova_domain *domain,struct page **pages,int count) { -struct vduse_bounce_map *map; -int i, ret; +struct vduse_bounce_map *map, *head_map; +int i, j, ret; +int inner_pages =PAGE_SIZE /BOUNCE_MAP_SIZE ; +int bounce_pfns = domain->bounce_size >>BOUNCE_MAP_SHIFT ; +struct page *head_page =NULL ; +bool need_copy;/* Now we don't support partial mapping */ if (count != (domain->bounce_size >>PAGE_SHIFT ))
@@ -263,17 +293,25 @@ int vduse_domain_add_user_bounce_pages(struct vduse_iova_domain *domain, goto out;for (i =0 ; i < count; i++) { - map = &domain->bounce_maps[i]; -if (map->bounce_page) { + need_copy = false; + head_map = &domain->bounce_maps[(i * inner_pages)]; + head_page = head_map->bounce_page; +for (j =0 ; j < inner_pages; j++) { +if ((i * inner_pages + j) >= bounce_pfns) +break ; + map = &domain->bounce_maps[(i * inner_pages + j)];/* Copy kernel page to user page if it's in use */ -if (map->orig_phys !=INVALID_PHYS_ADDR ) -memcpy_to_page (pages[i],0 , -page_address (map->bounce_page), -PAGE_SIZE ); -__free_page (map->bounce_page); +if ((head_page) && (map->orig_phys !=INVALID_PHYS_ADDR )) + need_copy = true; + map->bounce_page = pages[i]; } - map->bounce_page = pages[i];get_page (pages[i]); +if ((head_page) && (need_copy)) +memcpy_to_page (pages[i],0 , +page_address (head_page), +PAGE_SIZE ); +if (head_page) +__free_page (head_page); } domain->user_bounce_pages = true; ret =0 ;
@@ -285,8 +323,12 @@ int vduse_domain_add_user_bounce_pages(struct vduse_iova_domain *domain,void vduse_domain_remove_user_bounce_pages (struct vduse_iova_domain *domain) { -struct vduse_bounce_map *map; -unsigned long i, count; +struct vduse_bounce_map *map, *head_map; +unsigned long i, j, count; +int inner_pages =PAGE_SIZE /BOUNCE_MAP_SIZE ; +int bounce_pfns = domain->bounce_size >>BOUNCE_MAP_SHIFT ; +struct page *head_page =NULL ; +bool need_copy;write_lock (&domain->bounce_lock);if (!domain->user_bounce_pages)
@@ -296,18 +338,34 @@ void vduse_domain_remove_user_bounce_pages(struct vduse_iova_domain *domain)for (i =0 ; i < count; i++) {struct page *page =NULL ; - map = &domain->bounce_maps[i]; -if (WARN_ON (!map->bounce_page)) + need_copy = false; + head_map = &domain->bounce_maps[(i * inner_pages)]; +if (WARN_ON (!head_map->bounce_page))continue ; - -/* Copy user page to kernel page if it's in use */ -if (map->orig_phys !=INVALID_PHYS_ADDR ) { + head_page = head_map->bounce_page; + +for (j =0 ; j < inner_pages; j++) { +if ((i * inner_pages + j) >= bounce_pfns) +break ; + map = &domain->bounce_maps[(i * inner_pages + j)]; +if (WARN_ON (!map->bounce_page)) +continue ; +/* Copy user page to kernel page if it's in use */ +if (map->orig_phys !=INVALID_PHYS_ADDR ) + need_copy = true; + } +if (need_copy) { page =alloc_page (GFP_ATOMIC | __GFP_NOFAIL);memcpy_from_page (page_address (page), - map->bounce_page,0 ,PAGE_SIZE ); + head_page,0 ,PAGE_SIZE ); + } +for (j =0 ; j < inner_pages; j++) { +if ((i * inner_pages + j) >= bounce_pfns) +break ; + map = &domain->bounce_maps[(i * inner_pages + j)]; + map->bounce_page = page; } -put_page (map->bounce_page); - map->bounce_page = page; +put_page (head_page); } domain->user_bounce_pages = false; out:
@@ -557,7 +615,7 @@ vduse_domain_create(unsigned long iova_limit, size_t bounce_size)unsigned long pfn, bounce_pfns;int ret; - bounce_pfns =PAGE_ALIGN (bounce_size) >>PAGE_SHIFT ; + bounce_pfns =PAGE_ALIGN (bounce_size) >>BOUNCE_MAP_SHIFT ;if (iova_limit <= bounce_size)return NULL ;
@@ -589,7 +647,7 @@ vduse_domain_create(unsigned long iova_limit, size_t bounce_size)rwlock_init (&domain->bounce_lock);spin_lock_init (&domain->iotlb_lock);init_iova_domain (&domain->stream_iovad, -PAGE_SIZE ,IOVA_START_PFN ); +BOUNCE_MAP_SIZE ,IOVA_START_PFN ); ret =iova_domain_init_rcaches (&domain->stream_iovad);if (ret) goto err_iovad_stream;
drivers/vdpa/vdpa_user/iova_domain.h +5 -0
@@ -19,6 +19,11 @@#define INVALID_PHYS_ADDR (~(phys_addr_t)0) +#define BOUNCE_MAP_SHIFT 12 +#define BOUNCE_MAP_SIZE (1 << BOUNCE_MAP_SHIFT) +#define BOUNCE_MAP_MASK (~(BOUNCE_MAP_SIZE - 1)) +#define BOUNCE_MAP_ALIGN (addr) (((addr) + BOUNCE_MAP_SIZE - 1) & ~(BOUNCE_MAP_SIZE - 1)) +struct vduse_bounce_map {struct page *bounce_page;u64 orig_phys;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/4 vduse: remove unused vaddr parameter of vduse_domain_free_coherent
From: Eugenio Pérez <[email protected]>
[ Upstream commit 766e1749c0ef6a09651be9b8a8283d508c322b58 ]
We will modify the function in next patches so let's clean it first.
Message-Id: <[email protected]>
Stable-dep-of: 9c1523803445 ("VDUSE: avoid leaking information to userspace")
--- drivers/vdpa/vdpa_user/iova_domain.c | 3 +-- drivers/vdpa/vdpa_user/iova_domain.h | 3 +-- drivers/vdpa/vdpa_user/vduse_dev.c | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-)
drivers/vdpa/vdpa_user/iova_domain.c +1 -2
@@ -514,8 +514,7 @@ void *vduse_domain_alloc_coherent(struct vduse_iova_domain *domain, }void vduse_domain_free_coherent (struct vduse_iova_domain *domain,size_t size, -void *vaddr,dma_addr_t dma_addr, -unsigned long attrs) +dma_addr_t dma_addr,unsigned long attrs) {struct iova_domain *iovad = &domain->consistent_iovad;struct vhost_iotlb_map *map;
drivers/vdpa/vdpa_user/iova_domain.h +1 -2
@@ -63,8 +63,7 @@ void *vduse_domain_alloc_coherent(struct vduse_iova_domain *domain, gfp_t flag, unsigned long attrs);void vduse_domain_free_coherent (struct vduse_iova_domain *domain,size_t size, -void *vaddr,dma_addr_t dma_addr, -unsigned long attrs); +dma_addr_t dma_addr,unsigned long attrs);void vduse_domain_reset_bounce_map (struct vduse_iova_domain *domain);
drivers/vdpa/vdpa_user/vduse_dev.c +1 -1
@@ -849,7 +849,7 @@ static void vduse_dev_free_coherent(struct device *dev, size_t size,struct vduse_dev *vdev =dev_to_vduse (dev);struct vduse_iova_domain *domain = vdev->domain; -vduse_domain_free_coherent (domain, size, vaddr, dma_addr, attrs); +vduse_domain_free_coherent (domain, size, dma_addr, attrs); }static size_t vduse_dev_max_mapping_size (struct device *dev)
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/4 vduse: take out allocations from vduse_dev_alloc_coherent
In reply to Sasha LevinFrom: Eugenio Pérez <[email protected]>
[ Upstream commit 489d76520612abf9a4ede4344349105406c91a73 ]
The function vduse_dev_alloc_coherent will be called under rwlock in next patches. Make it out of the lock to avoid increasing its fail rate.
Message-Id: <[email protected]>
Stable-dep-of: 9c1523803445 ("VDUSE: avoid leaking information to userspace")
--- drivers/vdpa/vdpa_user/iova_domain.c | 24 +++++++----------------- drivers/vdpa/vdpa_user/iova_domain.h | 5 ++--- drivers/vdpa/vdpa_user/vduse_dev.c | 14 ++++++++++---- 3 files changed, 19 insertions(+), 24 deletions(-)
drivers/vdpa/vdpa_user/iova_domain.c +7 -17
@@ -479,17 +479,15 @@ void vduse_domain_unmap_page(struct vduse_iova_domain *domain,vduse_domain_free_iova (iovad, dma_addr, size); } -void *vduse_domain_alloc_coherent (struct vduse_iova_domain *domain, -size_t size,dma_addr_t *dma_addr, -gfp_t flag,unsigned long attrs) +dma_addr_t vduse_domain_alloc_coherent (struct vduse_iova_domain *domain, +size_t size,void *orig) {struct iova_domain *iovad = &domain->consistent_iovad;unsigned long limit = domain->iova_limit;dma_addr_t iova =vduse_domain_alloc_iova (iovad, size, limit); -void *orig =alloc_pages_exact (size, flag); -if (!iova || !orig) - goto err; +if (!iova) +return DMA_MAPPING_ERROR ;spin_lock (&domain->iotlb_lock); if (vduse_iotlb_add_range(domain, (u64)iova, (u64)iova + size - 1,
@@ -500,17 +498,12 @@ void *vduse_domain_alloc_coherent(struct vduse_iova_domain *domain, }spin_unlock (&domain->iotlb_lock); - *dma_addr = iova; +return iova; -return orig; err: - *dma_addr =DMA_MAPPING_ERROR ; -if (orig) -free_pages_exact (orig, size); -if (iova) -vduse_domain_free_iova (iovad, iova, size); +vduse_domain_free_iova (iovad, iova, size); -return NULL ; +return DMA_MAPPING_ERROR ; } void vduse_domain_free_coherent(struct vduse_iova_domain *domain, size_t size,
@@ -519,7 +512,6 @@ void vduse_domain_free_coherent(struct vduse_iova_domain *domain, size_t size,struct iova_domain *iovad = &domain->consistent_iovad;struct vhost_iotlb_map *map;struct vdpa_map_file *map_file; -phys_addr_t pa;spin_lock (&domain->iotlb_lock); map = vhost_iotlb_itree_first(domain->iotlb, (u64)dma_addr,
@@ -531,12 +523,10 @@ void vduse_domain_free_coherent(struct vduse_iova_domain *domain, size_t size, map_file = (struct vdpa_map_file *)map->opaque;fput (map_file->file);kfree (map_file); - pa = map->addr;vhost_iotlb_map_free (domain->iotlb, map);spin_unlock (&domain->iotlb_lock);vduse_domain_free_iova (iovad, dma_addr, size); -free_pages_exact (phys_to_virt (pa), size); }static vm_fault_t vduse_domain_mmap_fault (struct vm_fault *vmf)
drivers/vdpa/vdpa_user/iova_domain.h +2 -3
@@ -58,9 +58,8 @@ void vduse_domain_unmap_page(struct vduse_iova_domain *domain, dma_addr_t dma_addr, size_t size, enum dma_data_direction dir, unsigned long attrs); -void *vduse_domain_alloc_coherent (struct vduse_iova_domain *domain, -size_t size,dma_addr_t *dma_addr, -gfp_t flag,unsigned long attrs); +dma_addr_t vduse_domain_alloc_coherent (struct vduse_iova_domain *domain, +size_t size,void *orig);void vduse_domain_free_coherent (struct vduse_iova_domain *domain,size_t size,dma_addr_t dma_addr,unsigned long attrs);
drivers/vdpa/vdpa_user/vduse_dev.c +10 -4
@@ -828,18 +828,23 @@ static void *vduse_dev_alloc_coherent(struct device *dev, size_t size, {struct vduse_dev *vdev =dev_to_vduse (dev);struct vduse_iova_domain *domain = vdev->domain; -unsigned long iova;void *addr; *dma_addr =DMA_MAPPING_ERROR ; - addr =vduse_domain_alloc_coherent (domain, size, - (dma_addr_t *)&iova, flag, attrs); + + addr =alloc_pages_exact (size, flag);if (!addr)return NULL ; - *dma_addr = (dma_addr_t )iova; + *dma_addr =vduse_domain_alloc_coherent (domain, size, addr); +if (*dma_addr ==DMA_MAPPING_ERROR ) + goto err;return addr; + +err: +free_pages_exact (addr, size); +return NULL ; } static void vduse_dev_free_coherent(struct device *dev, size_t size,
@@ -850,6 +855,7 @@ static void vduse_dev_free_coherent(struct device *dev, size_t size,struct vduse_iova_domain *domain = vdev->domain;vduse_domain_free_coherent (domain, size, dma_addr, attrs); +free_pages_exact (vaddr, size); }static size_t vduse_dev_max_mapping_size (struct device *dev)
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/4 VDUSE: avoid leaking information to userspace
In reply to Sasha LevinFrom: Jason Wang <[email protected]>
[ Upstream commit 9c1523803445ee0348f62b77793266dd981596e0 ]
The bounceing is not necessarily page aligned, so current VDUSE can leak kernel information through mapping bounce pages to userspace. Allocate bounce pages with __GFP_ZERO to avoid leaking information to userspace.
Message-ID: <[email protected]>
--- drivers/vdpa/vdpa_user/iova_domain.c | 2 +- drivers/vdpa/vdpa_user/vduse_dev.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
drivers/vdpa/vdpa_user/iova_domain.c +1 -1
@@ -124,7 +124,7 @@ static int vduse_domain_map_bounce_page(struct vduse_iova_domain *domain,if (!map->bounce_page) { head_map = &domain->bounce_maps[(iova &PAGE_MASK ) >>BOUNCE_MAP_SHIFT ];if (!head_map->bounce_page) { - tmp_page =alloc_page (GFP_ATOMIC ); + tmp_page =alloc_page (GFP_ATOMIC | __GFP_ZERO);if (!tmp_page)return -ENOMEM ;if (cmpxchg (&head_map->bounce_page,NULL , tmp_page))
drivers/vdpa/vdpa_user/vduse_dev.c +1 -1
@@ -832,7 +832,7 @@ static void *vduse_dev_alloc_coherent(struct device *dev, size_t size, *dma_addr =DMA_MAPPING_ERROR ; - addr =alloc_pages_exact (size, flag); + addr =alloc_pages_exact (size, flag | __GFP_ZERO);if (!addr)return NULL ;
Signature · 1 line
2.53.0