[PATCH v3 1/2] liveupdate: prevent double management of files

Pasha Tatashin posted 2 patches 1 week, 1 day ago
There is a newer version of this series
[PATCH v3 1/2] liveupdate: prevent double management of files
Posted by Pasha Tatashin 1 week, 1 day ago
Currently, LUO does not prevent the same file from being managed twice
across different active sessions.

Use a global xarray luo_preserved_files to keep track of file
pointers being preserved by LUO. Update luo_preserve_file() to check and
insert the file pointer into this xarray when it is preserved, and
erase it in luo_file_unpreserve_files() when it is released.

This ensures that the same file (struct file) cannot be managed by
multiple sessions. If another session attempts to preserve an already
managed file, it will now fail with -EBUSY.

Acked-by: Pratyush Yadav (Google) <pratyush@kernel.org>
Reviewed-by: Samiullah Khawaja <skhawaja@google.com>
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
 kernel/liveupdate/luo_file.c | 26 +++++++++++++++++++++++---
 1 file changed, 23 insertions(+), 3 deletions(-)

diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c
index a38ea4975824..d1bb4213fd14 100644
--- a/kernel/liveupdate/luo_file.c
+++ b/kernel/liveupdate/luo_file.c
@@ -110,11 +110,15 @@
 #include <linux/sizes.h>
 #include <linux/slab.h>
 #include <linux/string.h>
+#include <linux/xarray.h>
 #include "luo_internal.h"
 
 static DECLARE_RWSEM(luo_file_handler_lock);
 static LIST_HEAD(luo_file_handler_list);
 
+/* Keep track of files being preserved by LUO */
+static DEFINE_XARRAY(luo_preserved_files);
+
 /* 2 4K pages, give space for 128 files per file_set */
 #define LUO_FILE_PGCNT		2ul
 #define LUO_FILE_MAX							\
@@ -249,6 +253,7 @@ static bool luo_token_is_used(struct luo_file_set *file_set, u64 token)
  * Context: Can be called from an ioctl handler during normal system operation.
  * Return: 0 on success. Returns a negative errno on failure:
  *         -EEXIST if the token is already used.
+ *         -EBUSY if the file descriptor is already preserved by another session.
  *         -EBADF if the file descriptor is invalid.
  *         -ENOSPC if the file_set is full.
  *         -ENOENT if no compatible handler is found.
@@ -277,6 +282,11 @@ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd)
 	if (err)
 		goto  err_fput;
 
