Skip to content

Commit 30661be

Browse files
Naman Jainnamancse
authored andcommitted
Drivers: hv: mshv_vtl: fix vmemmap race in low pgmap resolver
memremap_pages() makes a pgmap visible to get_dev_pagemap() before arch_add_memory() populates the vmemmap. A concurrent mshv_vtl_low_huge_fault() running while another thread is still inside MSHV_ADD_VTL0_MEMORY can resolve a pfn whose struct page sits behind an empty vmemmap PMD, oopsing on the first page_folio() deref: BUG: unable to handle page fault for address: ffffea000404ca08 PGD ... PUD ... PMD 0 RIP: 0010:mshv_vtl_low_huge_fault+0x4b/0x240 Call Trace: mshv_vtl_low_fault+0xb/0x10 __do_fault+0x32/0xa0 __handle_mm_fault+0xc2f/0x2110 Replace get_dev_pagemap()-based resolution with a driver-owned RCU list of completed VTL0 ranges. Each range is added only after devm_memremap_pages() returns, so a hit guarantees the vmemmap is populated and the struct page is initialized. Entries are never removed (pgmaps live for the life of the module). Fixes: 775741a ("Drivers: hv: mshv_vtl: use folio-aware inserters for huge VTL0 mappings") Signed-off-by: Naman Jain <namjain@linux.microsoft.com>
1 parent 71b491d commit 30661be

1 file changed

Lines changed: 66 additions & 17 deletions

File tree

drivers/hv/mshv_vtl_main.c

Lines changed: 66 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,9 +1164,26 @@ static struct address_space *mshv_vtl_low_mapping;
11641164
/* Identity token tagged on every mshv_vtl pgmap; only its address matters. */
11651165
static const u8 mshv_vtl_pgmap_token;
11661166

1167+
/*
1168+
* List of pgmap-backed VTL0 ranges, published only after devm_memremap_pages()
1169+
* returns. memremap_pages() makes a pgmap visible to get_dev_pagemap() before
1170+
* arch_add_memory() populates the vmemmap, so a concurrent fault could resolve
1171+
* a pfn whose struct page is still backed by an empty vmemmap PMD and oops on
1172+
* dereference. The driver-owned list is the gate; entries are never removed.
1173+
*/
1174+
static LIST_HEAD(mshv_vtl_low_ranges);
1175+
static DEFINE_SPINLOCK(mshv_vtl_low_ranges_lock);
1176+
1177+
struct mshv_vtl_low_range {
1178+
struct list_head list;
1179+
unsigned long start_pfn;
1180+
unsigned long end_pfn; /* exclusive */
1181+
};
1182+
11671183
static int mshv_vtl_ioctl_add_vtl0_mem(struct mshv_vtl *vtl, void __user *arg)
11681184
{
11691185
struct mshv_vtl_ram_disposition vtl0_mem;
1186+
struct mshv_vtl_low_range *range;
11701187
struct dev_pagemap *pgmap;
11711188
void *addr;
11721189
bool decrypted;
@@ -1209,13 +1226,27 @@ static int mshv_vtl_ioctl_add_vtl0_mem(struct mshv_vtl *vtl, void __user *arg)
12091226
"Add VTL0 memory: start: 0x%llx, end_pfn: 0x%llx, page order: %lu\n",
12101227
vtl0_mem.start_pfn, vtl0_mem.last_pfn, pgmap->vmemmap_shift);
12111228

1229+
range = kzalloc(sizeof(*range), GFP_KERNEL);
1230+
if (!range) {
1231+
kfree(pgmap);
1232+
return -ENOMEM;
1233+
}
1234+
12121235
addr = devm_memremap_pages(mem_dev, pgmap);
12131236
if (IS_ERR(addr)) {
12141237
dev_err(vtl->module_dev, "devm_memremap_pages error: %ld\n", PTR_ERR(addr));
1238+
kfree(range);
12151239
kfree(pgmap);
12161240
return PTR_ERR(addr);
12171241
}
12181242

