[PATCH v4 11/19] mm/shmem_luo: Require permission to preserve a file

From: David Matlack

Date: Tue Nov 10 2026 - 19:29:01 EST


Require the file to be opened for reading and the caller to have
permission to write to it in order to preserve it. Also let the owner
preserve a file that is immutable or on a read-only superblock, and
reject append-only files.

While a file is preserved, its size is frozen for all of its users:
truncating it, fallocate() and writes past its end fail with -EPERM.
Today any process with an fd for a memfd can do that, even one that was
only given a read-only fd. Requiring write permission limits this to
callers that could truncate the file themselves. A file descriptor
opened for writing isn't required, since that would rule out preserving
a running binary, which can't be opened for writing. Only the permission
of the inode is checked, so the mount doesn't need to be writable
either. The size of an immutable file or of a file on a read-only
superblock can't change anyway, so its owner may also preserve it.
Append-only files are rejected, since their writers can only append to
them, and freezing the size would stop all writes.

The retrieved file can be read, so also require the file to be opened
for reading. Otherwise a caller with a write-only fd could preserve the
file to read its contents.

Document the frozen size and the permission requirements.

Signed-off-by: David Matlack <dmatlack@xxxxxxxxxx>
Acked-by: Pasha Tatashin <pasha.tatashin@xxxxxxxxxx>
---
mm/shmem_luo.c | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)

diff --git a/mm/shmem_luo.c b/mm/shmem_luo.c
index dc6ede528822..bdf90aac5820 100644
--- a/mm/shmem_luo.c
+++ b/mm/shmem_luo.c
@@ -27,6 +27,15 @@
* the file are preserved. All others are reset to default. The preserved
* properties are described below.
*
+ * Preserving a file requires it to be opened for reading and the caller to have
+ * permission to write to it, but it doesn't need to be opened for writing. The
+ * owner of a file that is immutable or on a read-only superblock may also
+ * preserve it. Append-only files can't be preserved.
+ *
+ * While a file is preserved, its size can't change and no holes can be punched
+ * into it: Truncating the file, ``fallocate()`` and writes past the end of the
+ * file fail with ``-EPERM``. This applies to all users of the file.
+ *
* .. note::
* The LUO API is not stabilized yet, so the preserved properties of a memfd
* are also not stable and are subject to backwards incompatible changes.
@@ -255,6 +264,30 @@ static void shmem_luo_unpreserve_folios(struct kho_vmalloc *kho_vmalloc,
vfree(folios_ser);
}

+/*
+ * Preserving a file freezes its size for all of its users, and the retrieved
+ * file can be read. So require a file opened for reading, and permission to
+ * write to the file or ownership of a file whose size can't change anyway.
+ * Reject append-only files, since freezing their size would stop all writes.
+ */
+static int shmem_luo_may_preserve(struct file *file)
+{
+ const struct mnt_idmap *idmap = file_mnt_idmap(file);
+ struct inode *inode = file_inode(file);
+
+ if (!(file->f_mode & FMODE_READ))
+ return -EBADF;
+
+ if (IS_APPEND(inode))
+ return -EPERM;
+
+ if ((sb_rdonly(inode->i_sb) || IS_IMMUTABLE(inode)) &&
+ inode_owner_or_capable(idmap, inode))
+ return 0;
+
+ return inode_permission(idmap, inode, MAY_WRITE);
+}
+
static int shmem_luo_preserve(struct liveupdate_file_op_args *args)
{
struct inode *inode = file_inode(args->file);
@@ -264,6 +297,11 @@ static int shmem_luo_preserve(struct liveupdate_file_op_args *args)
int err = 0, seals;

inode_lock(inode);
+
+ err = shmem_luo_may_preserve(args->file);
+ if (err)
+ goto err_unlock_inode;
+
shmem_freeze(inode, true);

/* Allocate the main serialization structure in preserved memory */
@@ -317,6 +355,7 @@ static int shmem_luo_preserve(struct liveupdate_file_op_args *args)
kho_unpreserve_free(ser);
err_unlock:
shmem_freeze(inode, false);
+err_unlock_inode:
inode_unlock(inode);
return err;
}
--
2.56.0.542.g3ef89b7020-goog