+	err = xa_insert(&luo_preserved_files, (unsigned long)file,
+			file, GFP_KERNEL);
+	if (err)
+		goto err_free_files_mem;
+
 	err = -ENOENT;
 	scoped_guard(rwsem_read, &luo_file_handler_lock) {
 		list_private_for_each_entry(fh, &luo_file_handler_list, list) {
@@ -289,11 +299,11 @@ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd)
 
 	/* err is still -ENOENT if no handler was found */
 	if (err)
-		goto err_free_files_mem;
+		goto err_erase_xa;
 
 	err = luo_flb_file_preserve(fh);
 	if (err)
-		goto err_free_files_mem;
+		goto err_erase_xa;
 
 	luo_file = kzalloc_obj(*luo_file);
 	if (!luo_file) {
@@ -323,6 +333,8 @@ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd)
 	kfree(luo_file);
 err_flb_unpreserve:
 	luo_flb_file_unpreserve(fh);
+err_erase_xa:
+	xa_erase(&luo_preserved_files, (unsigned long)file);
 err_free_files_mem:
 	luo_free_files_mem(file_set);
 err_fput:
@@ -366,6 +378,7 @@ void luo_file_unpreserve_files(struct luo_file_set *file_set)
 		luo_file->fh->ops->unpreserve(&args);
 		luo_flb_file_unpreserve(luo_file->fh);
 
+		xa_erase(&luo_preserved_files, (unsigned long)luo_file->file);
 		list_del(&luo_file->list);
 		file_set->count--;
 
@@ -609,6 +622,10 @@ int luo_retrieve_file(struct luo_file_set *file_set, u64 token,
 	luo_file->file = args.file;
 	/* Get reference so we can keep this file in LUO until finish */
 	get_file(luo_file->file);
+
+	WARN_ON(xa_insert(&luo_preserved_files, (unsigned long)luo_file->file,
+			  luo_file->file, GFP_KERNEL));
+
 	*filep = luo_file->file;
 	luo_file->retrieve_status = 1;
 
@@ -704,8 +721,11 @@ int luo_file_finish(struct luo_file_set *file_set)
 
 		luo_file_finish_one(file_set, luo_file);
 
-		if (luo_file->file)
+		if (luo_file->file) {
+			xa_erase(&luo_preserved_files,
+				 (unsigned long)luo_file->file);
 			fput(luo_file->file);
+		}
 		list_del(&luo_file->list);
 		file_set->count--;
 		mutex_destroy(&luo_file->mutex);
-- 
2.43.0
Re: [PATCH v3 1/2] liveupdate: prevent double management of files
Posted by Pratyush Yadav 1 week, 1 day ago
Hi Pasha,

On Wed, Mar 25 2026, Pasha Tatashin wrote:

> Currently, LUO does not prevent the same file from being managed twice
> across different active sessions.
>
> Use a global xarray luo_preserved_files to keep track of file
> pointers being preserved by LUO. Update luo_preserve_file() to check and
> insert the file pointer into this xarray when it is preserved, and
> erase it in luo_file_unpreserve_files() when it is released.
>
> This ensures that the same file (struct file) cannot be managed by
> multiple sessions. If another session attempts to preserve an already
> managed file, it will now fail with -EBUSY.
>
> Acked-by: Pratyush Yadav (Google) <pratyush@kernel.org>
> Reviewed-by: Samiullah Khawaja <skhawaja@google.com>
> Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>

For memfd and hugetlb at least, we serialize the _inode_ not the file.
The inode has the contents that we care to preserve.

So if two FDs point to the same inode, this will break. You can do this
by first creating a memfd and then by opening "/proc/self/fd/<fd>". Then
you would be able to trigger the preservation twice, causing all sorts
of problems. Same on the retrieve side.

So I think you should be storing the inode here.

Except... IOMMUFD and VFIO seem to be using different things for the
state that needs to be preserved. I don't know either of the subsystems
that well but from a quick look, I see that
iommufd_liveupdate_preserve() [0] calls iommufd_ctx_from_file() (which
stores the ictx in file->private_data) and does most of the preservation
operations on the ictx.

Similarly, vfio_pci_liveupdate_preserve() [1] calls
vfio_device_from_file(), which also uses file->private_data.

So, there seems to be no single way to get the "target object" that can
tell us whether it is already serialized or not. For memfd and hugetlb
it is the inode, for IOMMUFD it is the iommufd_ctx and for VFIO it is
the vfio_device.

So unless I am missing something, I don't think this approach will work.
As much as I hate to suggest it, I think we need to move this check to
each caller so they can find out the object they need to serialize and
check if it already is.

[0] https://github.com/samikhawaja/linux/blob/iommu/phase1-v1/drivers/iommu/iommufd/liveupdate.c#L210
[1] https://github.com/dmatlack/linux/blob/liveupdate/vfio/cdev/v3/drivers/vfio/pci/vfio_pci_liveupdate.c#L145

[...]

-- 
Regards,
Pratyush Yadav
Re: [PATCH v3 1/2] liveupdate: prevent double management of files
Posted by David Matlack 1 week, 1 day ago
On Wed, Mar 25, 2026 at 1:20 PM Pratyush Yadav <pratyush@kernel.org> wrote:

> For memfd and hugetlb at least, we serialize the _inode_ not the file.
> The inode has the contents that we care to preserve.
>
> So if two FDs point to the same inode, this will break. You can do this
> by first creating a memfd and then by opening "/proc/self/fd/<fd>". Then
> you would be able to trigger the preservation twice, causing all sorts
> of problems. Same on the retrieve side.

> So unless I am missing something, I don't think this approach will work.
> As much as I hate to suggest it, I think we need to move this check to
> each caller so they can find out the object they need to serialize and
> check if it already is.

I think LUO can still enforce that the file is not preserved twice.
HugeTLB and memfd's preserve() functions just need to also check that
the associated inode has not already been preserved?
Re: [PATCH v3 1/2] liveupdate: prevent double management of files
Posted by Pasha Tatashin 1 week, 1 day ago
On Wed, Mar 25, 2026 at 4:34 PM David Matlack <dmatlack@google.com> wrote:
>
> On Wed, Mar 25, 2026 at 1:20 PM Pratyush Yadav <pratyush@kernel.org> wrote:
>
> > For memfd and hugetlb at least, we serialize the _inode_ not the file.
> > The inode has the contents that we care to preserve.
> >
> > So if two FDs point to the same inode, this will break. You can do this
> > by first creating a memfd and then by opening "/proc/self/fd/<fd>". Then
> > you would be able to trigger the preservation twice, causing all sorts
> > of problems. Same on the retrieve side.

Hm.

>
> > So unless I am missing something, I don't think this approach will work.
> > As much as I hate to suggest it, I think we need to move this check to
> > each caller so they can find out the object they need to serialize and
> > check if it already is.
>
> I think LUO can still enforce that the file is not preserved twice.
> HugeTLB and memfd's preserve() functions just need to also check that
> the associated inode has not already been preserved?

For memfd/hugetlbs the true state is in inode
For vfio/kvm the shared anonymous inode is just a dummy wrapper, and
the true state is in file->private_data.

I wonder if we could use the XArray to track inodes for standard
files, but track the struct file itself for anonymous files (we would
need a new function from FS that allows us to determine if "struct
file" has anonymous inode or not).

Pasha
Re: [PATCH v3 1/2] liveupdate: prevent double management of files
Posted by Mike Rapoport 1 week ago
On Wed, Mar 25, 2026 at 05:08:57PM -0400, Pasha Tatashin wrote:
> On Wed, Mar 25, 2026 at 4:34 PM David Matlack <dmatlack@google.com> wrote:
> >
> > On Wed, Mar 25, 2026 at 1:20 PM Pratyush Yadav <pratyush@kernel.org> wrote:
> >
> > > For memfd and hugetlb at least, we serialize the _inode_ not the file.
> > > The inode has the contents that we care to preserve.
> > >
> > > So if two FDs point to the same inode, this will break. You can do this
> > > by first creating a memfd and then by opening "/proc/self/fd/<fd>". Then
> > > you would be able to trigger the preservation twice, causing all sorts
> > > of problems. Same on the retrieve side.
> 
> Hm.
> 
> >
> > > So unless I am missing something, I don't think this approach will work.
> > > As much as I hate to suggest it, I think we need to move this check to
> > > each caller so they can find out the object they need to serialize and
> > > check if it already is.
> >
> > I think LUO can still enforce that the file is not preserved twice.
> > HugeTLB and memfd's preserve() functions just need to also check that
> > the associated inode has not already been preserved?
> 
> For memfd/hugetlbs the true state is in inode
> For vfio/kvm the shared anonymous inode is just a dummy wrapper, and
> the true state is in file->private_data.
> 
> I wonder if we could use the XArray to track inodes for standard
> files, but track the struct file itself for anonymous files (we would
> need a new function from FS that allows us to determine if "struct
> file" has anonymous inode or not).

Don't all files we preserve use anon inodes?

How about we extend the fh->ops with a method that will return "unique"
object? 

	list_private_for_each_entry(fh, &luo_file_handler_list, list) {
		if (fh->ops->can_preserve(fh, file)) {
			unique_handle = fh->ops->unique_handle(fh, file);
			err = 0;
			break;
		}
	}

	xa_insert(&luo_preserved_objects, unique_handle,
		  (unsigned long)unique_handle, GFP_KERNEL);
 
> Pasha

-- 
Sincerely yours,
Mike.
Re: [PATCH v3 1/2] liveupdate: prevent double management of files
Posted by Pasha Tatashin 1 week ago
On Thu, Mar 26, 2026 at 5:04 AM Mike Rapoport <rppt@kernel.org> wrote:
>
> On Wed, Mar 25, 2026 at 05:08:57PM -0400, Pasha Tatashin wrote:
> > On Wed, Mar 25, 2026 at 4:34 PM David Matlack <dmatlack@google.com> wrote:
> > >
> > > On Wed, Mar 25, 2026 at 1:20 PM Pratyush Yadav <pratyush@kernel.org> wrote:
> > >
> > > > For memfd and hugetlb at least, we serialize the _inode_ not the file.
> > > > The inode has the contents that we care to preserve.
> > > >
> > > > So if two FDs point to the same inode, this will break. You can do this
> > > > by first creating a memfd and then by opening "/proc/self/fd/<fd>". Then
> > > > you would be able to trigger the preservation twice, causing all sorts
> > > > of problems. Same on the retrieve side.
> >
> > Hm.
> >
> > >
> > > > So unless I am missing something, I don't think this approach will work.
> > > > As much as I hate to suggest it, I think we need to move this check to
> > > > each caller so they can find out the object they need to serialize and
> > > > check if it already is.
> > >
> > > I think LUO can still enforce that the file is not preserved twice.
> > > HugeTLB and memfd's preserve() functions just need to also check that
> > > the associated inode has not already been preserved?
> >
> > For memfd/hugetlbs the true state is in inode
> > For vfio/kvm the shared anonymous inode is just a dummy wrapper, and
> > the true state is in file->private_data.
> >
> > I wonder if we could use the XArray to track inodes for standard
> > files, but track the struct file itself for anonymous files (we would
> > need a new function from FS that allows us to determine if "struct
> > file" has anonymous inode or not).
>
> Don't all files we preserve use anon inodes?

No for memfd_create(), the inode allocation path depends on the
presence of the MFD_HUGETLB flag:

1. Regular memfd (shmem):
memfd_alloc_file() calls shmem_file_setup(), which leads to:
shmem_get_inode() -> __shmem_get_inode() -> new_inode().
The allocated inode is a struct shmem_inode_info.

2. HugeTLB memfd (hugetlbfs):
memfd_alloc_file() calls hugetlb_file_setup(), which leads to:
hugetlbfs_get_inode() -> new_inode().
The allocated inode is a struct hugetlbfs_inode_info.

> How about we extend the fh->ops with a method that will return "unique"
> object?

Yeap, this is exactly what I am proposing. Adding an optional
get_id(struct file *file) to fh->ops allows each handler to define
what constitutes a "unique" identity for its supported file types.

luo_file.c will have a helper like this:

static inline unsigned long luo_get_id(struct liveupdate_file_handler
*fh, struct file *file)
{
     return (fh->ops->get_id) ? fh->ops->get_id(file) : (unsigned long)file;
}

For memfd, the get_id implementation will return the inode pointer.

Pasha


>
>         list_private_for_each_entry(fh, &luo_file_handler_list, list) {
>                 if (fh->ops->can_preserve(fh, file)) {
>                         unique_handle = fh->ops->unique_handle(fh, file);
>                         err = 0;
>                         break;
>                 }
>         }
>
>         xa_insert(&luo_preserved_objects, unique_handle,
>                   (unsigned long)unique_handle, GFP_KERNEL);
>
> > Pasha
>
> --
> Sincerely yours,
> Mike.
>
Re: [PATCH v3 1/2] liveupdate: prevent double management of files
Posted by Pasha Tatashin 1 week, 1 day ago
On Wed, Mar 25, 2026 at 5:08 PM Pasha Tatashin
<pasha.tatashin@soleen.com> wrote:
>
> On Wed, Mar 25, 2026 at 4:34 PM David Matlack <dmatlack@google.com> wrote:
> >
> > On Wed, Mar 25, 2026 at 1:20 PM Pratyush Yadav <pratyush@kernel.org> wrote:
> >
> > > For memfd and hugetlb at least, we serialize the _inode_ not the file.
> > > The inode has the contents that we care to preserve.
> > >
> > > So if two FDs point to the same inode, this will break. You can do this
> > > by first creating a memfd and then by opening "/proc/self/fd/<fd>". Then
> > > you would be able to trigger the preservation twice, causing all sorts
> > > of problems. Same on the retrieve side.
>
> Hm.
>
> >
> > > So unless I am missing something, I don't think this approach will work.
> > > As much as I hate to suggest it, I think we need to move this check to
> > > each caller so they can find out the object they need to serialize and
> > > check if it already is.
> >
> > I think LUO can still enforce that the file is not preserved twice.
> > HugeTLB and memfd's preserve() functions just need to also check that
> > the associated inode has not already been preserved?
>
> For memfd/hugetlbs the true state is in inode
> For vfio/kvm the shared anonymous inode is just a dummy wrapper, and
> the true state is in file->private_data.
>
> I wonder if we could use the XArray to track inodes for standard
> files, but track the struct file itself for anonymous files (we would
> need a new function from FS that allows us to determine if "struct
> file" has anonymous inode or not).

Actually, let's not modify the fs layer, instead, add an optional
get_id(struct file *file) callback to the luo file handler (struct
liveupdate_file_ops). This would return a unique deduplication key.
For example, the memfd callback would return (unsigned
long)file->f_inode. If get_id() is not implemented for a given
handler, the LUO would default to using the struct file pointer
directly

>
> Pasha
Re: [PATCH v3 1/2] liveupdate: prevent double management of files
Posted by Mike Rapoport 1 week, 1 day ago
On Wed, Mar 25, 2026 at 06:20:25PM +0000, Pasha Tatashin wrote:
> Currently, LUO does not prevent the same file from being managed twice
> across different active sessions.
> 
> Use a global xarray luo_preserved_files to keep track of file
> pointers being preserved by LUO. Update luo_preserve_file() to check and
> insert the file pointer into this xarray when it is preserved, and
> erase it in luo_file_unpreserve_files() when it is released.
> 
> This ensures that the same file (struct file) cannot be managed by
> multiple sessions. If another session attempts to preserve an already
> managed file, it will now fail with -EBUSY.
> 
> Acked-by: Pratyush Yadav (Google) <pratyush@kernel.org>
> Reviewed-by: Samiullah Khawaja <skhawaja@google.com>
> Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>

Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>

> ---
>  kernel/liveupdate/luo_file.c | 26 +++++++++++++++++++++++---
>  1 file changed, 23 insertions(+), 3 deletions(-)
 
-- 
Sincerely yours,
Mike.