* [PATCH 0/2] exfat: fix cluster allocation accounting and locking
@ 2026-09-05 5:49 Chi Zhiling
2026-09-05 5:49 ` [PATCH 1/2] exfat: fix used_clusters accounting during cluster allocation Chi Zhiling
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Chi Zhiling @ 2026-09-05 5:49 UTC (permalink / raw)
To: exfat, linux-kernel; +Cc: Namjae Jeon, Sungjong Seo, Yuezhang Mo, Chi Zhiling
From: Chi Zhiling <chizhiling@kylinos.cn>
Patch 1 fixes sbi->used_clusters accounting when cluster allocation
fails partway through. This can cause the counter to underflow.
The issue is caught by generic/476.
Patch 2 takes bitmap_lock before checking and allocating clusters.
Chi Zhiling (2):
exfat: fix used_clusters accounting during cluster allocation
exfat: take bitmap_lock at the start of exfat_alloc_cluster()
fs/exfat/fatent.c | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 1/2] exfat: fix used_clusters accounting during cluster allocation
2026-09-05 5:49 [PATCH 0/2] exfat: fix cluster allocation accounting and locking Chi Zhiling
@ 2026-09-05 5:49 ` Chi Zhiling
2026-09-05 5:49 ` [PATCH 2/2] exfat: take bitmap_lock at the start of exfat_alloc_cluster() Chi Zhiling
2026-09-05 9:33 ` [PATCH 0/2] exfat: fix cluster allocation accounting and locking Namjae Jeon
2 siblings, 0 replies; 7+ messages in thread
From: Chi Zhiling @ 2026-09-05 5:49 UTC (permalink / raw)
To: exfat, linux-kernel; +Cc: Namjae Jeon, Sungjong Seo, Yuezhang Mo, Chi Zhiling
From: Chi Zhiling <chizhiling@kylinos.cn>
In the current exfat_alloc_cluster(), p_chain->size is incremented as
each cluster is allocated so that the partially allocated chain can
be freed by __exfat_free_cluster() on the error path.
However, sbi->used_clusters is still updated only after all clusters
have been allocated, while __exfat_free_cluster() unconditionally
subtracts the number of freed clusters from sbi->used_clusters.
Therefore, when allocation fails partway through, the partially
allocated clusters are subtracted from sbi->used_clusters even
though they were never added to it. This corrupts the free-space
accounting and can cause sbi->used_clusters to underflow.
Increment sbi->used_clusters together with p_chain->size as each
cluster is allocated. This keeps the accounting consistent with
__exfat_free_cluster() on both the success and error paths.
This issue is caught by generic/476 with 256k cluster size:
Ran: generic/476
Failures: generic/476
Failed 1 of 1 tests
*** fsck.exfat output ***
ERROR: <path>: more clusters are allocated. truncate to N bytes
...
/dev/vdc: corrupted. directories 331, files 526
/dev/vdc: files corrupted 9, files fixed 0
*** end fsck.exfat output ***
Fixes: d5c514b6a0c0 ("exfat: fix the newly allocated clusters are not freed in error handling")
Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn>
---
fs/exfat/fatent.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/exfat/fatent.c b/fs/exfat/fatent.c
index a8b11e2ce43f..a6728c361289 100644
--- a/fs/exfat/fatent.c
+++ b/fs/exfat/fatent.c
@@ -509,13 +509,13 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
}
}
p_chain->size++;
+ sbi->used_clusters++;
last_clu = new_clu;
if (p_chain->size == num_alloc) {
done:
sbi->clu_srch_ptr = hint_clu;
- sbi->used_clusters += p_chain->size;
mutex_unlock(&sbi->bitmap_lock);
return 0;
}
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 2/2] exfat: take bitmap_lock at the start of exfat_alloc_cluster()
2026-09-05 5:49 [PATCH 0/2] exfat: fix cluster allocation accounting and locking Chi Zhiling
2026-09-05 5:49 ` [PATCH 1/2] exfat: fix used_clusters accounting during cluster allocation Chi Zhiling
@ 2026-09-05 5:49 ` Chi Zhiling
2026-09-07 3:21 ` David Timber
2026-09-05 9:33 ` [PATCH 0/2] exfat: fix cluster allocation accounting and locking Namjae Jeon
2 siblings, 1 reply; 7+ messages in thread
From: Chi Zhiling @ 2026-09-05 5:49 UTC (permalink / raw)
To: exfat, linux-kernel; +Cc: Namjae Jeon, Sungjong Seo, Yuezhang Mo, Chi Zhiling
From: Chi Zhiling <chizhiling@kylinos.cn>
exfat_alloc_cluster() checks sbi->used_clusters against the total
number of data clusters before acquiring sbi->bitmap_lock. A concurrent
allocation can update sbi->used_clusters after the check but before
the lock is acquired, making the check stale. This can allow the
allocation to proceed even though there are not enough free clusters,
causing it to fail partway through.
Acquire sbi->bitmap_lock before checking sbi->used_clusters so that
the free-space check and subsequent cluster allocation are serialized
with concurrent allocations.
Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn>
---
fs/exfat/fatent.c | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/fs/exfat/fatent.c b/fs/exfat/fatent.c
index a6728c361289..3c8bdc131f6f 100644
--- a/fs/exfat/fatent.c
+++ b/fs/exfat/fatent.c
@@ -427,19 +427,22 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ mutex_lock(&sbi->bitmap_lock);
+
total_cnt = EXFAT_DATA_CLUSTER_COUNT(sbi);
if (unlikely(total_cnt < sbi->used_clusters)) {
exfat_fs_error_ratelimit(sb,
"%s: invalid used clusters(t:%u,u:%u)\n",
__func__, total_cnt, sbi->used_clusters);
- return -EIO;
+ ret = -EIO;
+ goto unlock;
}
- if (num_alloc > total_cnt - sbi->used_clusters)
- return -ENOSPC;
-
- mutex_lock(&sbi->bitmap_lock);
+ if (num_alloc > total_cnt - sbi->used_clusters) {
+ ret = -ENOSPC;
+ goto unlock;
+ }
hint_clu = p_chain->dir;
/* find new cluster */
@@ -516,8 +519,8 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
if (p_chain->size == num_alloc) {
done:
sbi->clu_srch_ptr = hint_clu;
- mutex_unlock(&sbi->bitmap_lock);
- return 0;
+ ret = 0;
+ goto unlock;
}
hint_clu = new_clu + 1;
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 0/2] exfat: fix cluster allocation accounting and locking
2026-09-05 5:49 [PATCH 0/2] exfat: fix cluster allocation accounting and locking Chi Zhiling
2026-09-05 5:49 ` [PATCH 1/2] exfat: fix used_clusters accounting during cluster allocation Chi Zhiling
2026-09-05 5:49 ` [PATCH 2/2] exfat: take bitmap_lock at the start of exfat_alloc_cluster() Chi Zhiling
@ 2026-09-05 9:33 ` Namjae Jeon
2 siblings, 0 replies; 7+ messages in thread
From: Namjae Jeon @ 2026-09-05 9:33 UTC (permalink / raw)
To: Chi Zhiling; +Cc: exfat, linux-kernel, Sungjong Seo, Yuezhang Mo, Chi Zhiling
On Sat, Sep 5, 2026 at 2:50 PM Chi Zhiling <chizhiling@163.com> wrote:
>
> From: Chi Zhiling <chizhiling@kylinos.cn>
>
> Patch 1 fixes sbi->used_clusters accounting when cluster allocation
> fails partway through. This can cause the counter to underflow.
> The issue is caught by generic/476.
>
> Patch 2 takes bitmap_lock before checking and allocating clusters.
>
>
> Chi Zhiling (2):
> exfat: fix used_clusters accounting during cluster allocation
> exfat: take bitmap_lock at the start of exfat_alloc_cluster()
Applied them to #dev.
Thanks!
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 2/2] exfat: take bitmap_lock at the start of exfat_alloc_cluster()
2026-09-05 5:49 ` [PATCH 2/2] exfat: take bitmap_lock at the start of exfat_alloc_cluster() Chi Zhiling
@ 2026-09-07 3:21 ` David Timber
2026-09-07 8:04 ` Chi Zhiling
0 siblings, 1 reply; 7+ messages in thread
From: David Timber @ 2026-09-07 3:21 UTC (permalink / raw)
To: Chi Zhiling, exfat, linux-kernel
Cc: Namjae Jeon, Sungjong Seo, Yuezhang Mo, Chi Zhiling
On 9/5/26 05:49, Chi Zhiling wrote:
> From: Chi Zhiling <chizhiling@kylinos.cn>
>
> exfat_alloc_cluster() checks sbi->used_clusters against the total
> number of data clusters before acquiring sbi->bitmap_lock. A concurrent
> allocation can update sbi->used_clusters after the check but before
> the lock is acquired, making the check stale. This can allow the
> allocation to proceed even though there are not enough free clusters,
> causing it to fail partway through.
>
> Acquire sbi->bitmap_lock before checking sbi->used_clusters so that
> the free-space check and subsequent cluster allocation are serialized
> with concurrent allocations.
>
> Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn>
> ---
> fs/exfat/fatent.c | 17 ++++++++++-------
> 1 file changed, 10 insertions(+), 7 deletions(-)
>
> diff --git a/fs/exfat/fatent.c b/fs/exfat/fatent.c
> index a6728c361289..3c8bdc131f6f 100644
> --- a/fs/exfat/fatent.c
> +++ b/fs/exfat/fatent.c
> @@ -427,19 +427,22 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
> struct super_block *sb = inode->i_sb;
> struct exfat_sb_info *sbi = EXFAT_SB(sb);
>
> + mutex_lock(&sbi->bitmap_lock);
Speaking of which, I think we should do this as well:
diff --git a/fs/exfat/super.c b/fs/exfat/super.c
index 217d150652cf..238533982831 100644
--- a/fs/exfat/super.c
+++ b/fs/exfat/super.c
@@ -62,7 +62,9 @@ static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf)
buf->f_type = sb->s_magic;
buf->f_bsize = sbi->cluster_size;
buf->f_blocks = sbi->num_clusters - 2; /* clu 0 & 1 */
+ mutex_lock(&sbi->bitmap_lock);
buf->f_bfree = buf->f_blocks - sbi->used_clusters;
+ mutex_unlock(&sbi->bitmap_lock);
buf->f_bavail = buf->f_bfree;
buf->f_fsid = u64_to_fsid(id);
/* Unicode utf16 255 characters */
Because there's a short window of chance that stale data is returned to
userspace on NUMA systems. For example, if a shell script or a
multi-threaded process makes changes to the fs and pulls statfs() in
rapid succession, the kernel might give userspace a wrong impression
that the fs has been chnaged by other users when it's really just a
cache coherency issue.
Well, this happens all the time with CoW-based fs like btrfs and zfs.
But this is a traditional fs and people would expect generally the same
behaviour as FAT(which does the right thing by placing a lock before
counting clusters).
Davo
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 2/2] exfat: take bitmap_lock at the start of exfat_alloc_cluster()
2026-09-07 3:21 ` David Timber
@ 2026-09-07 8:04 ` Chi Zhiling
2026-09-08 0:53 ` David Timber
0 siblings, 1 reply; 7+ messages in thread
From: Chi Zhiling @ 2026-09-07 8:04 UTC (permalink / raw)
To: David Timber, exfat, linux-kernel
Cc: Namjae Jeon, Sungjong Seo, Yuezhang Mo, Chi Zhiling
Hi, David
On 9/7/26 11:21, David Timber wrote:
> On 9/5/26 05:49, Chi Zhiling wrote:
>> From: Chi Zhiling <chizhiling@kylinos.cn>
>>
>> exfat_alloc_cluster() checks sbi->used_clusters against the total
>> number of data clusters before acquiring sbi->bitmap_lock. A concurrent
>> allocation can update sbi->used_clusters after the check but before
>> the lock is acquired, making the check stale. This can allow the
>> allocation to proceed even though there are not enough free clusters,
>> causing it to fail partway through.
>>
>> Acquire sbi->bitmap_lock before checking sbi->used_clusters so that
>> the free-space check and subsequent cluster allocation are serialized
>> with concurrent allocations.
>>
>> Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn>
>> ---
>> fs/exfat/fatent.c | 17 ++++++++++-------
>> 1 file changed, 10 insertions(+), 7 deletions(-)
>>
>> diff --git a/fs/exfat/fatent.c b/fs/exfat/fatent.c
>> index a6728c361289..3c8bdc131f6f 100644
>> --- a/fs/exfat/fatent.c
>> +++ b/fs/exfat/fatent.c
>> @@ -427,19 +427,22 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
>> struct super_block *sb = inode->i_sb;
>> struct exfat_sb_info *sbi = EXFAT_SB(sb);
>>
>> + mutex_lock(&sbi->bitmap_lock);
> Speaking of which, I think we should do this as well:
>
> diff --git a/fs/exfat/super.c b/fs/exfat/super.c
> index 217d150652cf..238533982831 100644
> --- a/fs/exfat/super.c
> +++ b/fs/exfat/super.c
> @@ -62,7 +62,9 @@ static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf)
> buf->f_type = sb->s_magic;
> buf->f_bsize = sbi->cluster_size;
> buf->f_blocks = sbi->num_clusters - 2; /* clu 0 & 1 */
> + mutex_lock(&sbi->bitmap_lock);
> buf->f_bfree = buf->f_blocks - sbi->used_clusters;
> + mutex_unlock(&sbi->bitmap_lock);
> buf->f_bavail = buf->f_bfree;
> buf->f_fsid = u64_to_fsid(id);
> /* Unicode utf16 255 characters */
>
>
> Because there's a short window of chance that stale data is returned to
> userspace on NUMA systems. For example, if a shell script or a
> multi-threaded process makes changes to the fs and pulls statfs() in
> rapid succession, the kernel might give userspace a wrong impression
> that the fs has been chnaged by other users when it's really just a
> cache coherency issue.
You mean one user has changed the fs, and others see the stale value of
used_clusters?
For SMP (including NUMA), if a certain CPU changes the value of
used_clusters, then the caches of all other CPUs will be invalidated and
need to be refilled. This process is completed by the hardware, see MESI
protocol.
>
> Well, this happens all the time with CoW-based fs like btrfs and zfs.
> But this is a traditional fs and people would expect generally the same
> behaviour as FAT(which does the right thing by placing a lock before
> counting clusters).
Perhaps I didn't fully understand your meaning. What is the FAT's behavior?
Thanks,
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 2/2] exfat: take bitmap_lock at the start of exfat_alloc_cluster()
2026-09-07 8:04 ` Chi Zhiling
@ 2026-09-08 0:53 ` David Timber
0 siblings, 0 replies; 7+ messages in thread
From: David Timber @ 2026-09-08 0:53 UTC (permalink / raw)
To: Chi Zhiling, exfat, linux-kernel
Cc: Namjae Jeon, Sungjong Seo, Yuezhang Mo, Chi Zhiling
On 9/7/26 08:04, Chi Zhiling wrote:
[...]
>
> You mean one user has changed the fs, and others see the stale value
> of used_clusters?
>
> For SMP (including NUMA), if a certain CPU changes the value of
> used_clusters, then the caches of all other CPUs will be invalidated
> and need to be refilled. This process is completed by the hardware,
> see MESI protocol.
My bad for taking NUMA as an example. I was just curious as to what you
might have to say on this since the fix is kinda related to your patch.
I was just pointing out that there's a minor race condition in
exfat_statfs(). I used NUMA as an example because the delay through the
interconnect would make the cache coherency problem worse. btw, I think
the MESI protocol only covers cache. It has no control over the
execution flow of processors.
I might have an access to a real NUMA hardware that I can test my
reproducer on. I'll report back with a patch if I can actually reproduce
it. Thanks anyways.
Davo
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-09-08 0:54 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-05 5:49 [PATCH 0/2] exfat: fix cluster allocation accounting and locking Chi Zhiling
2026-09-05 5:49 ` [PATCH 1/2] exfat: fix used_clusters accounting during cluster allocation Chi Zhiling
2026-09-05 5:49 ` [PATCH 2/2] exfat: take bitmap_lock at the start of exfat_alloc_cluster() Chi Zhiling
2026-09-07 3:21 ` David Timber
2026-09-07 8:04 ` Chi Zhiling
2026-09-08 0:53 ` David Timber
2026-09-05 9:33 ` [PATCH 0/2] exfat: fix cluster allocation accounting and locking Namjae Jeon
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®