fs/ocfs2/move_extents.c | 5 +++++ 1 file changed, 5 insertions(+)
The extent map cache can become stale when extents are moved or
defragmented, causing subsequent operations to see outdated extent
flags. This triggers a BUG_ON in ocfs2_refcount_cal_cow_clusters().
The problem occurs when:
1. copy_file_range() creates a reflinked extent with OCFS2_EXT_REFCOUNTED
2. ioctl(FITRIM) triggers ocfs2_move_extents()
3. __ocfs2_move_extents_range() reads and caches the extent (flags=0x2)
4. ocfs2_move_extent()/ocfs2_defrag_extent() calls __ocfs2_move_extent()
which clears OCFS2_EXT_REFCOUNTED flag on disk (flags=0x0)
5. The extent map cache is not invalidated after the move
6. Later write() operations read stale cached flags (0x2) but disk has
updated flags (0x0), causing a mismatch
7. BUG_ON(!(rec->e_flags & OCFS2_EXT_REFCOUNTED)) triggers
Fix by clearing the extent map cache after each extent move/defrag
operation in __ocfs2_move_extents_range(). This ensures subsequent
operations read fresh extent data from disk.
Link: https://lore.kernel.org/all/20251009142917.517229-1-kartikey406@gmail.com/T/
Reported-by: syzbot+6fdd8fa3380730a4b22c@syzkaller.appspotmail.com
Tested-by: syzbot+6fdd8fa3380730a4b22c@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?id=2959889e1f6e216585ce522f7e8bc002b46ad9e7
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
Changes in v2:
- Fix moved to __ocfs2_move_extents_range() instead of ocfs2_refcount_cow()
- The real issue is in FITRIM/move_extents code path, not COW path
- COW path already clears cache at end of ocfs2_refcount_cow_hunk()
fs/ocfs2/move_extents.c | 5 +++++
1 file changed, 5 insertions(+)
---
fs/ocfs2/move_extents.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/fs/ocfs2/move_extents.c b/fs/ocfs2/move_extents.c
index 86f2631e6360..10923bf7c8b8 100644
--- a/fs/ocfs2/move_extents.c
+++ b/fs/ocfs2/move_extents.c
@@ -867,6 +867,11 @@ static int __ocfs2_move_extents_range(struct buffer_head *di_bh,
mlog_errno(ret);
goto out;
}
+ /*
+ * Invalidate extent cache after moving/defragging to prevent
+ * stale cached data with outdated extent flags.
+ */
+ ocfs2_extent_map_trunc(inode, cpos);
context->clusters_moved += alloc_size;
next:
--
2.43.0
On Thu, 9 Oct 2025 21:19:03 +0530 Deepanshu Kartikey <kartikey406@gmail.com> wrote: > The extent map cache can become stale when extents are moved or > defragmented, causing subsequent operations to see outdated extent > flags. This triggers a BUG_ON in ocfs2_refcount_cal_cow_clusters(). We try to avoid BUG_ONs ;) > > ... > > The problem occurs when: > > ... > > --- a/fs/ocfs2/move_extents.c > +++ b/fs/ocfs2/move_extents.c > @@ -867,6 +867,11 @@ static int __ocfs2_move_extents_range(struct buffer_head *di_bh, > mlog_errno(ret); > goto out; > } > + /* > + * Invalidate extent cache after moving/defragging to prevent > + * stale cached data with outdated extent flags. > + */ > + ocfs2_extent_map_trunc(inode, cpos); > > context->clusters_moved += alloc_size; > next: I assume we should backport this into earlier kernels, so I'll add a cc:stable to the changelog. It's very nice (but not essential) to add a Fixes: as well, so -stable maintainers know how far back in time to backport the fix.
On 2025/10/9 23:49, Deepanshu Kartikey wrote: > The extent map cache can become stale when extents are moved or > defragmented, causing subsequent operations to see outdated extent > flags. This triggers a BUG_ON in ocfs2_refcount_cal_cow_clusters(). > > The problem occurs when: > 1. copy_file_range() creates a reflinked extent with OCFS2_EXT_REFCOUNTED > 2. ioctl(FITRIM) triggers ocfs2_move_extents() > 3. __ocfs2_move_extents_range() reads and caches the extent (flags=0x2) > 4. ocfs2_move_extent()/ocfs2_defrag_extent() calls __ocfs2_move_extent() > which clears OCFS2_EXT_REFCOUNTED flag on disk (flags=0x0) > 5. The extent map cache is not invalidated after the move > 6. Later write() operations read stale cached flags (0x2) but disk has > updated flags (0x0), causing a mismatch > 7. BUG_ON(!(rec->e_flags & OCFS2_EXT_REFCOUNTED)) triggers > > Fix by clearing the extent map cache after each extent move/defrag > operation in __ocfs2_move_extents_range(). This ensures subsequent > operations read fresh extent data from disk. > > Link: https://lore.kernel.org/all/20251009142917.517229-1-kartikey406@gmail.com/T/ > Reported-by: syzbot+6fdd8fa3380730a4b22c@syzkaller.appspotmail.com > Tested-by: syzbot+6fdd8fa3380730a4b22c@syzkaller.appspotmail.com > Closes: https://syzkaller.appspot.com/bug?id=2959889e1f6e216585ce522f7e8bc002b46ad9e7 > Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com> Looks fine. Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> > --- > Changes in v2: > - Fix moved to __ocfs2_move_extents_range() instead of ocfs2_refcount_cow() > - The real issue is in FITRIM/move_extents code path, not COW path > - COW path already clears cache at end of ocfs2_refcount_cow_hunk() > > fs/ocfs2/move_extents.c | 5 +++++ > 1 file changed, 5 insertions(+) > --- > fs/ocfs2/move_extents.c | 5 +++++ > 1 file changed, 5 insertions(+) > > diff --git a/fs/ocfs2/move_extents.c b/fs/ocfs2/move_extents.c > index 86f2631e6360..10923bf7c8b8 100644 > --- a/fs/ocfs2/move_extents.c > +++ b/fs/ocfs2/move_extents.c > @@ -867,6 +867,11 @@ static int __ocfs2_move_extents_range(struct buffer_head *di_bh, > mlog_errno(ret); > goto out; > } > + /* > + * Invalidate extent cache after moving/defragging to prevent > + * stale cached data with outdated extent flags. > + */ > + ocfs2_extent_map_trunc(inode, cpos); > > context->clusters_moved += alloc_size; > next:
Hi Joseph, please check my comments in another mail. https://lore.kernel.org/all/20251009142917.517229-1-kartikey406@gmail.com/T/#m0c02c884701c12d71206d0f9aba4450879b18e0c - Heming On 10/10/25 09:21, Joseph Qi wrote: > > > On 2025/10/9 23:49, Deepanshu Kartikey wrote: >> The extent map cache can become stale when extents are moved or >> defragmented, causing subsequent operations to see outdated extent >> flags. This triggers a BUG_ON in ocfs2_refcount_cal_cow_clusters(). >> >> The problem occurs when: >> 1. copy_file_range() creates a reflinked extent with OCFS2_EXT_REFCOUNTED >> 2. ioctl(FITRIM) triggers ocfs2_move_extents() >> 3. __ocfs2_move_extents_range() reads and caches the extent (flags=0x2) >> 4. ocfs2_move_extent()/ocfs2_defrag_extent() calls __ocfs2_move_extent() >> which clears OCFS2_EXT_REFCOUNTED flag on disk (flags=0x0) >> 5. The extent map cache is not invalidated after the move >> 6. Later write() operations read stale cached flags (0x2) but disk has >> updated flags (0x0), causing a mismatch >> 7. BUG_ON(!(rec->e_flags & OCFS2_EXT_REFCOUNTED)) triggers >> >> Fix by clearing the extent map cache after each extent move/defrag >> operation in __ocfs2_move_extents_range(). This ensures subsequent >> operations read fresh extent data from disk. >> >> Link: https://lore.kernel.org/all/20251009142917.517229-1-kartikey406@gmail.com/T/ >> Reported-by: syzbot+6fdd8fa3380730a4b22c@syzkaller.appspotmail.com >> Tested-by: syzbot+6fdd8fa3380730a4b22c@syzkaller.appspotmail.com >> Closes: https://syzkaller.appspot.com/bug?id=2959889e1f6e216585ce522f7e8bc002b46ad9e7 >> Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com> > > Looks fine. > Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> > >> --- >> Changes in v2: >> - Fix moved to __ocfs2_move_extents_range() instead of ocfs2_refcount_cow() >> - The real issue is in FITRIM/move_extents code path, not COW path >> - COW path already clears cache at end of ocfs2_refcount_cow_hunk() >> >> fs/ocfs2/move_extents.c | 5 +++++ >> 1 file changed, 5 insertions(+) >> --- >> fs/ocfs2/move_extents.c | 5 +++++ >> 1 file changed, 5 insertions(+) >> >> diff --git a/fs/ocfs2/move_extents.c b/fs/ocfs2/move_extents.c >> index 86f2631e6360..10923bf7c8b8 100644 >> --- a/fs/ocfs2/move_extents.c >> +++ b/fs/ocfs2/move_extents.c >> @@ -867,6 +867,11 @@ static int __ocfs2_move_extents_range(struct buffer_head *di_bh, >> mlog_errno(ret); >> goto out; >> } >> + /* >> + * Invalidate extent cache after moving/defragging to prevent >> + * stale cached data with outdated extent flags. >> + */ >> + ocfs2_extent_map_trunc(inode, cpos); >> >> context->clusters_moved += alloc_size; >> next: > >
On Thu, Oct 9, 2025 at 8:49 AM Deepanshu Kartikey <kartikey406@gmail.com> wrote: > > The extent map cache can become stale when extents are moved or > defragmented, causing subsequent operations to see outdated extent > flags. This triggers a BUG_ON in ocfs2_refcount_cal_cow_clusters(). > > The problem occurs when: > 1. copy_file_range() creates a reflinked extent with OCFS2_EXT_REFCOUNTED > 2. ioctl(FITRIM) triggers ocfs2_move_extents() > 3. __ocfs2_move_extents_range() reads and caches the extent (flags=0x2) > 4. ocfs2_move_extent()/ocfs2_defrag_extent() calls __ocfs2_move_extent() > which clears OCFS2_EXT_REFCOUNTED flag on disk (flags=0x0) > 5. The extent map cache is not invalidated after the move > 6. Later write() operations read stale cached flags (0x2) but disk has > updated flags (0x0), causing a mismatch > 7. BUG_ON(!(rec->e_flags & OCFS2_EXT_REFCOUNTED)) triggers > > Fix by clearing the extent map cache after each extent move/defrag > operation in __ocfs2_move_extents_range(). This ensures subsequent > operations read fresh extent data from disk. > > Link: https://lore.kernel.org/all/20251009142917.517229-1-kartikey406@gmail.com/T/ > Reported-by: syzbot+6fdd8fa3380730a4b22c@syzkaller.appspotmail.com > Tested-by: syzbot+6fdd8fa3380730a4b22c@syzkaller.appspotmail.com > Closes: https://syzkaller.appspot.com/bug?id=2959889e1f6e216585ce522f7e8bc002b46ad9e7 > Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com> Reviewed-by: Mark Fasheh <mark@fasheh.com>
© 2016 - 2026 Red Hat, Inc.