The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH] ramfs: nommu: fix error rollback and reader races in ramfs_nommu_expand_for_mapping()
@ 2026-07-13 11:57 Daniel Palmer
  2026-07-13 19:49 ` Hajime Tazaki
  0 siblings, 1 reply; 4+ messages in thread
From: Daniel Palmer @ 2026-07-13 11:57 UTC (permalink / raw)
  To: ljs, akpm, dhowells; +Cc: linux-kernel, thehajime, Daniel Palmer, Sashiko

ramfs_nommu_expand_for_mapping() sets the new i_size before it has
allocated or inserted any of the contiguous backing pages.

If alloc_pages() or add_to_page_cache_lru() fails, the inode is left
with an inflated i_size and possibly a partial run of pages.  As
ramfs_nommu_setattr() treats a truncate to the current i_size as a
no-op, the expansion cannot be retried and shared mmap() of the file
fails with -ENOSYS.

Setting i_size early also races with lockless readers: buffered reads
and splice do not take i_rwsem, so once the new size is visible a
concurrent read can instantiate a zero-filled folio, making the
expansion's add_to_page_cache_lru() fail with -EEXIST.

Fix this by taking mapping->invalidate_lock around the insertion,
evicting any stray folios first, and only publishing i_size once
every page is in place.

On failure, after freeing the pages that were allocated but
not inserted truncate the mapping back to empty so already inserted
pages are also disposed of.

Fixes: 642fb4d1f1dd ("[PATCH] NOMMU: Provide shared-writable mmap support on ramfs")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/message/20260523130445.1101818-1-daniel%40thingy.jp
Assisted-by: Claude:claude-5-fable # expanded my fix to address the reader race etc.
Signed-off-by: Daniel Palmer <daniel@thingy.jp>
---

sashiko found these issues on a previous patch to fix creating memfd's.
I quickly fixed up the i_size part and then fed it to fable for review
before sending and it said the reader thing needed to be fixed too and
expanded the commit message to reflect that.

I am not sure about the locking part at all so hopefully someone can
chime in on that. Maybe sashiko will say the things it reported and
fable fixed aren't a problem after all.. :).

I tested the happy path on my mc68000 virt machine and manually stepped
through the code to make sure its actually being executed and tested on
real hardware.

 fs/ramfs/file-nommu.c | 21 +++++++++++++++++----
 1 file changed, 17 insertions(+), 4 deletions(-)

diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c
index fb471bf88ab7..6819f4790065 100644
--- a/fs/ramfs/file-nommu.c
+++ b/fs/ramfs/file-nommu.c
@@ -80,8 +80,6 @@ int ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize)
 	if (ret)
 		return ret;
 
-	i_size_write(inode, newsize);
-
 	/* allocate enough contiguous pages to be able to satisfy the
 	 * request */
 	pages = alloc_pages(gfp, order);
@@ -99,9 +97,15 @@ int ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize)
 		__free_page(pages + loop);
 
 	/* clear the memory we allocated */
-	newsize = PAGE_SIZE * npages;
 	data = page_address(pages);
-	memset(data, 0, newsize);
+	memset(data, 0, PAGE_SIZE * npages);
+
+	/* block the read and splice paths from instantiating pagecache
+	 * folios whilst we build the contiguous mapping, and evict any
+	 * folio they may already have instantiated (the read path only
+	 * checks i_size after folio lookup/creation) */
+	filemap_invalidate_lock(inode->i_mapping);
+	truncate_inode_pages(inode->i_mapping, 0);
 
 	/* attach all the pages to the inode's address space */
 	for (loop = 0; loop < npages; loop++) {
@@ -120,11 +124,20 @@ int ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize)
 		put_page(page);
 	}
 
+	/* only publish the new size once all of the backing pages are in
+	 * place, so that readers never observe a size without the pages to
+	 * back it and a failure leaves the inode unchanged at size 0 */
+	i_size_write(inode, newsize);
+	filemap_invalidate_unlock(inode->i_mapping);
 	return 0;
 
 add_error:
+	/* free the pages we hadn't inserted yet... */
 	while (loop < npages)
 		__free_page(pages + loop++);
+	/* ...and evict the ones we had; they belong to the page cache now */
+	truncate_inode_pages(inode->i_mapping, 0);
+	filemap_invalidate_unlock(inode->i_mapping);
 	return ret;
 }
 
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-07-14  0:27 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-13 11:57 [PATCH] ramfs: nommu: fix error rollback and reader races in ramfs_nommu_expand_for_mapping() Daniel Palmer
2026-07-13 19:49 ` Hajime Tazaki
2026-07-13 23:36   ` Daniel Palmer
2026-07-14  0:26     ` Hajime Tazaki

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox