[PATCH v2 5/6] selftests/liveupdate: add test for operations on a preserved memfd

Pratyush Yadav
2026-04-03T19:40:37Z

Once a memfd is preserved, certain operations are not allowed to
succeed since they might make the state of the memfd inconsistent with
the serialized state. Among these operations are truncating or growing
the memfd. Writes and reads to already existing memfd should succeed.

Add a test that makes sure a preserved memfd does not allow growing or
shrinking, but does allow reads and writes to existing memory to go
thorough.

Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Signed-off-by: Pratyush Yadav <ptyadav@amazon.de>
Signed-off-by: Pratyush Yadav (Google) <pratyush@kernel.org>
---
.../testing/selftests/liveupdate/luo_memfd.c | 57 +++++++++++++++++++
1 file changed, 57 insertions(+)

diff --git a/tools/testing/selftests/liveupdate/luo_memfd.c b/tools/testing/selftests/liveupdate/luo_memfd.c
index 3ba0709f2c61..a1e9f34da006 100644
--- a/tools/testing/selftests/liveupdate/luo_memfd.c
+++ b/tools/testing/selftests/liveupdate/luo_memfd.c
@@ -39,6 +39,10 @@
#define ZERO_SESSION_NAME "zero_session"
#define ZERO_MEMFD_TOKEN 1

+#define PRESERVED_SESSION_NAME "preserved_session"
+#define PRESERVED_MEMFD_TOKEN 1
+#define PRESERVED_BUFFER_SIZE SZ_1M
+
static int luo_fd = -1;
static int stage;

@@ -165,6 +169,59 @@ TEST(zero_memfd)
}
}

+/*
+ * Test that preserved memfd can't grow or shrink, but reads and writes still
+ * work.
+ */
+TEST(preserved_ops)
+{
+ char write_buffer[128] = {'A'};
+ int fd, session;
+ char *buffer;
+ struct liveupdate_session_preserve_fd preserve_arg = { .size = sizeof(preserve_arg) };
+
+ if (stage != 1)
+ SKIP(return, "test only expected to run on stage 1");
+
+ buffer = malloc(PRESERVED_BUFFER_SIZE);
+ ASSERT_NE(buffer, NULL);
+
+ session = luo_create_session(luo_fd, PRESERVED_SESSION_NAME);
+ ASSERT_GE(session, 0);
+
+ fd = create_random_memfd("preserved_memfd", buffer, PRESERVED_BUFFER_SIZE);
+ ASSERT_GE(fd, 0);
+
+ preserve_arg.fd = fd;
+ preserve_arg.token = PRESERVED_MEMFD_TOKEN;
+ ASSERT_GE(ioctl(session, LIVEUPDATE_SESSION_PRESERVE_FD, &preserve_arg), 0);
+
+ /*
+ * Write to the preserved memfd (within existing size). This should
+ * work.
+ */
+ ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);
+ /* Write buffer is smaller than total file size. */
+ ASSERT_EQ(write_size(fd, write_buffer, sizeof(write_buffer)), 0);
+ ASSERT_EQ(verify_fd_content_read(fd, write_buffer, sizeof(write_buffer)), 0);
+
+ /* Try to grow the file using write(). */
+
+ /* First, seek to one byte behind initial size. */
+ ASSERT_GE(lseek(fd, PRESERVED_BUFFER_SIZE - 1, SEEK_SET), 0);
+
+ /*
+ * Then, write some data that should increase the file size. This should
+ * fail.
+ */
+ ASSERT_LT(write_size(fd, write_buffer, sizeof(write_buffer)), 0);
+ ASSERT_EQ(lseek(fd, 0, SEEK_END), PRESERVED_BUFFER_SIZE);
+
+ /* Try to shrink the file using truncate. This should also fail. */
+ ASSERT_LT(ftruncate(fd, PRESERVED_BUFFER_SIZE / 2), 0);
+ ASSERT_EQ(lseek(fd, 0, SEEK_END), PRESERVED_BUFFER_SIZE);
+}
+
int main(int argc, char *argv[])
{
int expected_stage = 0;
--
2.53.0.1213.gd9a14994de-goog