On Wed, May 13, 2026 at 11:15:37AM +0000, Anirudh Rayabharam wrote:
> On Mon, May 11, 2026 at 08:06:44AM -0700, Stanislav Kinsburskii wrote:
> > On Mon, May 11, 2026 at 01:48:56PM +0000, Anirudh Rayabharam wrote:
> > > On Thu, May 07, 2026 at 03:43:09PM +0000, Stanislav Kinsburskii wrote:
> > > > mshv_prepare_pinned_region() returns 0 (success) when mshv_region_map()
> > > > fails on an unencrypted partition. The condition on the error path:
> > > >
> > > > if (ret && mshv_partition_encrypted(partition))
> > > >
> > > > only handles map failures for encrypted partitions — if the partition is
> > > > not encrypted and the map fails, execution falls through to 'return 0',
> > > > silently ignoring the error.
> > > >
> > > > Additionally, calling mshv_region_invalidate() inline on map failure
> > > > zeroes the mreg_pages array before the caller's cleanup path
> > > > (mshv_region_destroy) can call mshv_region_unmap(). Since unmap skips
> > > > pages where mreg_pages[offset] is NULL, this can leave stale SLAT
> > > > mappings for partially-mapped pages.
> > > >
> > > > Fix by returning immediately on success and falling through to error
> > > > return on failure. For unencrypted partitions, the caller's
> > > > mshv_region_destroy() handles unmap followed by invalidate in the
> > > > correct order. For encrypted partitions where re-sharing fails, zero
> > > > the page array without unpinning — the pages are inaccessible to the
> > > > host and must not be unpinned, but zeroing prevents
> > > > mshv_region_destroy() from attempting to unpin them.
> > >
> > > mshv_region_destroy() calls mshv_region_invalidate() which calls
> > > mshv_region_invalidate_pages() which just does:
> > >
> > > static void mshv_region_invalidate_pages(struct mshv_mem_region *region,
> > > u64 page_offset, u64
> > > page_count)
> > > {
> > > if (region->mreg_type == MSHV_REGION_TYPE_MEM_PINNED)
> > > unpin_user_pages(region->mreg_pages + page_offset,
> > > page_count);
> > >
> > > memset(region->mreg_pages + page_offset, 0,
> > > page_count * sizeof(struct page *));
> > > }
> > >
> > > It doesn't checked for zeroed pages before unpinning.
> > >
> > > >
> > > > Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to
> > > > expose /dev/mshv to VMMs")
> > > > Signed-off-by: Stanislav Kinsburskii <[email protected]>
> > > > ---
> > > > drivers/hv/mshv_root_main.c | 26 ++++++++++++++++----------
> > > > 1 file changed, 16 insertions(+), 10 deletions(-)
> > > >
> > > > diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
> > > > index 665d565899c15..7e4252b6bc65c 100644
> > > > --- a/drivers/hv/mshv_root_main.c
> > > > +++ b/drivers/hv/mshv_root_main.c
> > > > @@ -1360,32 +1360,38 @@ static int mshv_prepare_pinned_region(struct
> > > > mshv_mem_region *region)
> > > > pt_err(partition,
> > > > "Failed to unshare memory region
> > > > (guest_pfn: %llu): %d\n",
> > > > region->start_gfn, ret);
> > > > - goto invalidate_region;
> > > > + goto err_out;
> > > > }
> > > > }
> > > >
> > > > ret = mshv_region_map(region);
> > > > - if (ret && mshv_partition_encrypted(partition)) {
> > > > + if (ret)
> > > > + goto share_region;
> > > > +
> > > > + return 0;
> > > > +
> > > > +share_region:
> > > > + if (mshv_partition_encrypted(partition)) {
> > > > int shrc;
> > > >
> > > > shrc = mshv_region_share(region);
> > > > if (!shrc)
> > > > - goto invalidate_region;
> > > > + goto err_out;
> > > >
> > > > pt_err(partition,
> > > > "Failed to share memory region (guest_pfn:
> > > > %llu): %d\n",
> > > > region->start_gfn, shrc);
> > > > /*
> > > > - * Don't unpin if marking shared failed because pages
> > > > are no
> > > > - * longer mapped in the host, ie root, anymore.
> > > > + * Re-sharing failed — the pages remain inaccessible to
> > > > the
> > > > + * host. Zero the page array so that
> > > > mshv_region_destroy()
> > > > + * won't attempt to unpin them (leaking the page
> > > > references
> > > > + * is intentional; unpinning host-inaccessible pages
> > > > would be
> > > > + * unsafe).
> > > > */
> > >
> > > Actually, mshv_region_destroy() also does mshv_region_share(). Maybe we
> > > can remove it from here altogether. Either way, should this zeroing
> > > logic be added there too so as not to crash the host by unpinning pages
> > > that weren't marked shared?
> > >
> >
> > Indeed.
> > The issue with all this code is that we are leaking state in
> > mshv_region_pin if we wimply return from it: we don't know if the pages
> > are pinned or unshared (or mapped) in the destruction callback.
> > We should either introduce a state variable to track this or just don't
> > call the generic mshv_region_put on case of region creation error.
> > The latter approch make mshv_map_user_memory idempotent and thus looks
> > like a better way forward.
> > What do you think?
>
I tried to say, that there can be two apporaches to solving this issue:
1. Either make sure mshv_region_pus() can destroy a partially created
region (tha'ts what this patch is doing) or
2. Make mshv_map_user_region be idempotent by not calling mshv_region_put()
if the region was not
fully created and destroy the partioally created parts of it on
error paths. This would require preserving some state in the region
struct to know if the region was pinned or shared or mapped.
This looks like a leaner apporach, but it requires additional state
tracking and more code changes.
Thanks,
Stanislav
> I'm not sure I follow the latter approach. Omitting mshv_region_put()
> would cause a dangling reference to the mshv_region right?
>
> Thanks,
> Anirudh.