On Fri, 19 Jun 2026 at 01:32, Ackerley Tng via B4 Relay <[email protected]> wrote: > > From: Ackerley Tng <[email protected]> > > The existing guest_memfd conversion tests only use single-page memory > regions. This provides no coverage for multi-page guest_memfd objects, > specifically whether KVM correctly handles the page index for conversion > operations. An incorrect implementation could, for example, always operate > on the first page regardless of the index provided. > > Add a new test case to verify that conversions between private and shared > memory correctly target the specified page within a multi-page guest_memfd. > > This test also verifies the precision of memory conversions by converting a > single page an then iterating through all other pages ensure they remain in > their original state. > > To support this test, add a new GMEM_CONVERSION_MULTIPAGE_TEST_INIT_SHARED > macro that handles setting up and tearing down the VM for each page > iteration. The teardown logic is adjusted to prevent a double-free in this > new scenario. > > Signed-off-by: Ackerley Tng <[email protected]> > Co-developed-by: Sean Christopherson <[email protected]> > Signed-off-by: Sean Christopherson <[email protected]>
Reviewed-by: Fuad Tabba <[email protected]> Cheers, /fuad > --- > .../kvm/x86/guest_memfd_conversions_test.c | 66 > ++++++++++++++++++++++ > 1 file changed, 66 insertions(+) > > diff --git a/tools/testing/selftests/kvm/x86/guest_memfd_conversions_test.c > b/tools/testing/selftests/kvm/x86/guest_memfd_conversions_test.c > index 5b070d3374eae..8e17d5c08aeb8 100644 > --- a/tools/testing/selftests/kvm/x86/guest_memfd_conversions_test.c > +++ b/tools/testing/selftests/kvm/x86/guest_memfd_conversions_test.c > @@ -61,8 +61,13 @@ static void gmem_conversions_do_setup(test_data_t *t, int > nr_pages, > > static void gmem_conversions_do_teardown(test_data_t *t) > { > + /* Use NULL to avoid second free in FIXTURE_TEARDOWN (multipage > tests). */ > + if (!t->vcpu) > + return; > + > /* No need to close gmem_fd, it's owned by the VM structure. */ > kvm_vm_free(t->vcpu->vm); > + t->vcpu = NULL; > } > > FIXTURE_TEARDOWN(gmem_conversions) > @@ -101,6 +106,29 @@ static void __gmem_conversions_##test(test_data_t *t, > int nr_pages) \ > #define GMEM_CONVERSION_TEST_INIT_SHARED(test) > \ > __GMEM_CONVERSION_TEST_INIT_SHARED(test, 1) > > +/* > + * Repeats test over nr_pages in a guest_memfd of size nr_pages, providing > each > + * test iteration with test_page, the index of the page under test in > + * guest_memfd. test_page takes values 0..(nr_pages - 1) inclusive. > + */ > +#define GMEM_CONVERSION_MULTIPAGE_TEST_INIT_SHARED(test, __nr_pages) > \ > +static void __gmem_conversions_multipage_##test(test_data_t *t, int > nr_pages, \ > + const int test_page); > \ > + > \ > +TEST_F(gmem_conversions, test) > \ > +{ > \ > + const u64 flags = GUEST_MEMFD_FLAG_MMAP | > GUEST_MEMFD_FLAG_INIT_SHARED; \ > + int i; > \ > + > \ > + for (i = 0; i < __nr_pages; ++i) { > \ > + gmem_conversions_do_setup(self, __nr_pages, flags); > \ > + __gmem_conversions_multipage_##test(self, __nr_pages, i); > \ > + gmem_conversions_do_teardown(self); > \ > + } > \ > +} > \ > +static void __gmem_conversions_multipage_##test(test_data_t *t, int > nr_pages, \ > + const int test_page) > + > struct guest_check_data { > void *mem; > char expected_val; > @@ -199,6 +227,44 @@ GMEM_CONVERSION_TEST_INIT_SHARED(init_shared) > test_convert_to_shared(t, 0, 'C', 'D', 'E'); > } > > +GMEM_CONVERSION_MULTIPAGE_TEST_INIT_SHARED(indexing, 4) > +{ > + int i; > + > + /* Get a char that varies with both i and n. */ > +#define combine(x, n) ((x << 4) + (n)) > +#define i_(n) (combine(i, n)) > +#define t_(n) (combine(test_page, n)) > + > + /* > + * Start with the highest index, to catch any errors when, perhaps, > the > + * first page is returned even for the last index. > + */ > + for (i = nr_pages - 1; i >= 0; --i) > + test_shared(t, i, 0, i_(0), i_(2)); > + > + test_convert_to_private(t, test_page, t_(2), t_(3)); > + > + for (i = 0; i < nr_pages; ++i) { > + if (i == test_page) > + test_private(t, test_page, t_(3), t_(4)); > + else > + test_shared(t, i, i_(2), i_(3), i_(4)); > + } > + > + test_convert_to_shared(t, test_page, t_(4), t_(5), t_(6)); > + > + for (i = 0; i < nr_pages; ++i) { > + char expected = i == test_page ? t_(6) : i_(4); > + > + test_shared(t, i, expected, i_(7), i_(8)); > + } > + > +#undef t_ > +#undef i_ > +#undef combine > +} > + > int main(int argc, char *argv[]) > { > TEST_REQUIRE(kvm_check_cap(KVM_CAP_VM_TYPES) & > BIT(KVM_X86_SW_PROTECTED_VM)); > > -- > 2.55.0.rc0.738.g0c8ab3ebcc-goog > >
