Skip to content

Commit ec34dfe

Browse files
author
Olga Kornievskaia
committed
NFS: Protect against 'eof page pollution'
JIRA: https://issues.redhat.com/browse/RHEL-95596 commit b1817b1 Author: Trond Myklebust <trond.myklebust@hammerspace.com> Date: Thu Sep 4 18:46:16 2025 -0400 NFS: Protect against 'eof page pollution' This commit fixes the failing xfstest 'generic/363'. When the user mmaps() an area that extends beyond the end of file, and proceeds to write data into the folio that straddles that eof, we're required to discard that folio data if the user calls some function that extends the file length. Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com> Signed-off-by: Olga Kornievskaia <okorniev@redhat.com>
1 parent e3a7126 commit ec34dfe

File tree

5 files changed

+54
-5
lines changed

5 files changed

+54
-5
lines changed

fs/nfs/file.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <linux/mm.h>
2929
#include <linux/pagemap.h>
3030
#include <linux/gfp.h>
31+
#include <linux/rmap.h>
3132
#include <linux/swap.h>
3233
#include <linux/compaction.h>
3334

@@ -279,6 +280,37 @@ nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
279280
}
280281
EXPORT_SYMBOL_GPL(nfs_file_fsync);
281282

283+
void nfs_truncate_last_folio(struct address_space *mapping, loff_t from,
284+
loff_t to)
285+
{
286+
struct folio *folio;
287+
288+
if (from >= to)
289+
return;
290+
291+
folio = filemap_lock_folio(mapping, from >> PAGE_SHIFT);
292+
if (IS_ERR(folio))
293+
return;
294+
295+
if (folio_mkclean(folio))
296+
folio_mark_dirty(folio);
297+
298+
if (folio_test_uptodate(folio)) {
299+
loff_t fpos = folio_pos(folio);
300+
size_t offset = from - fpos;
301+
size_t end = folio_size(folio);
302+
303+
if (to - fpos < end)
304+
end = to - fpos;
305+
folio_zero_segment(folio, offset, end);
306+
trace_nfs_size_truncate_folio(mapping->host, to);
307+
}
308+
309+
folio_unlock(folio);
310+
folio_put(folio);
311+
}
312+
EXPORT_SYMBOL_GPL(nfs_truncate_last_folio);
313+
282314
/*
283315
* Decide whether a read/modify/write cycle may be more efficient
284316
* then a modify/write/read cycle when writing to a page in the
@@ -353,6 +385,7 @@ static int nfs_write_begin(struct file *file, struct address_space *mapping,
353385

354386
dfprintk(PAGECACHE, "NFS: write_begin(%pD2(%lu), %u@%lld)\n",
355387
file, mapping->host->i_ino, len, (long long) pos);
388+
nfs_truncate_last_folio(mapping, i_size_read(mapping->host), pos);
356389

357390
fgp |= fgf_set_order(len);
358391
start:

fs/nfs/inode.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,7 @@ nfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
710710
{
711711
struct inode *inode = d_inode(dentry);
712712
struct nfs_fattr *fattr;
713+
loff_t oldsize = i_size_read(inode);
713714
int error = 0;
714715

715716
nfs_inc_stats(inode, NFSIOS_VFSSETATTR);
@@ -725,7 +726,7 @@ nfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
725726
if (error)
726727
return error;
727728

728-
if (attr->ia_size == i_size_read(inode))
729+
if (attr->ia_size == oldsize)
729730
attr->ia_valid &= ~ATTR_SIZE;
730731
}
731732

@@ -771,8 +772,12 @@ nfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
771772
}
772773

773774
error = NFS_PROTO(inode)->setattr(dentry, fattr, attr);
774-
if (error == 0)
775+
if (error == 0) {
776+
if (attr->ia_valid & ATTR_SIZE)
777+
nfs_truncate_last_folio(inode->i_mapping, oldsize,
778+
attr->ia_size);
775779
error = nfs_refresh_inode(inode, fattr);
780+
}
776781
nfs_free_fattr(fattr);
777782
out:
778783
trace_nfs_setattr_exit(inode, error);

fs/nfs/internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,8 @@ int nfs_file_release(struct inode *, struct file *);
438438
int nfs_lock(struct file *, int, struct file_lock *);
439439
int nfs_flock(struct file *, int, struct file_lock *);
440440
int nfs_check_flags(int);
441+
void nfs_truncate_last_folio(struct address_space *mapping, loff_t from,
442+
loff_t to);
441443

442444
/* inode.c */
443445
extern struct workqueue_struct *nfsiod_workqueue;

fs/nfs/nfs42proc.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ int nfs42_proc_allocate(struct file *filep, loff_t offset, loff_t len)
137137
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ALLOCATE],
138138
};
139139
struct inode *inode = file_inode(filep);
140+
loff_t oldsize = i_size_read(inode);
140141
int err;
141142

142143
if (!nfs_server_capable(inode, NFS_CAP_ALLOCATE))
@@ -145,7 +146,11 @@ int nfs42_proc_allocate(struct file *filep, loff_t offset, loff_t len)
145146
inode_lock(inode);
146147

147148
err = nfs42_proc_fallocate(&msg, filep, offset, len);
148-
if (err == -EOPNOTSUPP)
149+
150+
if (err == 0)
151+
nfs_truncate_last_folio(inode->i_mapping, oldsize,
152+
offset + len);
153+
else if (err == -EOPNOTSUPP)
149154
NFS_SERVER(inode)->caps &= ~(NFS_CAP_ALLOCATE |
150155
NFS_CAP_ZERO_RANGE);
151156

@@ -183,6 +188,7 @@ int nfs42_proc_zero_range(struct file *filep, loff_t offset, loff_t len)
183188
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ZERO_RANGE],
184189
};
185190
struct inode *inode = file_inode(filep);
191+
loff_t oldsize = i_size_read(inode);
186192
int err;
187193

188194
if (!nfs_server_capable(inode, NFS_CAP_ZERO_RANGE))
@@ -191,9 +197,11 @@ int nfs42_proc_zero_range(struct file *filep, loff_t offset, loff_t len)
191197
inode_lock(inode);
192198

193199
err = nfs42_proc_fallocate(&msg, filep, offset, len);
194-
if (err == 0)
200+
if (err == 0) {
201+
nfs_truncate_last_folio(inode->i_mapping, oldsize,
202+
offset + len);
195203
truncate_pagecache_range(inode, offset, (offset + len) -1);
196-
if (err == -EOPNOTSUPP)
204+
} else if (err == -EOPNOTSUPP)
197205
NFS_SERVER(inode)->caps &= ~NFS_CAP_ZERO_RANGE;
198206

199207
inode_unlock(inode);

fs/nfs/nfstrace.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ DECLARE_EVENT_CLASS(nfs_update_size_class,
267267
TP_ARGS(inode, new_size))
268268

269269
DEFINE_NFS_UPDATE_SIZE_EVENT(truncate);
270+
DEFINE_NFS_UPDATE_SIZE_EVENT(truncate_folio);
270271
DEFINE_NFS_UPDATE_SIZE_EVENT(wcc);
271272
DEFINE_NFS_UPDATE_SIZE_EVENT(update);
272273
DEFINE_NFS_UPDATE_SIZE_EVENT(grow);

0 commit comments

Comments
 (0)