Re: [PATCH v1] exfat: add cluster chain loop check for dir

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Jun 13, 2025 at 7:39 PM Yuezhang Mo <Yuezhang.Mo@xxxxxxxx> wrote:
>
> An infinite loop may occur if the following conditions occur due to
> file system corruption.
>
> (1) Condition for exfat_count_dir_entries() to loop infinitely.
>     - The cluster chain includes a loop.
>     - There is no UNUSED entry in the cluster chain.
>
> (2) Condition for exfat_create_upcase_table() to loop infinitely.
>     - The cluster chain of the root directory includes a loop.
>     - There are no UNUSED entry and up-case table entry in the cluster
>       chain of the root directory.
>
> (3) Condition for exfat_load_bitmap() to loop infinitely.
>     - The cluster chain of the root directory includes a loop.
>     - There are no UNUSED entry and bitmap entry in the cluster chain
>       of the root directory.
>
> This commit adds checks in exfat_count_num_clusters() and
> exfat_count_dir_entries() to see if the cluster chain includes a loop,
> thus avoiding the above infinite loops.
>
> Signed-off-by: Yuezhang Mo <Yuezhang.Mo@xxxxxxxx>
> ---
>  fs/exfat/dir.c    | 33 +++++++++++++++++++++------------
>  fs/exfat/fatent.c | 10 ++++++++++
>  fs/exfat/super.c  | 32 +++++++++++++++++++++-----------
>  3 files changed, 52 insertions(+), 23 deletions(-)
>
> diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
> index 3103b932b674..467271ad4d71 100644
> --- a/fs/exfat/dir.c
> +++ b/fs/exfat/dir.c
> @@ -1194,7 +1194,8 @@ int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir)
>  {
>         int i, count = 0;
>         int dentries_per_clu;
> -       unsigned int entry_type;
> +       unsigned int entry_type = TYPE_FILE;
> +       unsigned int clu_count = 0;
>         struct exfat_chain clu;
>         struct exfat_dentry *ep;
>         struct exfat_sb_info *sbi = EXFAT_SB(sb);
> @@ -1205,18 +1206,26 @@ int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir)
>         exfat_chain_dup(&clu, p_dir);
>
>         while (clu.dir != EXFAT_EOF_CLUSTER) {
> -               for (i = 0; i < dentries_per_clu; i++) {
> -                       ep = exfat_get_dentry(sb, &clu, i, &bh);
> -                       if (!ep)
> -                               return -EIO;
> -                       entry_type = exfat_get_entry_type(ep);
> -                       brelse(bh);
> +               clu_count++;
> +               if (clu_count > sbi->used_clusters) {
                    if (++clu_count > sbi->used_clusters) {

> +                       exfat_fs_error(sb, "dir size or FAT or bitmap is corrupted");
> +                       return -EIO;
> +               }
>
> -                       if (entry_type == TYPE_UNUSED)
> -                               return count;
> -                       if (entry_type != TYPE_DIR)
> -                               continue;
> -                       count++;
> +               if (entry_type != TYPE_UNUSED) {
> +                       for (i = 0; i < dentries_per_clu; i++) {
> +                               ep = exfat_get_dentry(sb, &clu, i, &bh);
> +                               if (!ep)
> +                                       return -EIO;
> +                               entry_type = exfat_get_entry_type(ep);
> +                               brelse(bh);
> +
> +                               if (entry_type == TYPE_UNUSED)
> +                                       break;
Is there any reason why you keep doing loop even though you found an
unused entry?

> +                               if (entry_type != TYPE_DIR)
> +                                       continue;
> +                               count++;
> +                       }
>                 }
>
>                 if (clu.flags == ALLOC_NO_FAT_CHAIN) {
> diff --git a/fs/exfat/fatent.c b/fs/exfat/fatent.c
> index 23065f948ae7..2a2615ca320f 100644
> --- a/fs/exfat/fatent.c
> +++ b/fs/exfat/fatent.c
> @@ -490,5 +490,15 @@ int exfat_count_num_clusters(struct super_block *sb,
>         }
>
>         *ret_count = count;
> +
> +       /*
> +        * since exfat_count_used_clusters() is not called, sbi->used_clusters
> +        * cannot be used here.
> +        */
> +       if (i == sbi->num_clusters) {
This is also right, But to make it more clear, wouldn't it be better
to do clu != EXFAT_EOF_CLUSTER?

Thanks.
> +               exfat_fs_error(sb, "The cluster chain has a loop");
> +               return -EIO;
> +       }
> +
>         return 0;
>  }





[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [NTFS 3]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [NTFS 3]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux