From: Sean Christopherson <[email protected]>

Add a test to verify that a guest_memfd's shared/private status is
consistent across processes.

The test forks a child process after creating the shared guest_memfd
region so that the second process exists alongside the main process for the
entire test.

The processes then take turns to access memory to check that the
shared/private status is consistent across processes.

Signed-off-by: Sean Christopherson <[email protected]>
Co-developed-by: Ackerley Tng <[email protected]>
Signed-off-by: Ackerley Tng <[email protected]>
---
 .../kvm/guest_memfd_conversions_test.c        | 74 +++++++++++++++++++
 1 file changed, 74 insertions(+)

diff --git a/tools/testing/selftests/kvm/guest_memfd_conversions_test.c 
b/tools/testing/selftests/kvm/guest_memfd_conversions_test.c
index 907d415d72315..e6abf2d30c62d 100644
--- a/tools/testing/selftests/kvm/guest_memfd_conversions_test.c
+++ b/tools/testing/selftests/kvm/guest_memfd_conversions_test.c
@@ -330,6 +330,80 @@ GMEM_CONVERSION_TEST_INIT_SHARED(truncate)
        test_private(t, 0, 0, 'A');
 }
 
+/* Test that shared/private memory protections work and are seen from any 
process. */
+GMEM_CONVERSION_TEST_INIT_SHARED(forked_accesses)
+{
+       /*
+        * No races are intended in this test, shared memory is only used to
+        * coordinate between processes.
+        */
+       static enum {
+               STATE_INIT,
+               STATE_CHECK_SHARED,
+               STATE_DONE_CHECKING_SHARED,
+               STATE_CHECK_PRIVATE,
+               STATE_DONE_CHECKING_PRIVATE,
+       } *test_state;
+       pid_t child_pid;
+
+       test_state = kvm_mmap(sizeof(*test_state), PROT_READ | PROT_WRITE,
+                             MAP_SHARED | MAP_ANONYMOUS, -1);
+
+#define TEST_STATE_AWAIT(__state)                                              
\
+       while (READ_ONCE(*test_state) != __state) {                             
\
+               if (child_pid != 0) {                                           
\
+                       int status;                                             
\
+                       pid_t pid;                                              
\
+                       do {                                                    
\
+                               pid = waitpid(child_pid, &status, WNOHANG);     
\
+                       } while (pid == -1 && errno == EINTR);                  
\
+                       if (pid == -1)                                          
\
+                               TEST_FAIL("Couldn't check child status.");      
\
+                       else if (pid != 0)                                      
\
+                               TEST_FAIL("Child exited prematurely.");         
\
+               }                                                               
\
+       }
+
+#define TEST_STATE_SET(__state) WRITE_ONCE(*test_state, __state)
+
+       child_pid = fork();
+       TEST_ASSERT(child_pid != -1, "fork failed");
+
+       if (child_pid == 0) {
+               const char inconsequential = 0xdd;
+
+               TEST_STATE_AWAIT(STATE_CHECK_SHARED);
+
+               /*
+                * This maps the pages into the child process as well, and tests
+                * that the conversion process will unmap the guest_memfd memory
+                * from all processes.
+                */
+               host_do_rmw(t->mem, 0, 0xB, 0xC);
+
+               TEST_STATE_SET(STATE_DONE_CHECKING_SHARED);
+               TEST_STATE_AWAIT(STATE_CHECK_PRIVATE);
+
+               TEST_EXPECT_SIGBUS(READ_ONCE(t->mem[0]));
+               TEST_EXPECT_SIGBUS(WRITE_ONCE(t->mem[0], inconsequential));
+
+               TEST_STATE_SET(STATE_DONE_CHECKING_PRIVATE);
+               exit(0);
+       }
+
+       test_shared(t, 0, 0, 0xA, 0xB);
+
+       TEST_STATE_SET(STATE_CHECK_SHARED);
+       TEST_STATE_AWAIT(STATE_DONE_CHECKING_SHARED);
+
+       test_convert_to_private(t, 0, 0xC, 0xD);
+
+       TEST_STATE_SET(STATE_CHECK_PRIVATE);
+       TEST_STATE_AWAIT(STATE_DONE_CHECKING_PRIVATE);
+
+       kvm_munmap(test_state, sizeof(*test_state));
+}
+
 int main(int argc, char *argv[])
 {
        TEST_REQUIRE(kvm_check_cap(KVM_CAP_VM_TYPES) & 
BIT(KVM_X86_SW_PROTECTED_VM));
-- 
2.51.0.858.gf9c4a03a3a-goog


Reply via email to