Thread (188 messages) 188 messages, 12 authors, 8d ago

Re: [PATCH v8 37/46] KVM: selftests: Test that shared/private status is consistent across processes

From: Fuad Tabba <hidden>
Date: 2026-06-25 07:15:32
Also in: kvm, linux-coco, linux-doc, linux-kselftest, linux-mm, lkml

On Fri, 19 Jun 2026 at 01:32, Ackerley Tng via B4 Relay
[off-list ref] wrote:
From: Sean Christopherson <seanjc@google.com>

Add a test to verify that a guest_memfd's shared/private status is
consistent across processes, and that any shared pages previously mapped in
any process are unmapped from all 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 <seanjc@google.com>
Co-developed-by: Ackerley Tng <redacted>
Signed-off-by: Ackerley Tng <redacted>
---
Two things below, otherwise:

Reviewed-by: Fuad Tabba <redacted>

Cheers,
/fuad

quoted hunk ↗ jump to hunk
 .../kvm/x86/guest_memfd_conversions_test.c         | 118 +++++++++++++++++++++
 1 file changed, 118 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 f03af2c46426f..99b0023609670 100644
--- a/tools/testing/selftests/kvm/x86/guest_memfd_conversions_test.c
+++ b/tools/testing/selftests/kvm/x86/guest_memfd_conversions_test.c
@@ -2,6 +2,8 @@
 /*
  * Copyright (c) 2024, Google LLC.
  */
+#include <pthread.h>
+#include <time.h>
 #include <sys/mman.h>
 #include <unistd.h>
nit: include order
quoted hunk ↗ jump to hunk
@@ -323,6 +325,122 @@ 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)
+{
+       enum test_state {
+               STATE_INIT,
+               STATE_CHECK_SHARED,
+               STATE_DONE_CHECKING_SHARED,
+               STATE_CHECK_PRIVATE,
+               STATE_DONE_CHECKING_PRIVATE,
+       };
+
+       struct sync_state {
+               pthread_mutex_t mutex;
+               pthread_cond_t cond;
+               enum test_state step;
+       } *sync;
+
+       pthread_mutexattr_t mattr;
+       pthread_condattr_t cattr;
+       pid_t child_pid, parent_pid;
+       int status;
+
+       sync = kvm_mmap(sizeof(*sync), PROT_READ | PROT_WRITE,
+                       MAP_SHARED | MAP_ANONYMOUS, -1);
+
+       pthread_mutexattr_init(&mattr);
+       pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
+       pthread_mutex_init(&sync->mutex, &mattr);
+       pthread_mutexattr_destroy(&mattr);
+
+       pthread_condattr_init(&cattr);
+       pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
+       pthread_cond_init(&sync->cond, &cattr);
+       pthread_condattr_destroy(&cattr);
+
+       sync->step = STATE_INIT;
+
+#define TEST_STATE_AWAIT(__state)                                              \
+       do {                                                                    \
+               pthread_mutex_lock(&sync->mutex);                               \
+               while (sync->step != (__state)) {                               \
+                       struct timespec ts, stop;                               \
+                       int ret;                                                \
+                                                                               \
+                       clock_gettime(CLOCK_REALTIME, &ts);                     \
+                       stop = timespec_add_ns(ts, 100 * 1000000UL);            \
+                                                                               \
+                       ret = pthread_cond_timedwait(&sync->cond, &sync->mutex, &stop); \
+                       if (ret == ETIMEDOUT) {                                 \
+                               bool alive = (child_pid == 0) ?                 \
+                                            (getppid() == parent_pid) :                \
+                                            (waitpid(child_pid, NULL, WNOHANG) == 0); \
Not sure it's worth it, but if you want to silence Sashiko, waitid
with WNOWAIT might be the way to go (not tested, just from looking at
the man page). This is though very unlikely, mentioning it since
Sashiko complained.

+                               TEST_ASSERT(alive, "Other process exited prematurely"); \
+                       } else {                                                \
+                               TEST_ASSERT(!ret, "pthread_cond_timedwait failed"); \
+                       }                                                       \
+               }                                                               \
+               pthread_mutex_unlock(&sync->mutex);                             \
+       } while (0)
+
+#define TEST_STATE_SET(__state)                                                        \
+       do {                                                                    \
+               pthread_mutex_lock(&sync->mutex);                               \
+               sync->step = (__state);                                         \
+               pthread_cond_broadcast(&sync->cond);                            \
+               pthread_mutex_unlock(&sync->mutex);                             \
+       } while (0)
+
+       parent_pid = getpid();
+       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);
+
+       TEST_ASSERT_EQ(waitpid(child_pid, &status, 0), child_pid);
+       TEST_ASSERT(WIFEXITED(status) && WEXITSTATUS(status) == 0,
+                   "Child exited with unexpected status");
+
+       pthread_mutex_destroy(&sync->mutex);
+       pthread_cond_destroy(&sync->cond);
+       kvm_munmap(sync, sizeof(*sync));
+
+#undef TEST_STATE_SET
+#undef TEST_STATE_AWAIT
+}
+
 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
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help