mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-06 01:49:46 +00:00
exfat: add cluster chain loop check for dir
[ Upstream commit 99f9a97dce ]
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.
(4) Condition for exfat_find_dir_entry() to loop infinitely.
- The cluster chain includes a loop.
- The unused directory entries were exhausted by some operation.
(5) Condition for exfat_check_dir_empty() to loop infinitely.
- The cluster chain includes a loop.
- The unused directory entries were exhausted by some operation.
- All files and sub-directories under the directory are deleted.
This commit adds checks to break the above infinite loop.
Signed-off-by: Yuezhang Mo <Yuezhang.Mo@sony.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
ab0a2713fd
commit
aa8fe7b7b7
@@ -1015,6 +1015,7 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
|
||||
struct exfat_hint_femp candi_empty;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
int num_entries = exfat_calc_num_entries(p_uniname);
|
||||
unsigned int clu_count = 0;
|
||||
|
||||
if (num_entries < 0)
|
||||
return num_entries;
|
||||
@@ -1152,6 +1153,10 @@ rewind:
|
||||
} else {
|
||||
if (exfat_get_next_cluster(sb, &clu.dir))
|
||||
return -EIO;
|
||||
|
||||
/* break if the cluster chain includes a loop */
|
||||
if (unlikely(++clu_count > EXFAT_DATA_CLUSTER_COUNT(sbi)))
|
||||
goto not_found;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1214,6 +1219,7 @@ 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 clu_count = 0;
|
||||
struct exfat_chain clu;
|
||||
struct exfat_dentry *ep;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
@@ -1246,6 +1252,12 @@ int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir)
|
||||
} else {
|
||||
if (exfat_get_next_cluster(sb, &(clu.dir)))
|
||||
return -EIO;
|
||||
|
||||
if (unlikely(++clu_count > sbi->used_clusters)) {
|
||||
exfat_fs_error(sb, "FAT or bitmap is corrupted");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -461,5 +461,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 (unlikely(i == sbi->num_clusters && clu != EXFAT_EOF_CLUSTER)) {
|
||||
exfat_fs_error(sb, "The cluster chain has a loop");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -888,6 +888,7 @@ static int exfat_check_dir_empty(struct super_block *sb,
|
||||
{
|
||||
int i, dentries_per_clu;
|
||||
unsigned int type;
|
||||
unsigned int clu_count = 0;
|
||||
struct exfat_chain clu;
|
||||
struct exfat_dentry *ep;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
@@ -924,6 +925,10 @@ static int exfat_check_dir_empty(struct super_block *sb,
|
||||
} else {
|
||||
if (exfat_get_next_cluster(sb, &(clu.dir)))
|
||||
return -EIO;
|
||||
|
||||
/* break if the cluster chain includes a loop */
|
||||
if (unlikely(++clu_count > EXFAT_DATA_CLUSTER_COUNT(sbi)))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -370,13 +370,12 @@ static void exfat_hash_init(struct super_block *sb)
|
||||
INIT_HLIST_HEAD(&sbi->inode_hashtable[i]);
|
||||
}
|
||||
|
||||
static int exfat_read_root(struct inode *inode)
|
||||
static int exfat_read_root(struct inode *inode, struct exfat_chain *root_clu)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
struct exfat_inode_info *ei = EXFAT_I(inode);
|
||||
struct exfat_chain cdir;
|
||||
int num_subdirs, num_clu = 0;
|
||||
int num_subdirs;
|
||||
|
||||
exfat_chain_set(&ei->dir, sbi->root_dir, 0, ALLOC_FAT_CHAIN);
|
||||
ei->entry = -1;
|
||||
@@ -389,12 +388,9 @@ static int exfat_read_root(struct inode *inode)
|
||||
ei->hint_stat.clu = sbi->root_dir;
|
||||
ei->hint_femp.eidx = EXFAT_HINT_NONE;
|
||||
|
||||
exfat_chain_set(&cdir, sbi->root_dir, 0, ALLOC_FAT_CHAIN);
|
||||
if (exfat_count_num_clusters(sb, &cdir, &num_clu))
|
||||
return -EIO;
|
||||
i_size_write(inode, num_clu << sbi->cluster_size_bits);
|
||||
i_size_write(inode, EXFAT_CLU_TO_B(root_clu->size, sbi));
|
||||
|
||||
num_subdirs = exfat_count_dir_entries(sb, &cdir);
|
||||
num_subdirs = exfat_count_dir_entries(sb, root_clu);
|
||||
if (num_subdirs < 0)
|
||||
return -EIO;
|
||||
set_nlink(inode, num_subdirs + EXFAT_MIN_SUBDIR);
|
||||
@@ -608,7 +604,8 @@ static int exfat_verify_boot_region(struct super_block *sb)
|
||||
}
|
||||
|
||||
/* mount the file system volume */
|
||||
static int __exfat_fill_super(struct super_block *sb)
|
||||
static int __exfat_fill_super(struct super_block *sb,
|
||||
struct exfat_chain *root_clu)
|
||||
{
|
||||
int ret;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
@@ -625,6 +622,18 @@ static int __exfat_fill_super(struct super_block *sb)
|
||||
goto free_bh;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call exfat_count_num_cluster() before searching for up-case and
|
||||
* bitmap directory entries to avoid infinite loop if they are missing
|
||||
* and the cluster chain includes a loop.
|
||||
*/
|
||||
exfat_chain_set(root_clu, sbi->root_dir, 0, ALLOC_FAT_CHAIN);
|
||||
ret = exfat_count_num_clusters(sb, root_clu, &root_clu->size);
|
||||
if (ret) {
|
||||
exfat_err(sb, "failed to count the number of clusters in root");
|
||||
goto free_bh;
|
||||
}
|
||||
|
||||
ret = exfat_create_upcase_table(sb);
|
||||
if (ret) {
|
||||
exfat_err(sb, "failed to load upcase table");
|
||||
@@ -657,6 +666,7 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
struct exfat_sb_info *sbi = sb->s_fs_info;
|
||||
struct exfat_mount_options *opts = &sbi->options;
|
||||
struct inode *root_inode;
|
||||
struct exfat_chain root_clu;
|
||||
int err;
|
||||
|
||||
if (opts->allow_utime == (unsigned short)-1)
|
||||
@@ -675,7 +685,7 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
sb->s_time_min = EXFAT_MIN_TIMESTAMP_SECS;
|
||||
sb->s_time_max = EXFAT_MAX_TIMESTAMP_SECS;
|
||||
|
||||
err = __exfat_fill_super(sb);
|
||||
err = __exfat_fill_super(sb, &root_clu);
|
||||
if (err) {
|
||||
exfat_err(sb, "failed to recognize exfat type");
|
||||
goto check_nls_io;
|
||||
@@ -710,7 +720,7 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
|
||||
root_inode->i_ino = EXFAT_ROOT_INO;
|
||||
inode_set_iversion(root_inode, 1);
|
||||
err = exfat_read_root(root_inode);
|
||||
err = exfat_read_root(root_inode, &root_clu);
|
||||
if (err) {
|
||||
exfat_err(sb, "failed to initialize root inode");
|
||||
goto put_inode;
|
||||
|
||||
Reference in New Issue
Block a user