1243+
/* Publish only now: vmemmap is populated and struct pages are initialized. */
1244+
range->start_pfn = vtl0_mem.start_pfn;
1245+
range->end_pfn = vtl0_mem.last_pfn;
1246+
spin_lock(&mshv_vtl_low_ranges_lock);
1247+
list_add_rcu(&range->list, &mshv_vtl_low_ranges);
1248+
spin_unlock(&mshv_vtl_low_ranges_lock);
1249+
12191250
/*
12201251
* Zap stale pte_special PTEs the 4K fallback installed before this
12211252
* range had a pgmap, so the next access re-faults into the folio path.
@@ -3720,27 +3751,27 @@ static bool can_fault(struct vm_fault *vmf, unsigned long size, unsigned long *p
37203751
}
37213752

37223753
/*
3723-
* Resolve a user-supplied PFN to a page owned by an mshv_vtl pgmap, or NULL.
3724-
* Look up the pgmap via get_dev_pagemap() rather than page_pgmap(): the pgmap
3725-
* is published in pgmap_array before per-page state is initialized, so a
3726-
* concurrent MSHV_ADD_VTL0_MEMORY can leave folio->pgmap unset while pfn_valid
3727-
* and is_zone_device_page already return true. The owner check additionally
3728-
* rejects foreign MEMORY_DEVICE_GENERIC pgmaps (e.g. DAX).
3754+
* Resolve a PFN to a page owned by an mshv_vtl pgmap, or NULL. The range list
3755+
* is only published after devm_memremap_pages() returns, so a hit here means
3756+
* the vmemmap is populated and the struct page is safe to dereference.
37293757
*/
37303758
static struct page *mshv_vtl_low_resolve_page(unsigned long pfn)
37313759
{
3732-
struct dev_pagemap *pgmap;
3733-
struct page *page;
3760+
struct mshv_vtl_low_range *r;
3761+
struct page *page = NULL;
37343762

3735-
pgmap = get_dev_pagemap(pfn);
3736-
if (!pgmap)
3737-
return NULL;
3738-
page = NULL;
3739-
if (pgmap->type == MEMORY_DEVICE_GENERIC &&
3740-
pgmap->owner == &mshv_vtl_pgmap_token)
3741-
page = pfn_to_page(pfn);
3742-
/* Safe to drop here: mshv_vtl pgmaps are never released for the life of the module. */
3743-
put_dev_pagemap(pgmap);
3763+
rcu_read_lock();
3764+
list_for_each_entry_rcu(r, &mshv_vtl_low_ranges, list) {
3765+
if (pfn >= r->start_pfn && pfn < r->end_pfn) {
3766+
struct page *p = pfn_to_page(pfn);
3767+
struct dev_pagemap *pgmap = page_pgmap(p);
3768+
3769+
if (pgmap && pgmap->owner == &mshv_vtl_pgmap_token)
3770+
page = p;
3771+
break;
3772+
}
3773+
}
3774+
rcu_read_unlock();
37443775
return page;
37453776
}
37463777

@@ -4062,6 +4093,9 @@ static int __init mshv_vtl_init(void)
40624093

40634094
static void __exit mshv_vtl_exit(void)
40644095
{
4096+
struct mshv_vtl_low_range *r, *tmp;
4097+
LIST_HEAD(stale);
4098+
40654099
ms_hyperv_free_redirected_intr();
40664100
mshv_free_apicid_to_cpuid_mapping();
40674101
misc_deregister(&mshv_vtl_sint_dev);
@@ -4074,6 +4108,21 @@ static void __exit mshv_vtl_exit(void)
40744108
misc_deregister(&mshv_vtl_sint_dev);
40754109
hv_vtl_remove_synic();
40764110
misc_deregister(&mshv_dev);
4111+
4112+
/*
4113+
* /dev/mshv_vtl_low is deregistered above, so no new faults can enter
4114+
* mshv_vtl_low_resolve_page(). Unlink each range under the spinlock,
4115+
* wait for any in-flight RCU reader, then free.
4116+
*/
4117+
spin_lock(&mshv_vtl_low_ranges_lock);
4118+
list_for_each_entry_safe(r, tmp, &mshv_vtl_low_ranges, list) {
4119+
list_del_rcu(&r->list);
4120+
list_add(&r->list, &stale);
4121+
}
4122+
spin_unlock(&mshv_vtl_low_ranges_lock);
4123+
synchronize_rcu();
4124+
list_for_each_entry_safe(r, tmp, &stale, list)
4125+
kfree(r);
40774126
}
40784127

40794128
module_init(mshv_vtl_init);

0 commit comments

Comments
 (0)