| From afb35d5bee9bc3ca54568961199f3c9cab91ab01 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 5 Feb 2026 15:52:57 +0100 |
| Subject: gfs2: fiemap page fault fix |
| |
| From: Andreas Gruenbacher <agruenba@redhat.com> |
| |
| [ Upstream commit e411d74cc5ba290f85d0dd5e4d1df8f1d6d975d2 ] |
| |
| In gfs2_fiemap(), we are calling iomap_fiemap() while holding the inode |
| glock. This can lead to recursive glock taking if the fiemap buffer is |
| memory mapped to the same inode and accessing it triggers a page fault. |
| |
| Fix by disabling page faults for iomap_fiemap() and faulting in the |
| buffer by hand if necessary. |
| |
| Fixes xfstest generic/742. |
| |
| Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| fs/gfs2/inode.c | 16 ++++++++++++++++ |
| 1 file changed, 16 insertions(+) |
| |
| diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c |
| index b65444bc1b5f2..45040622d316e 100644 |
| --- a/fs/gfs2/inode.c |
| +++ b/fs/gfs2/inode.c |
| @@ -2105,6 +2105,14 @@ static int gfs2_getattr(struct mnt_idmap *idmap, |
| return 0; |
| } |
| |
| +static bool fault_in_fiemap(struct fiemap_extent_info *fi) |
| +{ |
| + struct fiemap_extent __user *dest = fi->fi_extents_start; |
| + size_t size = sizeof(*dest) * fi->fi_extents_max; |
| + |
| + return fault_in_safe_writeable((char __user *)dest, size) == 0; |
| +} |
| + |
| static int gfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, |
| u64 start, u64 len) |
| { |
| @@ -2114,14 +2122,22 @@ static int gfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, |
| |
| inode_lock_shared(inode); |
| |
| +retry: |
| ret = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, 0, &gh); |
| if (ret) |
| goto out; |
| |
| + pagefault_disable(); |
| ret = iomap_fiemap(inode, fieinfo, start, len, &gfs2_iomap_ops); |
| + pagefault_enable(); |
| |
| gfs2_glock_dq_uninit(&gh); |
| |
| + if (ret == -EFAULT && fault_in_fiemap(fieinfo)) { |
| + fieinfo->fi_extents_mapped = 0; |
| + goto retry; |
| + } |
| + |
| out: |
| inode_unlock_shared(inode); |
| return ret; |
| -- |
| 2.51.0 |
| |