Fix CVE-2026-45984. Andreas Gruenbacher (6): gfs2: Add new gfs2_iomap_get helper gfs2: Turn gfs2_extent_map into gfs2_{get,alloc}_extent gfs2: Replace gfs2_lblk_to_dblk with gfs2_get_extent gfs2: Add wrapper for iomap_file_buffered_write gfs2: Move the inode glock locking to gfs2_file_buffered_write gfs2: Add metapath_dibh helper Deepanshu Kartikey (1): gfs2: Fix use-after-free in iomap inline data write path fs/gfs2/bmap.c | 227 +++++++++++++++++++-------------------------- fs/gfs2/bmap.h | 13 ++- fs/gfs2/dir.c | 13 ++- fs/gfs2/file.c | 59 +++++++++--- fs/gfs2/log.c | 6 +- fs/gfs2/quota.c | 4 +- fs/gfs2/recovery.c | 4 +- 7 files changed, 162 insertions(+), 164 deletions(-) -- 2.34.1
From: Andreas Gruenbacher <agruenba@redhat.com> stable inclusion from stable-v5.10.252 commit a39b8e41d2f66f81c46ac2604bae059fcdb21a6a category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15235 CVE: CVE-2026-45984 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- [ Upstream commit 54992257fe4bb9f76f66b3863492aa8cc5567790 ] Rename the current gfs2_iomap_get and gfs2_iomap_alloc functions to __*. Add a new gfs2_iomap_get helper that doesn't expose struct metapath. Rename gfs2_iomap_get_alloc to gfs2_iomap_alloc. Use the new helpers where they make sense. Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> Stable-dep-of: faddeb848305 ("gfs2: Fix use-after-free in iomap inline data write path") Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Hongbo Li <lihongbo22@huawei.com> --- fs/gfs2/bmap.c | 65 +++++++++++++++++++++++++++----------------------- fs/gfs2/bmap.h | 6 +++-- fs/gfs2/file.c | 5 ++-- 3 files changed, 41 insertions(+), 35 deletions(-) diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 3d60ad9982c8..8ec1114ab452 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -629,7 +629,7 @@ enum alloc_state { }; /** - * gfs2_iomap_alloc - Build a metadata tree of the requested height + * __gfs2_iomap_alloc - Build a metadata tree of the requested height * @inode: The GFS2 inode * @iomap: The iomap structure * @mp: The metapath, with proper height information calculated @@ -639,7 +639,7 @@ enum alloc_state { * ii) Indirect blocks to fill in lower part of the metadata tree * iii) Data blocks * - * This function is called after gfs2_iomap_get, which works out the + * This function is called after __gfs2_iomap_get, which works out the * total number of blocks which we need via gfs2_alloc_size. * * We then do the actual allocation asking for an extent at a time (if @@ -657,8 +657,8 @@ enum alloc_state { * Returns: errno on error */ -static int gfs2_iomap_alloc(struct inode *inode, struct iomap *iomap, - struct metapath *mp) +static int __gfs2_iomap_alloc(struct inode *inode, struct iomap *iomap, + struct metapath *mp) { struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); @@ -799,10 +799,10 @@ static u64 gfs2_alloc_size(struct inode *inode, struct metapath *mp, u64 size) /* * For writes to stuffed files, this function is called twice via - * gfs2_iomap_get, before and after unstuffing. The size we return the + * __gfs2_iomap_get, before and after unstuffing. The size we return the * first time needs to be large enough to get the reservation and * allocation sizes right. The size we return the second time must - * be exact or else gfs2_iomap_alloc won't do the right thing. + * be exact or else __gfs2_iomap_alloc won't do the right thing. */ if (gfs2_is_stuffed(ip) || mp->mp_fheight != mp->mp_aheight) { @@ -826,7 +826,7 @@ static u64 gfs2_alloc_size(struct inode *inode, struct metapath *mp, u64 size) } /** - * gfs2_iomap_get - Map blocks from an inode to disk blocks + * __gfs2_iomap_get - Map blocks from an inode to disk blocks * @inode: The inode * @pos: Starting position in bytes * @length: Length to map, in bytes @@ -836,9 +836,9 @@ static u64 gfs2_alloc_size(struct inode *inode, struct metapath *mp, u64 size) * * Returns: errno */ -static int gfs2_iomap_get(struct inode *inode, loff_t pos, loff_t length, - unsigned flags, struct iomap *iomap, - struct metapath *mp) +static int __gfs2_iomap_get(struct inode *inode, loff_t pos, loff_t length, + unsigned flags, struct iomap *iomap, + struct metapath *mp) { struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); @@ -972,12 +972,10 @@ static int gfs2_iomap_get(struct inode *inode, loff_t pos, loff_t length, int gfs2_lblk_to_dblk(struct inode *inode, u32 lblock, u64 *dblock) { struct iomap iomap = { }; - struct metapath mp = { .mp_aheight = 1, }; loff_t pos = (loff_t)lblock << inode->i_blkbits; int ret; - ret = gfs2_iomap_get(inode, pos, i_blocksize(inode), 0, &iomap, &mp); - release_metapath(&mp); + ret = gfs2_iomap_get(inode, pos, i_blocksize(inode), &iomap); if (ret == 0) *dblock = iomap.addr >> inode->i_blkbits; @@ -1106,14 +1104,14 @@ static int gfs2_iomap_begin_write(struct inode *inode, loff_t pos, if (ret) goto out_trans_end; release_metapath(mp); - ret = gfs2_iomap_get(inode, iomap->offset, - iomap->length, flags, iomap, mp); + ret = __gfs2_iomap_get(inode, iomap->offset, + iomap->length, flags, iomap, mp); if (ret) goto out_trans_end; } if (iomap->type == IOMAP_HOLE) { - ret = gfs2_iomap_alloc(inode, iomap, mp); + ret = __gfs2_iomap_alloc(inode, iomap, mp); if (ret) { gfs2_trans_end(sdp); gfs2_inplace_release(ip); @@ -1165,7 +1163,7 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, goto out; } - ret = gfs2_iomap_get(inode, pos, length, flags, iomap, &mp); + ret = __gfs2_iomap_get(inode, pos, length, flags, iomap, &mp); if (ret) goto out_unlock; @@ -1286,9 +1284,7 @@ int gfs2_block_map(struct inode *inode, sector_t lblock, struct gfs2_inode *ip = GFS2_I(inode); loff_t pos = (loff_t)lblock << inode->i_blkbits; loff_t length = bh_map->b_size; - struct metapath mp = { .mp_aheight = 1, }; struct iomap iomap = { }; - int flags = create ? IOMAP_WRITE : 0; int ret; clear_buffer_mapped(bh_map); @@ -1296,10 +1292,10 @@ int gfs2_block_map(struct inode *inode, sector_t lblock, clear_buffer_boundary(bh_map); trace_gfs2_bmap(ip, bh_map, lblock, create, 1); - ret = gfs2_iomap_get(inode, pos, length, flags, &iomap, &mp); - if (create && !ret && iomap.type == IOMAP_HOLE) - ret = gfs2_iomap_alloc(inode, &iomap, &mp); - release_metapath(&mp); + if (!create) + ret = gfs2_iomap_get(inode, pos, length, &iomap); + else + ret = gfs2_iomap_alloc(inode, pos, length, &iomap); if (ret) goto out; @@ -1457,15 +1453,26 @@ static int trunc_start(struct inode *inode, u64 newsize) return error; } -int gfs2_iomap_get_alloc(struct inode *inode, loff_t pos, loff_t length, - struct iomap *iomap) +int gfs2_iomap_get(struct inode *inode, loff_t pos, loff_t length, + struct iomap *iomap) +{ + struct metapath mp = { .mp_aheight = 1, }; + int ret; + + ret = __gfs2_iomap_get(inode, pos, length, 0, iomap, &mp); + release_metapath(&mp); + return ret; +} + +int gfs2_iomap_alloc(struct inode *inode, loff_t pos, loff_t length, + struct iomap *iomap) { struct metapath mp = { .mp_aheight = 1, }; int ret; - ret = gfs2_iomap_get(inode, pos, length, IOMAP_WRITE, iomap, &mp); + ret = __gfs2_iomap_get(inode, pos, length, IOMAP_WRITE, iomap, &mp); if (!ret && iomap->type == IOMAP_HOLE) - ret = gfs2_iomap_alloc(inode, iomap, &mp); + ret = __gfs2_iomap_alloc(inode, iomap, &mp); release_metapath(&mp); return ret; } @@ -2516,7 +2523,6 @@ int __gfs2_punch_hole(struct file *file, loff_t offset, loff_t length) static int gfs2_map_blocks(struct iomap_writepage_ctx *wpc, struct inode *inode, loff_t offset) { - struct metapath mp = { .mp_aheight = 1, }; int ret; if (WARN_ON_ONCE(gfs2_is_stuffed(GFS2_I(inode)))) @@ -2527,8 +2533,7 @@ static int gfs2_map_blocks(struct iomap_writepage_ctx *wpc, struct inode *inode, return 0; memset(&wpc->iomap, 0, sizeof(wpc->iomap)); - ret = gfs2_iomap_get(inode, offset, INT_MAX, 0, &wpc->iomap, &mp); - release_metapath(&mp); + ret = gfs2_iomap_get(inode, offset, INT_MAX, &wpc->iomap); return ret; } diff --git a/fs/gfs2/bmap.h b/fs/gfs2/bmap.h index aed4632d47d3..c63efee8aaa4 100644 --- a/fs/gfs2/bmap.h +++ b/fs/gfs2/bmap.h @@ -49,8 +49,10 @@ extern const struct iomap_writeback_ops gfs2_writeback_ops; extern int gfs2_unstuff_dinode(struct gfs2_inode *ip, struct page *page); extern int gfs2_block_map(struct inode *inode, sector_t lblock, struct buffer_head *bh, int create); -extern int gfs2_iomap_get_alloc(struct inode *inode, loff_t pos, loff_t length, - struct iomap *iomap); +extern int gfs2_iomap_get(struct inode *inode, loff_t pos, loff_t length, + struct iomap *iomap); +extern int gfs2_iomap_alloc(struct inode *inode, loff_t pos, loff_t length, + struct iomap *iomap); extern int gfs2_extent_map(struct inode *inode, u64 lblock, int *new, u64 *dblock, unsigned *extlen); extern int gfs2_setattr_size(struct inode *inode, u64 size); diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 24ab28f02004..fd33858e5cde 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -421,7 +421,7 @@ static int gfs2_allocate_page_backing(struct page *page, unsigned int length) do { struct iomap iomap = { }; - if (gfs2_iomap_get_alloc(page->mapping->host, pos, length, &iomap)) + if (gfs2_iomap_alloc(page->mapping->host, pos, length, &iomap)) return -EIO; if (length < iomap.length) @@ -997,8 +997,7 @@ static int fallocate_chunk(struct inode *inode, loff_t offset, loff_t len, while (offset < end) { struct iomap iomap = { }; - error = gfs2_iomap_get_alloc(inode, offset, end - offset, - &iomap); + error = gfs2_iomap_alloc(inode, offset, end - offset, &iomap); if (error) goto out; offset = iomap.offset + iomap.length; -- 2.34.1
From: Andreas Gruenbacher <agruenba@redhat.com> stable inclusion from stable-v5.10.252 commit 6453edad624d7396bf37ef2ddc53c04883cbfe1f category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15235 CVE: CVE-2026-45984 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- [ Upstream commit 9153dac13a6966b63183bac450d5cd39b07cc85c ] Convert gfs2_extent_map to iomap and split it into gfs2_get_extent and gfs2_alloc_extent. Instead of hardcoding the extent size, pass it in via the extlen parameter. Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> Stable-dep-of: faddeb848305 ("gfs2: Fix use-after-free in iomap inline data write path") Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Hongbo Li <lihongbo22@huawei.com> --- fs/gfs2/bmap.c | 59 ++++++++++++++++++++++++++++++---------------- fs/gfs2/bmap.h | 6 +++-- fs/gfs2/dir.c | 13 +++++----- fs/gfs2/quota.c | 4 ++-- fs/gfs2/recovery.c | 4 ++-- 5 files changed, 53 insertions(+), 33 deletions(-) diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 8ec1114ab452..41e98499862d 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1316,28 +1316,47 @@ int gfs2_block_map(struct inode *inode, sector_t lblock, return ret; } -/* - * Deprecated: do not use in new code - */ -int gfs2_extent_map(struct inode *inode, u64 lblock, int *new, u64 *dblock, unsigned *extlen) +int gfs2_get_extent(struct inode *inode, u64 lblock, u64 *dblock, + unsigned int *extlen) { - struct buffer_head bh = { .b_state = 0, .b_blocknr = 0 }; + unsigned int blkbits = inode->i_blkbits; + struct iomap iomap = { }; + unsigned int len; int ret; - int create = *new; - - BUG_ON(!extlen); - BUG_ON(!dblock); - BUG_ON(!new); - - bh.b_size = BIT(inode->i_blkbits + (create ? 0 : 5)); - ret = gfs2_block_map(inode, lblock, &bh, create); - *extlen = bh.b_size >> inode->i_blkbits; - *dblock = bh.b_blocknr; - if (buffer_new(&bh)) - *new = 1; - else - *new = 0; - return ret; + + ret = gfs2_iomap_get(inode, lblock << blkbits, *extlen << blkbits, + &iomap); + if (ret) + return ret; + if (iomap.type != IOMAP_MAPPED) + return -EIO; + *dblock = iomap.addr >> blkbits; + len = iomap.length >> blkbits; + if (len < *extlen) + *extlen = len; + return 0; +} + +int gfs2_alloc_extent(struct inode *inode, u64 lblock, u64 *dblock, + unsigned int *extlen, bool *new) +{ + unsigned int blkbits = inode->i_blkbits; + struct iomap iomap = { }; + unsigned int len; + int ret; + + ret = gfs2_iomap_alloc(inode, lblock << blkbits, *extlen << blkbits, + &iomap); + if (ret) + return ret; + if (iomap.type != IOMAP_MAPPED) + return -EIO; + *dblock = iomap.addr >> blkbits; + len = iomap.length >> blkbits; + if (len < *extlen) + *extlen = len; + *new = iomap.flags & IOMAP_F_NEW; + return 0; } /* diff --git a/fs/gfs2/bmap.h b/fs/gfs2/bmap.h index c63efee8aaa4..67ef7cf7fdac 100644 --- a/fs/gfs2/bmap.h +++ b/fs/gfs2/bmap.h @@ -53,8 +53,10 @@ extern int gfs2_iomap_get(struct inode *inode, loff_t pos, loff_t length, struct iomap *iomap); extern int gfs2_iomap_alloc(struct inode *inode, loff_t pos, loff_t length, struct iomap *iomap); -extern int gfs2_extent_map(struct inode *inode, u64 lblock, int *new, - u64 *dblock, unsigned *extlen); +extern int gfs2_get_extent(struct inode *inode, u64 lblock, u64 *dblock, + unsigned int *extlen); +extern int gfs2_alloc_extent(struct inode *inode, u64 lblock, u64 *dblock, + unsigned *extlen, bool *new); extern int gfs2_setattr_size(struct inode *inode, u64 size); extern void gfs2_trim_blocks(struct inode *inode); extern int gfs2_truncatei_resume(struct gfs2_inode *ip); diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c index a0c7be449271..7b11f7b7151a 100644 --- a/fs/gfs2/dir.c +++ b/fs/gfs2/dir.c @@ -160,7 +160,7 @@ static int gfs2_dir_write_data(struct gfs2_inode *ip, const char *buf, unsigned int o; int copied = 0; int error = 0; - int new = 0; + bool new = false; if (!size) return 0; @@ -190,9 +190,9 @@ static int gfs2_dir_write_data(struct gfs2_inode *ip, const char *buf, amount = sdp->sd_sb.sb_bsize - o; if (!extlen) { - new = 1; - error = gfs2_extent_map(&ip->i_inode, lblock, &new, - &dblock, &extlen); + extlen = 1; + error = gfs2_alloc_extent(&ip->i_inode, lblock, &dblock, + &extlen, &new); if (error) goto fail; error = -EIO; @@ -287,15 +287,14 @@ static int gfs2_dir_read_data(struct gfs2_inode *ip, __be64 *buf, while (copied < size) { unsigned int amount; struct buffer_head *bh; - int new; amount = size - copied; if (amount > sdp->sd_sb.sb_bsize - o) amount = sdp->sd_sb.sb_bsize - o; if (!extlen) { - new = 0; - error = gfs2_extent_map(&ip->i_inode, lblock, &new, + extlen = 32; + error = gfs2_get_extent(&ip->i_inode, lblock, &dblock, &extlen); if (error || !dblock) goto fail; diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 57897826da41..c822d8c08504 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -1390,8 +1390,8 @@ int gfs2_quota_init(struct gfs2_sbd *sdp) unsigned int y; if (!extlen) { - int new = 0; - error = gfs2_extent_map(&ip->i_inode, x, &new, &dblock, &extlen); + extlen = 32; + error = gfs2_get_extent(&ip->i_inode, x, &dblock, &extlen); if (error) goto fail; } diff --git a/fs/gfs2/recovery.c b/fs/gfs2/recovery.c index 8f9c6480a5df..b925ba068af5 100644 --- a/fs/gfs2/recovery.c +++ b/fs/gfs2/recovery.c @@ -34,12 +34,12 @@ int gfs2_replay_read_block(struct gfs2_jdesc *jd, unsigned int blk, { struct gfs2_inode *ip = GFS2_I(jd->jd_inode); struct gfs2_glock *gl = ip->i_gl; - int new = 0; u64 dblock; u32 extlen; int error; - error = gfs2_extent_map(&ip->i_inode, blk, &new, &dblock, &extlen); + extlen = 32; + error = gfs2_get_extent(&ip->i_inode, blk, &dblock, &extlen); if (error) return error; if (!dblock) { -- 2.34.1
From: Andreas Gruenbacher <agruenba@redhat.com> stable inclusion from stable-v5.10.252 commit 775d2ad001122b7d612a23bef3df477dc7e684d6 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15235 CVE: CVE-2026-45984 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- [ Upstream commit 152f58c9af21abf913699e671b425fd38447b170 ] We don't need two very similar functions for mapping logical blocks to physical blocks. Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> Stable-dep-of: faddeb848305 ("gfs2: Fix use-after-free in iomap inline data write path") Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Hongbo Li <lihongbo22@huawei.com> --- fs/gfs2/bmap.c | 24 ------------------------ fs/gfs2/bmap.h | 1 - fs/gfs2/log.c | 6 +++++- 3 files changed, 5 insertions(+), 26 deletions(-) diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 41e98499862d..4514d9a8d3cd 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -958,30 +958,6 @@ static int __gfs2_iomap_get(struct inode *inode, loff_t pos, loff_t length, goto out; } -/** - * gfs2_lblk_to_dblk - convert logical block to disk block - * @inode: the inode of the file we're mapping - * @lblock: the block relative to the start of the file - * @dblock: the returned dblock, if no error - * - * This function maps a single block from a file logical block (relative to - * the start of the file) to a file system absolute block using iomap. - * - * Returns: the absolute file system block, or an error - */ -int gfs2_lblk_to_dblk(struct inode *inode, u32 lblock, u64 *dblock) -{ - struct iomap iomap = { }; - loff_t pos = (loff_t)lblock << inode->i_blkbits; - int ret; - - ret = gfs2_iomap_get(inode, pos, i_blocksize(inode), &iomap); - if (ret == 0) - *dblock = iomap.addr >> inode->i_blkbits; - - return ret; -} - static int gfs2_write_lock(struct inode *inode) { struct gfs2_inode *ip = GFS2_I(inode); diff --git a/fs/gfs2/bmap.h b/fs/gfs2/bmap.h index 67ef7cf7fdac..6676d863faef 100644 --- a/fs/gfs2/bmap.h +++ b/fs/gfs2/bmap.h @@ -66,6 +66,5 @@ extern int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset, extern int gfs2_map_journal_extents(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd); extern void gfs2_free_journal_extents(struct gfs2_jdesc *jd); extern int __gfs2_punch_hole(struct file *file, loff_t offset, loff_t length); -extern int gfs2_lblk_to_dblk(struct inode *inode, u32 lblock, u64 *dblock); #endif /* __BMAP_DOT_H__ */ diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index 7473b894e3c6..a667d315b156 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -793,7 +793,11 @@ void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd, if (!list_empty(&jd->extent_list)) dblock = gfs2_log_bmap(jd, lblock); else { - int ret = gfs2_lblk_to_dblk(jd->jd_inode, lblock, &dblock); + unsigned int extlen; + int ret; + + extlen = 1; + ret = gfs2_get_extent(jd->jd_inode, lblock, &dblock, &extlen); if (gfs2_assert_withdraw(sdp, ret == 0)) return; } -- 2.34.1
From: Andreas Gruenbacher <agruenba@redhat.com> stable inclusion from stable-v5.10.252 commit 52a1f117a4b4d856cc613106f51f96b962fea3b3 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15235 CVE: CVE-2026-45984 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- [ Upstream commit 2eb7509a05443048fb4df60b782de3f03c6c298b ] Add a wrapper around iomap_file_buffered_write. We'll add code for when the operation needs to be retried here later. Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> Stable-dep-of: faddeb848305 ("gfs2: Fix use-after-free in iomap inline data write path") Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Hongbo Li <lihongbo22@huawei.com> --- fs/gfs2/file.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index fd33858e5cde..52c16848a2fd 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -884,6 +884,20 @@ static ssize_t gfs2_file_read_iter(struct kiocb *iocb, struct iov_iter *to) return written ? written : ret; } +static ssize_t gfs2_file_buffered_write(struct kiocb *iocb, struct iov_iter *from) +{ + struct file *file = iocb->ki_filp; + struct inode *inode = file_inode(file); + ssize_t ret; + + current->backing_dev_info = inode_to_bdi(inode); + ret = iomap_file_buffered_write(iocb, from, &gfs2_iomap_ops); + current->backing_dev_info = NULL; + if (ret > 0) + iocb->ki_pos += ret; + return ret; +} + /** * gfs2_file_write_iter - Perform a write to a file * @iocb: The io context @@ -935,9 +949,7 @@ static ssize_t gfs2_file_write_iter(struct kiocb *iocb, struct iov_iter *from) goto out_unlock; iocb->ki_flags |= IOCB_DSYNC; - current->backing_dev_info = inode_to_bdi(inode); - buffered = iomap_file_buffered_write(iocb, from, &gfs2_iomap_ops); - current->backing_dev_info = NULL; + buffered = gfs2_file_buffered_write(iocb, from); if (unlikely(buffered <= 0)) { if (!ret) ret = buffered; @@ -951,7 +963,6 @@ static ssize_t gfs2_file_write_iter(struct kiocb *iocb, struct iov_iter *from) * the direct I/O range as we don't know if the buffered pages * made it to disk. */ - iocb->ki_pos += buffered; ret2 = generic_write_sync(iocb, buffered); invalidate_mapping_pages(mapping, (iocb->ki_pos - buffered) >> PAGE_SHIFT, @@ -959,13 +970,9 @@ static ssize_t gfs2_file_write_iter(struct kiocb *iocb, struct iov_iter *from) if (!ret || ret2 > 0) ret += ret2; } else { - current->backing_dev_info = inode_to_bdi(inode); - ret = iomap_file_buffered_write(iocb, from, &gfs2_iomap_ops); - current->backing_dev_info = NULL; - if (likely(ret > 0)) { - iocb->ki_pos += ret; + ret = gfs2_file_buffered_write(iocb, from); + if (likely(ret > 0)) ret = generic_write_sync(iocb, ret); - } } out_unlock: -- 2.34.1
From: Andreas Gruenbacher <agruenba@redhat.com> stable inclusion from stable-v5.10.252 commit d1a71694e0260c0a9beb623aaab15201fc65fb66 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15235 CVE: CVE-2026-45984 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- [ Upstream commit b924bdab7445946e2ed364a0e6e249d36f1f1158 ] So far, for buffered writes, we were taking the inode glock in gfs2_iomap_begin and dropping it in gfs2_iomap_end with the intention of not holding the inode glock while iomap_write_actor faults in user pages. It turns out that iomap_write_actor is called inside iomap_begin ... iomap_end, so the user pages were still faulted in while holding the inode glock and the locking code in iomap_begin / iomap_end was completely pointless. Move the locking into gfs2_file_buffered_write instead. We'll take care of the potential deadlocks due to faulting in user pages while holding a glock in a subsequent patch. Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> Stable-dep-of: faddeb848305 ("gfs2: Fix use-after-free in iomap inline data write path") Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Hongbo Li <lihongbo22@huawei.com> --- fs/gfs2/bmap.c | 60 +------------------------------------------------- fs/gfs2/file.c | 27 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 59 deletions(-) diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 4514d9a8d3cd..f7d51dd6559c 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -958,46 +958,6 @@ static int __gfs2_iomap_get(struct inode *inode, loff_t pos, loff_t length, goto out; } -static int gfs2_write_lock(struct inode *inode) -{ - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_sbd *sdp = GFS2_SB(inode); - int error; - - gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &ip->i_gh); - error = gfs2_glock_nq(&ip->i_gh); - if (error) - goto out_uninit; - if (&ip->i_inode == sdp->sd_rindex) { - struct gfs2_inode *m_ip = GFS2_I(sdp->sd_statfs_inode); - - error = gfs2_glock_nq_init(m_ip->i_gl, LM_ST_EXCLUSIVE, - GL_NOCACHE, &m_ip->i_gh); - if (error) - goto out_unlock; - } - return 0; - -out_unlock: - gfs2_glock_dq(&ip->i_gh); -out_uninit: - gfs2_holder_uninit(&ip->i_gh); - return error; -} - -static void gfs2_write_unlock(struct inode *inode) -{ - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_sbd *sdp = GFS2_SB(inode); - - if (&ip->i_inode == sdp->sd_rindex) { - struct gfs2_inode *m_ip = GFS2_I(sdp->sd_statfs_inode); - - gfs2_glock_dq_uninit(&m_ip->i_gh); - } - gfs2_glock_dq_uninit(&ip->i_gh); -} - static int gfs2_iomap_page_prepare(struct inode *inode, loff_t pos, unsigned len, struct iomap *iomap) { @@ -1116,11 +1076,6 @@ static int gfs2_iomap_begin_write(struct inode *inode, loff_t pos, return ret; } -static inline bool gfs2_iomap_need_write_lock(unsigned flags) -{ - return (flags & IOMAP_WRITE) && !(flags & IOMAP_DIRECT); -} - static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, unsigned flags, struct iomap *iomap, struct iomap *srcmap) @@ -1133,12 +1088,6 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, iomap->flags |= IOMAP_F_BUFFER_HEAD; trace_gfs2_iomap_start(ip, pos, length, flags); - if (gfs2_iomap_need_write_lock(flags)) { - ret = gfs2_write_lock(inode); - if (ret) - goto out; - } - ret = __gfs2_iomap_get(inode, pos, length, flags, iomap, &mp); if (ret) goto out_unlock; @@ -1166,10 +1115,7 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp); out_unlock: - if (ret && gfs2_iomap_need_write_lock(flags)) - gfs2_write_unlock(inode); release_metapath(&mp); -out: trace_gfs2_iomap_end(ip, iomap, ret); return ret; } @@ -1216,15 +1162,11 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length, } if (unlikely(!written)) - goto out_unlock; + return 0; if (iomap->flags & IOMAP_F_SIZE_CHANGED) mark_inode_dirty(inode); set_bit(GLF_DIRTY, &ip->i_gl->gl_flags); - -out_unlock: - if (gfs2_iomap_need_write_lock(flags)) - gfs2_write_unlock(inode); return 0; } diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 52c16848a2fd..06dc4fee7be1 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -888,13 +888,40 @@ static ssize_t gfs2_file_buffered_write(struct kiocb *iocb, struct iov_iter *fro { struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); + struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_sbd *sdp = GFS2_SB(inode); ssize_t ret; + gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &ip->i_gh); + ret = gfs2_glock_nq(&ip->i_gh); + if (ret) + goto out_uninit; + + if (inode == sdp->sd_rindex) { + struct gfs2_inode *m_ip = GFS2_I(sdp->sd_statfs_inode); + + ret = gfs2_glock_nq_init(m_ip->i_gl, LM_ST_EXCLUSIVE, + GL_NOCACHE, &m_ip->i_gh); + if (ret) + goto out_unlock; + } + current->backing_dev_info = inode_to_bdi(inode); ret = iomap_file_buffered_write(iocb, from, &gfs2_iomap_ops); current->backing_dev_info = NULL; if (ret > 0) iocb->ki_pos += ret; + + if (inode == sdp->sd_rindex) { + struct gfs2_inode *m_ip = GFS2_I(sdp->sd_statfs_inode); + + gfs2_glock_dq_uninit(&m_ip->i_gh); + } + +out_unlock: + gfs2_glock_dq(&ip->i_gh); +out_uninit: + gfs2_holder_uninit(&ip->i_gh); return ret; } -- 2.34.1
From: Andreas Gruenbacher <agruenba@redhat.com> stable inclusion from stable-v5.10.252 commit 17eba0be94cebced190fee2742e383ad03094d07 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15235 CVE: CVE-2026-45984 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- [ Upstream commit 92099f0c92270c8c7a79e6bc6e0312ad248ea331 ] Add a metapath_dibh() helper for extracting the inode's buffer head from a metapath. Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> Stable-dep-of: faddeb848305 ("gfs2: Fix use-after-free in iomap inline data write path") Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Hongbo Li <lihongbo22@huawei.com> --- fs/gfs2/bmap.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index f7d51dd6559c..1e0e350b5a5a 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -318,6 +318,12 @@ static void gfs2_metapath_ra(struct gfs2_glock *gl, __be64 *start, __be64 *end) } } +static inline struct buffer_head * +metapath_dibh(struct metapath *mp) +{ + return mp->mp_bh[0]; +} + static int __fillup_metapath(struct gfs2_inode *ip, struct metapath *mp, unsigned int x, unsigned int h) { @@ -662,7 +668,7 @@ static int __gfs2_iomap_alloc(struct inode *inode, struct iomap *iomap, { struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); - struct buffer_head *dibh = mp->mp_bh[0]; + struct buffer_head *dibh = metapath_dibh(mp); u64 bn; unsigned n, i, blks, alloced = 0, iblks = 0, branch_start = 0; size_t dblks = iomap->length >> inode->i_blkbits; -- 2.34.1
From: Deepanshu Kartikey <kartikey406@gmail.com> stable inclusion from stable-v5.10.252 commit 1403989d1b502f4a2c0d0b42ccf1c25748442eff category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15235 CVE: CVE-2026-45984 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- [ Upstream commit faddeb848305e79db89ee0479bb0e33380656321 ] The inline data buffer head (dibh) is being released prematurely in gfs2_iomap_begin() via release_metapath() while iomap->inline_data still points to dibh->b_data. This causes a use-after-free when iomap_write_end_inline() later attempts to write to the inline data area. The bug sequence: 1. gfs2_iomap_begin() calls gfs2_meta_inode_buffer() to read inode metadata into dibh 2. Sets iomap->inline_data = dibh->b_data + sizeof(struct gfs2_dinode) 3. Calls release_metapath() which calls brelse(dibh), dropping refcount to 0 4. kswapd reclaims the page (~39ms later in the syzbot report) 5. iomap_write_end_inline() tries to memcpy() to iomap->inline_data 6. KASAN detects use-after-free write to freed memory Fix by storing dibh in iomap->private and incrementing its refcount with get_bh() in gfs2_iomap_begin(). The buffer is then properly released in gfs2_iomap_end() after the inline write completes, ensuring the page stays alive for the entire iomap operation. Note: A C reproducer is not available for this issue. The fix is based on analysis of the KASAN report and code review showing the buffer head is freed before use. [agruenba: Take buffer head reference in gfs2_iomap_begin() to avoid leaks in gfs2_iomap_get() and gfs2_iomap_alloc().] Reported-by: syzbot+ea1cd4aa4d1e98458a55@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=ea1cd4aa4d1e98458a55 Fixes: d0a22a4b03b8 ("gfs2: Fix iomap write page reclaim deadlock") Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com> Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Hongbo Li <lihongbo22@huawei.com> --- fs/gfs2/bmap.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 1e0e350b5a5a..10b0151b2789 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1115,10 +1115,18 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, goto out_unlock; break; default: - goto out_unlock; + goto out; } ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp); + if (ret) + goto out_unlock; + +out: + if (iomap->type == IOMAP_INLINE) { + iomap->private = metapath_dibh(&mp); + get_bh(iomap->private); + } out_unlock: release_metapath(&mp); @@ -1132,6 +1140,9 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length, struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); + if (iomap->private) + brelse(iomap->private); + switch (flags & (IOMAP_WRITE | IOMAP_ZERO)) { case IOMAP_WRITE: if (flags & IOMAP_DIRECT) -- 2.34.1
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://atomgit.com/openeuler/kernel/merge_requests/23189 邮件列表地址:https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/PJW... FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://atomgit.com/openeuler/kernel/merge_requests/23189 Mailing list address: https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/PJW...
participants (2)
-
Hongbo Li -
patchwork bot