[PATCH AUTOSEL 6.12 2/3] fs/ntfs3: Fix case when unmarked clusters intersect with zone

From: Sasha Levin
Date: Wed Dec 04 2024 - 18:31:58 EST


From: Konstantin Komarov <almaz.alexandrovich@xxxxxxxxxxxxxxxxxxxx>

[ Upstream commit 5fc982fe7eca9d0cf7b25832450ebd4f7c8e1c36 ]

Reported-by: syzbot+7f3761b790fa41d0f3d5@xxxxxxxxxxxxxxxxxxxxxxxxx
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@xxxxxxxxxxxxxxxxxxxx>
Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>
---
fs/ntfs3/run.c | 40 ++++++++++++++++++++++++++++++----------
1 file changed, 30 insertions(+), 10 deletions(-)

diff --git a/fs/ntfs3/run.c b/fs/ntfs3/run.c
index 58e988cd80490..48566dff0dc92 100644
--- a/fs/ntfs3/run.c
+++ b/fs/ntfs3/run.c
@@ -1055,8 +1055,8 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino,
{
int ret, err;
CLST next_vcn, lcn, len;
- size_t index;
- bool ok;
+ size_t index, done;
+ bool ok, zone;
struct wnd_bitmap *wnd;

ret = run_unpack(run, sbi, ino, svcn, evcn, vcn, run_buf, run_buf_size);
@@ -1087,8 +1087,9 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino,
continue;

down_read_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS);
+ zone = max(wnd->zone_bit, lcn) < min(wnd->zone_end, lcn + len);
/* Check for free blocks. */
- ok = wnd_is_used(wnd, lcn, len);
+ ok = !zone && wnd_is_used(wnd, lcn, len);
up_read(&wnd->rw_lock);
if (ok)
continue;
@@ -1096,14 +1097,33 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino,
/* Looks like volume is corrupted. */
ntfs_set_state(sbi, NTFS_DIRTY_ERROR);

- if (down_write_trylock(&wnd->rw_lock)) {
- /* Mark all zero bits as used in range [lcn, lcn+len). */
- size_t done;
- err = wnd_set_used_safe(wnd, lcn, len, &done);
- up_write(&wnd->rw_lock);
- if (err)
- return err;
+ if (!down_write_trylock(&wnd->rw_lock))
+ continue;
+
+ if (zone) {
+ /*
+ * Range [lcn, lcn + len) intersects with zone.
+ * To avoid complex with zone just turn it off.
+ */
+ wnd_zone_set(wnd, 0, 0);
+ }
+
+ /* Mark all zero bits as used in range [lcn, lcn+len). */
+ err = wnd_set_used_safe(wnd, lcn, len, &done);
+ if (zone) {
+ /* Restore zone. Lock mft run. */
+ struct rw_semaphore *lock;
+ lock = is_mounted(sbi) ? &sbi->mft.ni->file.run_lock :
+ NULL;
+ if (lock)
+ down_read(lock);
+ ntfs_refresh_zone(sbi);
+ if (lock)
+ up_read(lock);
}
+ up_write(&wnd->rw_lock);
+ if (err)
+ return err;
}

return ret;
--
2.43.0