]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
gfs2: page poisoning fix
authorAndreas Gruenbacher <agruenba@redhat.com>
Wed, 27 May 2026 19:15:04 +0000 (21:15 +0200)
committerAndreas Gruenbacher <agruenba@redhat.com>
Fri, 29 May 2026 09:11:40 +0000 (11:11 +0200)
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>
fs/gfs2/bmap.c
fs/gfs2/bmap.h
fs/gfs2/file.c

index b3d7fcd95f03ce112ad39533836c2c99eb4bdb41..95a64819fe2c2fdae816160935806518faafaa96 100644 (file)
@@ -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) +
index 6cdc72dd55a3fa725e6ecd886baef3da00669c0a..e3d6efdfd8903798d62766714e4ca187662c02bb 100644 (file)
@@ -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);
index 48ebda5ba808b68f826fc18cb0c360a7aa2c48b4..b8c10de113ba7afc5232f7e86cacd6cfa2e825a8 100644 (file)
@@ -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;