All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "David Matlack" <dmatlack@google.com>,
	kexec@lists.infradead.org, linux-mm@kvack.org
Cc: "Alexander Graf" <graf@amazon.com>,
	"Andrew Morton" <akpm@linux-foundation.org>,
	"Baolin Wang" <baolin.wang@linux.alibaba.com>,
	"Christian Brauner" <brauner@kernel.org>,
	"David Hildenbrand" <david@kernel.org>,
	"Hugh Dickins" <hughd@google.com>,
	"Jonathan Corbet" <corbet@lwn.net>,
	"Liam R. Howlett" <liam@infradead.org>,
	"Lorenzo Stoakes" <ljs@kernel.org>,
	"Michal Hocko" <mhocko@suse.com>,
	"Mike Rapoport" <rppt@kernel.org>,
	"Pasha Tatashin" <pasha.tatashin@soleen.com>,
	"Pratyush Yadav" <pratyush@kernel.org>,
	"Randy Dunlap" <rdunlap@infradead.org>,
	"Samiullah Khawaja" <skhawaja@google.com>,
	"Shuah Khan" <skhan@linuxfoundation.org>,
	"Suren Baghdasaryan" <surenb@google.com>,
	"Vlastimil Babka" <vbabka@kernel.org>
Subject: Re: [PATCH v4 12/19] mm/shmem_luo: Retrieve tmpfs files into a file created by userspace
Date: Thu, 12 Nov 2026 00:31:20 +0000	[thread overview]
Message-ID: <sashiko-outbox-209944@kernel.org> (raw)
In-Reply-To: <20261111002358.1842967-13-dmatlack@google.com>

Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [Medium] mm/shmem_luo: tail pages dropped on folio split in
  shmem_luo_retrieve_into()
- [Medium] mm/shmem_luo: race with khugepaged/MADV_COLLAPSE in
  shmem_luo_retrieve_into()
--

commit 780f293994f8e014ce055385564e7193d31d8556
Author: David Matlack <dmatlack@google.com>

mm/shmem_luo: Retrieve tmpfs files into a file created by userspace

This commit adds support for retrieving preserved tmpfs files into a
userspace-created file on a tmpfs mount via LIVEUPDATE_SESSION_RETRIEVE_FD,
and adds SHMEM_LUO_FLAG_TMPFS to distinguish tmpfs files from memfds.

