Re: [PATCH 1/2] mm/memfd_luo: reject memfds whose page count exceeds UINT_MAX
From: Pasha Tatashin
Date: Fri May 01 2026 - 15:00:28 EST
On 04-23 13:56, David Carlier wrote:
> memfd_luo_preserve_folios() declares max_folios as unsigned int and
> computes it from the inode size, then passes it to memfd_pin_folios()
> which itself caps max_folios at unsigned int. For files whose base-page
> count exceeds UINT_MAX (larger than 16 TiB with 4 KiB pages), the
> assignment truncates silently: only a prefix of the file gets pinned and
> preserved, while memfd_luo_preserve() still records the full inode size
> in ser->size. On retrieve the inode is restored to the full size but
> only the preserved prefix repopulates the page cache, so the tail comes
> back as holes and user data is silently lost across the live update.
>
> Reject such files at preserve time with -EFBIG rather than chunk the
> pin loop, which would also require enlarging the preserved folios array
> well beyond what is practical.
>
> Fixes: b3749f174d68 ("mm: memfd_luo: allow preserving memfd")
> Signed-off-by: David Carlier <devnexen@xxxxxxxxx>
> ---
> mm/memfd_luo.c | 15 +++++++++++++--
> 1 file changed, 13 insertions(+), 2 deletions(-)
>
> diff --git a/mm/memfd_luo.c b/mm/memfd_luo.c
> index b02b503c750d..f41d11053b7d 100644
> --- a/mm/memfd_luo.c
> +++ b/mm/memfd_luo.c
> @@ -259,7 +259,7 @@ static int memfd_luo_preserve(struct liveupdate_file_op_args *args)
> struct inode *inode = file_inode(args->file);
> struct memfd_luo_folio_ser *folios_ser;
> struct memfd_luo_ser *ser;
> - u64 nr_folios;
> + u64 nr_folios, inode_size;
> int err = 0, seals;
>
> inode_lock(inode);
> @@ -285,7 +285,18 @@ static int memfd_luo_preserve(struct liveupdate_file_op_args *args)
> }
>
> ser->pos = args->file->f_pos;
> - ser->size = i_size_read(inode);
> + inode_size = i_size_read(inode);
> +
> + /*
> + * memfd_pin_folios() caps at UINT_MAX folios; refuse larger
> + * files to avoid silently preserving only a prefix.
> + */
I think, the fix should be first done at memfd_pin_folios() to change
max_folios to 'long' or 'unsigned long', and then just updated
memfd_luo.c to match.
Pasha
> + if (DIV_ROUND_UP_ULL(inode_size, PAGE_SIZE) > UINT_MAX) {
> + err = -EFBIG;
> + goto err_free_ser;
> + }
> +
> + ser->size = inode_size;
> ser->seals = seals;
>
> err = memfd_luo_preserve_folios(args->file, &ser->folios,
> --
> 2.53.0
>