Skip to content

Commit faddeb8

Browse files
deepanshu406Andreas Gruenbacher
authored andcommitted
gfs2: Fix use-after-free in iomap inline data write path
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: d0a22a4 ("gfs2: Fix iomap write page reclaim deadlock") Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com> Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
1 parent 22150a7 commit faddeb8

1 file changed

Lines changed: 12 additions & 1 deletion

File tree

fs/gfs2/bmap.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1127,10 +1127,18 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length,
11271127
goto out_unlock;
11281128
break;
11291129
default:
1130-
goto out_unlock;
1130+
goto out;
11311131
}
11321132

11331133
ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp);
1134+
if (ret)
1135+
goto out_unlock;
1136+
1137+
out:
1138+
if (iomap->type == IOMAP_INLINE) {
1139+
iomap->private = metapath_dibh(&mp);
1140+
get_bh(iomap->private);
1141+
}
11341142

11351143
out_unlock:
11361144
release_metapath(&mp);
@@ -1144,6 +1152,9 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length,
11441152
struct gfs2_inode *ip = GFS2_I(inode);
11451153
struct gfs2_sbd *sdp = GFS2_SB(inode);
11461154

1155+
if (iomap->private)
1156+
brelse(iomap->private);
1157+
11471158
switch (flags & (IOMAP_WRITE | IOMAP_ZERO)) {
11481159
case IOMAP_WRITE:
11491160
if (flags & IOMAP_DIRECT)

0 commit comments

Comments
 (0)