summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Gruenbacher <agruenba@redhat.com>2026-05-27 21:15:04 +0200
committerAndreas Gruenbacher <agruenba@redhat.com>2026-05-29 11:11:40 +0200
commit4982e58669b11c43644efb5fb7435975848b716e (patch)
tree7c3e48a85cd3e6ba18dfd541d60b0e89ee55a337
parent70008e22ab3fd619fb2a50dcfa80b9dfa26c5d8a (diff)
gfs2: page poisoning fix
Processes can write to the last page of a file using mmap, and when the file size is not a multiple of the page size, this can be used to write beyond the end of the file. This is sometimes referred to as page poisoning, and it is not a problem in itself because the data beyond eof will be ignored. However, we currently fail to clear out any space beyond the end of the file that we skip over when the file size is increased, so that "poison" can end up getting exposed. Fix that. Fixes xfstest generic/363. Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
-rw-r--r--fs/gfs2/bmap.c19
-rw-r--r--fs/gfs2/bmap.h1
-rw-r--r--fs/gfs2/file.c10
3 files changed, 30 insertions, 0 deletions
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index b3d7fcd95f03..95a64819fe2c 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -1321,6 +1321,19 @@ static int gfs2_block_zero_range(struct inode *inode, loff_t from, loff_t length
&gfs2_iomap_write_ops, NULL);
}
+int gfs2_clear_beyond_eof(struct inode *inode, loff_t end)
+{
+ loff_t isize = i_size_read(inode);
+ unsigned int len = isize & ~PAGE_MASK;
+
+ if (!len || isize >= end)
+ return 0;
+ len = PAGE_SIZE - len;
+ if (end - isize < len)
+ len = end - isize;
+ return gfs2_block_zero_range(inode, isize, len);
+}
+
#define GFS2_JTRUNC_REVOKES 8192
/**
@@ -2096,6 +2109,12 @@ static int do_grow(struct inode *inode, u64 size)
unstuff = 1;
}
+ if (!unstuff) {
+ error = gfs2_clear_beyond_eof(inode, size);
+ if (error)
+ goto do_grow_qunlock;
+ }
+
error = gfs2_trans_begin(sdp, RES_DINODE + RES_STATFS + RES_RG_BIT +
(unstuff &&
gfs2_is_jdata(ip) ? RES_JDATA : 0) +
diff --git a/fs/gfs2/bmap.h b/fs/gfs2/bmap.h
index 6cdc72dd55a3..e3d6efdfd890 100644
--- a/fs/gfs2/bmap.h
+++ b/fs/gfs2/bmap.h
@@ -58,6 +58,7 @@ int gfs2_get_extent(struct inode *inode, u64 lblock, u64 *dblock,
unsigned int *extlen);
int gfs2_alloc_extent(struct inode *inode, u64 lblock, u64 *dblock,
unsigned *extlen, bool *new);
+int gfs2_clear_beyond_eof(struct inode *inode, loff_t end);
int gfs2_setattr_size(struct inode *inode, u64 size);
int gfs2_truncatei_resume(struct gfs2_inode *ip);
int gfs2_file_dealloc(struct gfs2_inode *ip);
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index 48ebda5ba808..b8c10de113ba 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -1057,6 +1057,10 @@ retry:
goto out_unlock;
}
+ ret = gfs2_clear_beyond_eof(inode, iocb->ki_pos);
+ if (ret)
+ goto out_unlock;
+
pagefault_disable();
ret = iomap_file_buffered_write(iocb, from, &gfs2_iomap_ops,
&gfs2_iomap_write_ops, NULL);
@@ -1265,6 +1269,12 @@ static long __gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t
next = (next + 1) << sdp->sd_sb.sb_bsize_shift;
+ if (!(mode & FALLOC_FL_KEEP_SIZE)) {
+ error = gfs2_clear_beyond_eof(inode, offset + len);
+ if (error)
+ return error;
+ }
+
offset &= bsize_mask;
len = next - offset;