mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
vhost-vdpa: fix pinned page leak in vhost_vdpa_pa_map() error path
When pin_user_pages() or vhost_vdpa_map() fails, vhost_vdpa_pa_map()
is supposed to unpin the pages pinned after the last successfully
mapped chunk, i.e. the range [map_pfn, last_pfn]. The unpin is guarded
by nchunks, but nchunks is reset to 0 every time a chunk is mapped from
inside the current page list, so a failure after any such boundary
leaves that pending range pinned forever. With physically scattered
memory nearly every page list contains a boundary, so the leak happens
on practically every failed IOTLB update.
The pending range is non-empty exactly when a page has been pinned,
which last_pfn already tells us, so drop nchunks and test last_pfn.
Fixes: ad89653f79 ("vhost-vdpa: fix page pinning leakage in error path (rework)")
Cc: stable@vger.kernel.org
Signed-off-by: Tal Zussman <tz2294@columbia.edu>
Reported-by: Linfeng Sun <linfeng.sun.dev@gmail.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261031-vhost-vdpa-unpin-v1-1-94ffc9683256@columbia.edu>
This commit is contained in:
parent
b179f21d2d
commit
fd1462c96b
|
|
@ -1170,7 +1170,7 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
|
|||
unsigned long list_size = PAGE_SIZE / sizeof(struct page *);
|
||||
unsigned int gup_flags = FOLL_LONGTERM;
|
||||
unsigned long npages, cur_base, map_pfn, last_pfn = 0;
|
||||
unsigned long lock_limit, sz2pin, nchunks, i;
|
||||
unsigned long lock_limit, sz2pin, i;
|
||||
unsigned long page_offset;
|
||||
u64 start = iova;
|
||||
long pinned;
|
||||
|
|
@ -1206,7 +1206,6 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
|
|||
|
||||
cur_base = uaddr & PAGE_MASK;
|
||||
iova &= PAGE_MASK;
|
||||
nchunks = 0;
|
||||
|
||||
while (npages) {
|
||||
sz2pin = min_t(unsigned long, npages, list_size);
|
||||
|
|
@ -1221,7 +1220,6 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
|
|||
}
|
||||
goto out;
|
||||
}
|
||||
nchunks++;
|
||||
|
||||
if (!last_pfn)
|
||||
map_pfn = page_to_pfn(page_list[0]);
|
||||
|
|
@ -1252,7 +1250,6 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
|
|||
|
||||
map_pfn = this_pfn;
|
||||
iova += csize;
|
||||
nchunks = 0;
|
||||
}
|
||||
|
||||
last_pfn = this_pfn;
|
||||
|
|
@ -1267,7 +1264,7 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
|
|||
PFN_PHYS(map_pfn), perm, NULL);
|
||||
out:
|
||||
if (ret) {
|
||||
if (nchunks) {
|
||||
if (last_pfn) {
|
||||
unsigned long pfn;
|
||||
|
||||
/*
|
||||
|
|
@ -1279,7 +1276,6 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
|
|||
* the corresponding unpinning will be handled by
|
||||
* vdpa_unmap().
|
||||
*/
|
||||
WARN_ON(!last_pfn);
|
||||
for (pfn = map_pfn; pfn <= last_pfn; pfn++)
|
||||
unpin_user_page(pfn_to_page(pfn));
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user