On Wed, Jan 15, 2025 at 10:16 PM Kun Hu <huk23@xxxxxxxxxxxxxx> wrote:
>
>
> > This is an already known issue and the relevant patch has been applied.
> > Please make sure that the following patch is applied to the kernel you tested.
> >
> > a5324b3a488d exfat: fix the infinite loop in __exfat_free_cluster()
> >
> > or try to reproduce it with linux-6.13-rc7.
>
> Hi Namjae,
Hi Kun,
>
> We still successfully reproduced it on the v6.13-rc7. Firstly, I apologize for taking up your time, I’m not sure if this is a significant issue since from the reproducer it kind of looks like it’s caused via fault injection.
>
>
> The syz_mount_image in the syscall reproducer mounts a randomly generated image and also has the potential to trigger an abnormal path to the file system. Specifically, the . /file0 file is crafted to contain invalid FAT table or bitmap information, it is possible to cause abnormal cyclic behavior in __exfat_free_cluster.
>
> Because p_chain->size is artificially constructed, if it has a large value, then exfat_clear_bitmap will be called frequently. As the call stack shows, the program eventually deadlocks in the loop in __exfat_free_cluster.
>
> This link is a link to our crash log in the rc7 kernel tree:
>
> Link: https://github.com/pghk13/Kernel-Bug/blob/main/0103_6.13rc5_%E6%9C%AA%E6%8A%A5%E5%91%8A/%E6%9C%89%E7%9B%B8%E4%BC%BC%E6%A3%80%E7%B4%A2%E8%AE%B0%E5%BD%95/39-BUG_%20soft%20lockup%20in%20sys_unlink/crashlog0115_rc7.txt
>
> As I said earlier, I'm still consistently reporting the crash I found to you guys now because I'm not sure if this issue is useful to you. If it is not useful, please ignore it. I hope it doesn't take up too much of your time.
Can you check an attached patch ?
Thanks.
>
> ———
> Kun Hu
>
>
>
>
> > This is an already known issue and the relevant patch has been applied.
> > Please make sure that the following patch is applied to the kernel you tested.
> >
> > a5324b3a488d exfat: fix the infinite loop in __exfat_free_cluster()
> >
> > or try to reproduce it with linux-6.13-rc7.
>
> Hi Namjae,
Hi Kun,
>
> We still successfully reproduced it on the v6.13-rc7. Firstly, I apologize for taking up your time, I’m not sure if this is a significant issue since from the reproducer it kind of looks like it’s caused via fault injection.
>
>
> The syz_mount_image in the syscall reproducer mounts a randomly generated image and also has the potential to trigger an abnormal path to the file system. Specifically, the . /file0 file is crafted to contain invalid FAT table or bitmap information, it is possible to cause abnormal cyclic behavior in __exfat_free_cluster.
>
> Because p_chain->size is artificially constructed, if it has a large value, then exfat_clear_bitmap will be called frequently. As the call stack shows, the program eventually deadlocks in the loop in __exfat_free_cluster.
>
> This link is a link to our crash log in the rc7 kernel tree:
>
> Link: https://github.com/pghk13/Kernel-Bug/blob/main/0103_6.13rc5_%E6%9C%AA%E6%8A%A5%E5%91%8A/%E6%9C%89%E7%9B%B8%E4%BC%BC%E6%A3%80%E7%B4%A2%E8%AE%B0%E5%BD%95/39-BUG_%20soft%20lockup%20in%20sys_unlink/crashlog0115_rc7.txt
>
> As I said earlier, I'm still consistently reporting the crash I found to you guys now because I'm not sure if this issue is useful to you. If it is not useful, please ignore it. I hope it doesn't take up too much of your time.
Can you check an attached patch ?
Thanks.
>
> ———
> Kun Hu
>
>
From 0bb26ac6aa65c9d9d41f19af305fb72c480fd1d6 Mon Sep 17 00:00:00 2001 From: Namjae Jeon <linkinjeon@kernel.org> Date: Wed, 22 Jan 2025 00:24:31 +0900 Subject: [PATCH] exfat: fix infinite loop Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> --- fs/exfat/balloc.c | 10 ++++++++-- fs/exfat/exfat_fs.h | 2 +- fs/exfat/fatent.c | 8 +++++--- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/fs/exfat/balloc.c b/fs/exfat/balloc.c index ce9be95c9172..9ff825f1502d 100644 --- a/fs/exfat/balloc.c +++ b/fs/exfat/balloc.c @@ -141,7 +141,7 @@ int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync) return 0; } -void exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync) +int exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync) { int i, b; unsigned int ent_idx; @@ -150,13 +150,17 @@ void exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync) struct exfat_mount_options *opts = &sbi->options; if (!is_valid_cluster(sbi, clu)) - return; + return -EIO; ent_idx = CLUSTER_TO_BITMAP_ENT(clu); i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx); b = BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent_idx); + if (!test_bit_le(b, sbi->vol_amap[i]->b_data)) + return -EIO; + clear_bit_le(b, sbi->vol_amap[i]->b_data); + exfat_update_bh(sbi->vol_amap[i], sync); if (opts->discard) { @@ -171,6 +175,8 @@ void exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync) opts->discard = 0; } } + + return 0; } /* diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index 78be6964a8a0..d30ce18a88b7 100644 --- a/fs/exfat/exfat_fs.h +++ b/fs/exfat/exfat_fs.h @@ -456,7 +456,7 @@ int exfat_count_num_clusters(struct super_block *sb, int exfat_load_bitmap(struct super_block *sb); void exfat_free_bitmap(struct exfat_sb_info *sbi); int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync); -void exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync); +int exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync); unsigned int exfat_find_free_bitmap(struct super_block *sb, unsigned int clu); int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count); int exfat_trim_fs(struct inode *inode, struct fstrim_range *range); diff --git a/fs/exfat/fatent.c b/fs/exfat/fatent.c index 9e5492ac409b..8d78b3030575 100644 --- a/fs/exfat/fatent.c +++ b/fs/exfat/fatent.c @@ -175,6 +175,7 @@ static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain BITMAP_OFFSET_SECTOR_INDEX(sb, CLUSTER_TO_BITMAP_ENT(clu)); if (p_chain->flags == ALLOC_NO_FAT_CHAIN) { + int err; unsigned int last_cluster = p_chain->dir + p_chain->size - 1; do { bool sync = false; @@ -189,7 +190,9 @@ static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain cur_cmap_i = next_cmap_i; } - exfat_clear_bitmap(inode, clu, (sync && IS_DIRSYNC(inode))); + err = exfat_clear_bitmap(inode, clu, (sync && IS_DIRSYNC(inode))); + if (err) + break; clu++; num_clusters++; } while (num_clusters < p_chain->size); @@ -215,7 +218,7 @@ static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain num_clusters++; if (err) - goto dec_used_clus; + break; if (num_clusters >= sbi->num_clusters - EXFAT_FIRST_CLUSTER) { /* @@ -229,7 +232,6 @@ static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain } while (clu != EXFAT_EOF_CLUSTER); } -dec_used_clus: sbi->used_clusters -= num_clusters; return 0; } -- 2.25.1