Skip to content

Commit 4a10df5

Browse files
committed
Merge: NFS: Protect against 'eof page pollution'
MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-10/-/merge_requests/1447 NFS: Protect against 'eof page pollution' JIRA: https://issues.redhat.com/browse/RHEL-95596 Signed-off-by: Olga Kornievskaia <okorniev@redhat.com> Approved-by: Benjamin Coddington <bcodding@redhat.com> Approved-by: Scott Mayhew <smayhew@redhat.com> Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> Merged-by: Scott Weaver <scweaver@redhat.com>
2 parents 50220e5 + bc422ba commit 4a10df5

File tree

5 files changed

+67
-11
lines changed

5 files changed

+67
-11
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: 24 additions & 9 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);
@@ -354,22 +362,27 @@ static int process_copy_commit(struct file *dst, loff_t pos_dst,
354362

355363
/**
356364
* nfs42_copy_dest_done - perform inode cache updates after clone/copy offload
357-
* @inode: pointer to destination inode
365+
* @file: pointer to destination file
358366
* @pos: destination offset
359367
* @len: copy length
368+
* @oldsize: length of the file prior to clone/copy
360369
*
361370
* Punch a hole in the inode page cache, so that the NFS client will
362371
* know to retrieve new data.
363372
* Update the file size if necessary, and then mark the inode as having
364373
* invalid cached values for change attribute, ctime, mtime and space used.
365374
*/
366-
static void nfs42_copy_dest_done(struct inode *inode, loff_t pos, loff_t len)
375+
static void nfs42_copy_dest_done(struct file *file, loff_t pos, loff_t len,
376+
loff_t oldsize)
367377
{
378+
struct inode *inode = file_inode(file);
379+
struct address_space *mapping = file->f_mapping;
368380
loff_t newsize = pos + len;
369381
loff_t end = newsize - 1;
370382

371-
WARN_ON_ONCE(invalidate_inode_pages2_range(inode->i_mapping,
372-
pos >> PAGE_SHIFT, end >> PAGE_SHIFT));
383+
nfs_truncate_last_folio(mapping, oldsize, pos);
384+
WARN_ON_ONCE(invalidate_inode_pages2_range(mapping, pos >> PAGE_SHIFT,
385+
end >> PAGE_SHIFT));
373386

374387
spin_lock(&inode->i_lock);
375388
if (newsize > i_size_read(inode))
@@ -402,6 +415,7 @@ static ssize_t _nfs42_proc_copy(struct file *src,
402415
struct nfs_server *src_server = NFS_SERVER(src_inode);
403416
loff_t pos_src = args->src_pos;
404417
loff_t pos_dst = args->dst_pos;
418+
loff_t oldsize_dst = i_size_read(dst_inode);
405419
size_t count = args->count;
406420
ssize_t status;
407421

@@ -475,7 +489,7 @@ static ssize_t _nfs42_proc_copy(struct file *src,
475489
goto out;
476490
}
477491

478-
nfs42_copy_dest_done(dst_inode, pos_dst, res->write_res.count);
492+
nfs42_copy_dest_done(dst, pos_dst, res->write_res.count, oldsize_dst);
479493
nfs_invalidate_atime(src_inode);
480494
status = res->write_res.count;
481495
out:
@@ -1242,6 +1256,7 @@ static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f,
12421256
struct nfs42_clone_res res = {
12431257
.server = server,
12441258
};
1259+
loff_t oldsize_dst = i_size_read(dst_inode);
12451260
int status;
12461261

12471262
msg->rpc_argp = &args;
@@ -1276,7 +1291,7 @@ static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f,
12761291
/* a zero-length count means clone to EOF in src */
12771292
if (count == 0 && res.dst_fattr->valid & NFS_ATTR_FATTR_SIZE)
12781293
count = nfs_size_to_loff_t(res.dst_fattr->size) - dst_offset;
1279-
nfs42_copy_dest_done(dst_inode, dst_offset, count);
1294+
nfs42_copy_dest_done(dst_f, dst_offset, count, oldsize_dst);
12801295
status = nfs_post_op_update_inode(dst_inode, res.dst_fattr);
12811296
}
12821297

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)