> diff --git a/mm/shmem_luo.c b/mm/shmem_luo.c
> index bdf90aac5820f..7242664ee4aee 100644
> --- a/mm/shmem_luo.c
> +++ b/mm/shmem_luo.c
[ ... ]
> @@ -577,6 +603,86 @@ static int shmem_luo_restore_folios(struct file *file,
>  	return err;
>  }
>  
> +/*
> + * Restore a preserved tmpfs file into the file that userspace passed in the
> + * retrieve argument.
> + */
> +static int shmem_luo_retrieve_into(struct liveupdate_file_op_args *args,
> +				   struct shmem_luo_ser *ser)
> +{
> +	const struct liveupdate_shmem_retrieve_arg *arg = args->arg;
> +	struct inode *inode;
> +	struct file *file;
> +	int err;
> +
> +	if (!arg)
> +		return -EINVAL;
> +
> +	CLASS(fd, target)(arg->fd);
> +	if (fd_empty(target))
> +		return -EBADF;
> +
> +	file = fd_file(target);
> +	inode = file_inode(file);
> +
> +	/* Like the preserved file, the file must be on a tmpfs mount. */
> +	if (!shmem_file(file) || shmem_inode_on_internal_mount(inode))
> +		return -EINVAL;
> +
> +	if (!(file->f_mode & FMODE_WRITE))
> +		return -EBADF;
> +
> +	/* Fill the file like a write, which also takes these locks. */
> +	file_start_write(file);
> +	inode_lock(inode);
> +
> +	err = -EPERM;
> +	if (IS_IMMUTABLE(inode))
> +		goto unlock;
> +
> +	err = -EINVAL;
> +	if (i_size_read(inode) || !mapping_empty(inode->i_mapping))
> +		goto unlock;
> +
> +	err = liveupdate_claim_file(args, file);
> +	if (err)
> +		goto unlock;
> +
> +	err = file_modified(file);
> +	if (err)
> +		goto unclaim;
> +
> +	/* The preserved state is consumed from here on. */
> +	args->retryable = false;
> +
> +	/*
> +	 * The folios are past the end of the file until i_size is set below,
> +	 * so reads, page faults and memfd_pin_folios() don't see them, and
> +	 * they can be removed again if adding one fails.
> +	 */
> +	err = shmem_luo_restore_folios(file, ser);

[Severity: Medium]
Can khugepaged or MADV_COLLAPSE race with shmem_luo_restore_folios() if the
target tmpfs file is mmap()ed?

shmem_mmap_prepare() does not take inode_lock(inode), and
shmem_luo_retrieve_into() checks mapping_empty() but not mapping_mapped().
Unlike shmem_fallocate(), which leaves newly allocated folios beyond i_size as
!uptodate, shmem_luo_retrieve_folios() marks each restored folio uptodate
before unlocking it on the LRU:

CPU0                                  CPU1
shmem_luo_retrieve_into()
  inode_lock(inode)
  args->retryable = false
  shmem_luo_restore_folios()
    shmem_luo_retrieve_folios()
      /* restores folio at index 0 */
      shmem_add_to_page_cache()
      folio_mark_uptodate(folio)
      folio_unlock(folio)
                                      madvise(..., MADV_COLLAPSE)
                                        ...
                                          collapse_file()
                                            /* sees uptodate folio at 0,
                                             * treats indices 1..511 as
                                             * holes without checking
                                             * i_size, installs 2M folio */
      /* restores folio at index 1 */
      shmem_add_to_page_cache()
        --> returns -EEXIST           <-- permanent retrieve failure

Neither collapse_file() nor try_collapse_pte_mapped_thp() acquires
inode_lock(inode) or checks i_size_read(inode) when a shmem folio is already
uptodate:

mm/khugepaged.c:collapse_file() {
    ...
			if (xa_is_value(folio) || !folio_test_uptodate(folio)) {
				xas_unlock_irq(&xas);
				/* swap in or instantiate fallocated page */
				if (shmem_get_folio(mapping->host, index, 0,
						&folio, SGP_NOALLOC)) {
					result = SCAN_FAIL;
					goto xa_unlocked;
				}
				/* drain lru cache to help folio_isolate_lru() */
				lru_add_drain();
			} else if (folio_trylock(folio)) {
    ...
}

In addition to causing shmem_add_to_page_cache() to fail with -EEXIST after
args->retryable is set to false, try_collapse_pte_mapped_thp() can install a
huge PMD into userspace page tables while i_size is still 0, exposing folios
before the retrieve finishes.

> +	if (err) {
> +		shmem_truncate_range(inode, 0, (uoff_t)-1);
> +		goto unclaim;
> +	}
> +
> +	i_size_write(inode, ser->size);

[Severity: Medium]
Can a folio split during shmem_luo_restore_folios() drop the tail pages of a
restored large folio because both i_size and SHMEM_I(inode)->fallocend are 0?

shmem_luo_retrieve_folios() unlocks each restored folio and places it on the
LRU while subsequent iterations allocate XArray nodes and charge memcg and
quotas with GFP_HIGHUSER_MOVABLE and GFP_KERNEL. If page reclaim or migration
splits an already-inserted large folio before i_size_write() runs,
__folio_freeze_split_file() computes the EOF index from i_size and
shmem_fallocend():

mm/huge_memory.c:__folio_freeze_split_file() {
    ...
	end = DIV_ROUND_UP(i_size_read(mapping->host), PAGE_SIZE);
	if (shmem_mapping(mapping))
		end = shmem_fallocend(mapping->host, end);
    ...
}

Because both i_size and info->fallocend are 0, end is 0, so every split
sub-folio with new_folio->index >= 1 is treated as beyond EOF, removed from
mapping->i_pages, and freed, silently corrupting the retrieved file. Setting
SHMEM_I(inode)->fallocend before shmem_luo_restore_folios() (as
shmem_fallocate() does) would prevent splits from discarding pages past
i_size.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261111002358.1842967-1-dmatlack@google.com?part=12


  reply	other threads:[~2026-11-12  0:31 UTC|newest]

Thread overview: 39+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-11  0:23 [PATCH v4 00/19] liveupdate: Preserve tmpfs files across Live Update David Matlack
2026-11-11  0:23 ` [PATCH v4 01/19] liveupdate: Remove the list of steps from luo_preserve_file()'s comment David Matlack
2026-11-12  0:31   ` sashiko-bot
2026-11-11  0:23 ` [PATCH v4 02/19] liveupdate: Pass user argument to file preserve and retrieve David Matlack
2026-11-12  0:31   ` sashiko-bot
2026-11-11  0:23 ` [PATCH v4 03/19] selftests/liveupdate: Add tests for preserve and retrieve user argument David Matlack
2026-11-12  0:31   ` sashiko-bot
2026-11-11  0:23 ` [PATCH v4 04/19] liveupdate: Allow file handlers to mark retrieve failures as retryable David Matlack
2026-11-12  0:31   ` sashiko-bot
2026-11-11  0:23 ` [PATCH v4 05/19] mm: Rename memfd_luo to shmem_luo David Matlack
2026-11-12  0:31   ` sashiko-bot
2026-11-11  0:23 ` [PATCH v4 06/19] liveupdate: Don't WARN if tracking a retrieved file fails David Matlack
2026-11-12  0:31   ` sashiko-bot
2026-11-11  0:23 ` [PATCH v4 07/19] liveupdate: Add helpers to track the files that LUO manages David Matlack
2026-11-12  0:31   ` sashiko-bot
2026-11-11  0:23 ` [PATCH v4 08/19] liveupdate: Let file handlers retrieve into an existing file David Matlack
2026-11-12  0:31   ` sashiko-bot
2026-11-11  0:23 ` [PATCH v4 09/19] mm/shmem_luo: Leave freeing the preserved state to finish() David Matlack
2026-11-12  0:31   ` sashiko-bot
2026-11-11  0:23 ` [PATCH v4 10/19] mm/shmem_luo: Don't make early retrieve failures permanent David Matlack
2026-11-12  0:31   ` sashiko-bot
2026-11-11  0:23 ` [PATCH v4 11/19] mm/shmem_luo: Require permission to preserve a file David Matlack
2026-11-12  0:31   ` sashiko-bot
2026-11-11  0:23 ` [PATCH v4 12/19] mm/shmem_luo: Retrieve tmpfs files into a file created by userspace David Matlack
2026-11-12  0:31   ` sashiko-bot [this message]
2026-11-11  0:23 ` [PATCH v4 13/19] mm/shmem_luo: Allow preserving tmpfs files David Matlack
2026-11-12  0:31   ` sashiko-bot
2026-11-11  0:23 ` [PATCH v4 14/19] selftests/liveupdate: Test which tmpfs files can be preserved David Matlack
2026-11-12  0:31   ` sashiko-bot
2026-11-11  0:23 ` [PATCH v4 15/19] selftests/liveupdate: Add a test for retrieving tmpfs files David Matlack
2026-11-12  0:31   ` sashiko-bot
2026-11-11  0:23 ` [PATCH v4 16/19] selftests/liveupdate: Test memfds and LUO interactions in luo_tmpfs David Matlack
2026-11-12  0:31   ` sashiko-bot
2026-11-11  0:23 ` [PATCH v4 17/19] selftests/liveupdate: Test the targets of retrieved tmpfs files David Matlack
2026-11-12  0:31   ` sashiko-bot
2026-11-11  0:23 ` [PATCH v4 18/19] selftests/liveupdate: Test retrieving tmpfs files preserved read-only David Matlack
2026-11-12  0:31   ` sashiko-bot
2026-11-11  0:23 ` [PATCH v4 19/19] selftests/liveupdate: Test block accounting of retrieved tmpfs files David Matlack
2026-11-12  0:31   ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-209944@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=akpm@linux-foundation.org \
    --cc=baolin.wang@linux.alibaba.com \
    --cc=brauner@kernel.org \
    --cc=corbet@lwn.net \
    --cc=david@kernel.org \
    --cc=dmatlack@google.com \
    --cc=graf@amazon.com \
    --cc=hughd@google.com \
    --cc=kexec@lists.infradead.org \
    --cc=liam@infradead.org \
    --cc=linux-mm@kvack.org \
    --cc=ljs@kernel.org \
    --cc=mhocko@suse.com \
    --cc=pasha.tatashin@soleen.com \
    --cc=pratyush@kernel.org \
    --cc=rdunlap@infradead.org \
    --cc=rppt@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=skhan@linuxfoundation.org \
    --cc=skhawaja@google.com \
    --cc=surenb@google.com \
    --cc=vbabka@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.