[PATCH] exfat: keep FITRIM within the requested range
From: Yang Wen
Date: Thu Aug 13 2026 - 10:29:45 EST
exfat_find_free_bitmap() searches the entire allocation bitmap and may
wrap around to its beginning. exfat_trim_fs() does not verify that the
returned cluster is still within the requested FITRIM range.
As a result, a partial FITRIM operation may discard free clusters outside
the user-specified range and report a trimmed length larger than the
requested length.
Validate each returned cluster against the requested range and stop the
search when it wraps around or passes the range end.
Signed-off-by: Yang Wen <anmuxixixi@xxxxxxxxx>
---
fs/exfat/balloc.c | 31 +++++++++++++++++++------------
1 file changed, 19 insertions(+), 12 deletions(-)
diff --git a/fs/exfat/balloc.c b/fs/exfat/balloc.c
index e66ebf899778..c0ddd522c1e1 100644
--- a/fs/exfat/balloc.c
+++ b/fs/exfat/balloc.c
@@ -340,14 +340,27 @@ int exfat_trim_fs(struct inode *inode, struct fstrim_range *range)
mutex_lock(&sbi->bitmap_lock);
trim_begin = trim_end = exfat_find_free_bitmap(sb, clu_start);
- if (trim_begin == EXFAT_EOF_CLUSTER)
+ /*
+ * exfat_find_free_bitmap() may wrap around to the beginning of
+ * the bitmap. Reject a cluster outside the requested range.
+ */
+ if (trim_begin == EXFAT_EOF_CLUSTER ||
+ trim_begin < clu_start || trim_begin > clu_end)
goto unlock;
- next_free_clu = exfat_find_free_bitmap(sb, trim_end + 1);
- if (next_free_clu == EXFAT_EOF_CLUSTER)
- goto unlock;
+ for (;;) {
+ if (trim_end >= clu_end)
+ break;
+
+ next_free_clu = exfat_find_free_bitmap(sb, trim_end + 1);
+ /*
+ * Stop if the search wrapped around or moved beyond the requested
+ * FITRIM range.
+ */
+ if (next_free_clu == EXFAT_EOF_CLUSTER ||
+ next_free_clu <= trim_end || next_free_clu > clu_end)
+ break;
- do {
if (next_free_clu == trim_end + 1) {
/* extend trim range for continuous free cluster */
trim_end++;
@@ -368,17 +381,11 @@ int exfat_trim_fs(struct inode *inode, struct fstrim_range *range)
trim_begin = trim_end = next_free_clu;
}
- if (next_free_clu >= clu_end)
- break;
-
if (fatal_signal_pending(current)) {
err = -ERESTARTSYS;
goto unlock;
}
-
- next_free_clu = exfat_find_free_bitmap(sb, next_free_clu + 1);
- } while (next_free_clu != EXFAT_EOF_CLUSTER &&
- next_free_clu > trim_end);
+ }
/* try to trim remainder */
count = trim_end - trim_begin + 1;
--
2.34.1