Commit 4b954598 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull exfat fixes from Namjae Jeon:

 - Fix page allocation failure from allocation bitmap by using
   kvmalloc_array/kvfree

 - Add the check to validate if filename entries exceeds max filename
   length

 - Fix potential deadlock condition from dir_emit*()

* tag 'exfat-for-6.5-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat:
  exfat: release s_lock before calling dir_emit()
  exfat: check if filename entries exceeds max filename length
  exfat: use kvmalloc_array/kvfree instead of kmalloc_array/kfree
parents 79d65ee5 ff84772f
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -69,7 +69,7 @@ static int exfat_allocate_bitmap(struct super_block *sb,
	}
	sbi->map_sectors = ((need_map_size - 1) >>
			(sb->s_blocksize_bits)) + 1;
	sbi->vol_amap = kmalloc_array(sbi->map_sectors,
	sbi->vol_amap = kvmalloc_array(sbi->map_sectors,
				sizeof(struct buffer_head *), GFP_KERNEL);
	if (!sbi->vol_amap)
		return -ENOMEM;
@@ -84,7 +84,7 @@ static int exfat_allocate_bitmap(struct super_block *sb,
			while (j < i)
				brelse(sbi->vol_amap[j++]);

			kfree(sbi->vol_amap);
			kvfree(sbi->vol_amap);
			sbi->vol_amap = NULL;
			return -EIO;
		}
@@ -138,7 +138,7 @@ void exfat_free_bitmap(struct exfat_sb_info *sbi)
	for (i = 0; i < sbi->map_sectors; i++)
		__brelse(sbi->vol_amap[i]);

	kfree(sbi->vol_amap);
	kvfree(sbi->vol_amap);
}

int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync)
+19 −17
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ static int exfat_get_uniname_from_ext_entry(struct super_block *sb,
{
	int i, err;
	struct exfat_entry_set_cache es;
	unsigned int uni_len = 0, len;

	err = exfat_get_dentry_set(&es, sb, p_dir, entry, ES_ALL_ENTRIES);
	if (err)
@@ -52,7 +53,10 @@ static int exfat_get_uniname_from_ext_entry(struct super_block *sb,
		if (exfat_get_entry_type(ep) != TYPE_EXTEND)
			break;

		exfat_extract_uni_name(ep, uniname);
		len = exfat_extract_uni_name(ep, uniname);
		uni_len += len;
		if (len != EXFAT_FILE_NAME_LEN || uni_len >= MAX_NAME_LENGTH)
			break;
		uniname += EXFAT_FILE_NAME_LEN;
	}

@@ -214,7 +218,10 @@ static void exfat_free_namebuf(struct exfat_dentry_namebuf *nb)
	exfat_init_namebuf(nb);
}

/* skip iterating emit_dots when dir is empty */
/*
 * Before calling dir_emit*(), sbi->s_lock should be released
 * because page fault can occur in dir_emit*().
 */
#define ITER_POS_FILLED_DOTS    (2)
static int exfat_iterate(struct file *file, struct dir_context *ctx)
{
@@ -229,11 +236,10 @@ static int exfat_iterate(struct file *file, struct dir_context *ctx)
	int err = 0, fake_offset = 0;

	exfat_init_namebuf(nb);
	mutex_lock(&EXFAT_SB(sb)->s_lock);

	cpos = ctx->pos;
	if (!dir_emit_dots(file, ctx))
		goto unlock;
		goto out;

	if (ctx->pos == ITER_POS_FILLED_DOTS) {
		cpos = 0;
@@ -245,16 +251,18 @@ static int exfat_iterate(struct file *file, struct dir_context *ctx)
	/* name buffer should be allocated before use */
	err = exfat_alloc_namebuf(nb);
	if (err)
		goto unlock;
		goto out;
get_new:
	mutex_lock(&EXFAT_SB(sb)->s_lock);

	if (ei->flags == ALLOC_NO_FAT_CHAIN && cpos >= i_size_read(inode))
		goto end_of_dir;

	err = exfat_readdir(inode, &cpos, &de);
	if (err) {
		/*
		 * At least we tried to read a sector.  Move cpos to next sector
		 * position (should be aligned).
		 * At least we tried to read a sector.
		 * Move cpos to next sector position (should be aligned).
		 */
		if (err == -EIO) {
			cpos += 1 << (sb->s_blocksize_bits);
@@ -277,16 +285,10 @@ static int exfat_iterate(struct file *file, struct dir_context *ctx)
		inum = iunique(sb, EXFAT_ROOT_INO);
	}

	/*
	 * Before calling dir_emit(), sb_lock should be released.
	 * Because page fault can occur in dir_emit() when the size
	 * of buffer given from user is larger than one page size.
	 */
	mutex_unlock(&EXFAT_SB(sb)->s_lock);
	if (!dir_emit(ctx, nb->lfn, strlen(nb->lfn), inum,
			(de.attr & ATTR_SUBDIR) ? DT_DIR : DT_REG))
		goto out_unlocked;
	mutex_lock(&EXFAT_SB(sb)->s_lock);
		goto out;
	ctx->pos = cpos;
	goto get_new;

@@ -294,9 +296,8 @@ static int exfat_iterate(struct file *file, struct dir_context *ctx)
	if (!cpos && fake_offset)
		cpos = ITER_POS_FILLED_DOTS;
	ctx->pos = cpos;
unlock:
	mutex_unlock(&EXFAT_SB(sb)->s_lock);
out_unlocked:
out:
	/*
	 * To improve performance, free namebuf after unlock sb_lock.
	 * If namebuf is not allocated, this function do nothing
@@ -1079,7 +1080,8 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
			if (entry_type == TYPE_EXTEND) {
				unsigned short entry_uniname[16], unichar;

				if (step != DIRENT_STEP_NAME) {
				if (step != DIRENT_STEP_NAME ||
				    name_len >= MAX_NAME_LENGTH) {
					step = DIRENT_STEP_FILE;
					continue;
				}