Description for this pull request:

- Fix a remount failure caused by differing process masks by inheriting
    the original mount options during the remount process.
  - Fix a potential divide-by-zero error and system crash in
    exfat_allocate_bitmap that occurred when the readahead count was zero.
  - Add validation for directory cluster bitmap bits to prevent directory
    and root cluster from being incorrectly zeroed out on corrupted images.
  - Clear the post-EOF page cache when extending a file to prevent stale
    mmap data from becoming visible, addressing an generic/363 failure.
  - Fix a reference count leak in exfat_find by properly releasing
    the dentry set in specific error paths.
 -----BEGIN PGP SIGNATURE-----
 
 iQJKBAABCgA0FiEE6NzKS6Uv/XAAGHgyZwv7A1FEIQgFAmkvk8kWHGxpbmtpbmpl
 b25Aa2VybmVsLm9yZwAKCRBnC/sDUUQhCHj+D/9go3EGGc/9c8AlyQfJ9eQd++cU
 BUD2DCsxH7GP5xTzMEuuLlzd3px8myZY8pgfu0dS4Aj8lwkZC4BqbV9fUniXmhTO
 OY8zxog++AwRAqITI1kja28bwQyPPUVGbFQX5N732stkatiWgWE/XtbaIzzhEfy1
 X3xtcAHRuqk5kd4mgjkpTzViqu6yZ4mrCkpbioKHUgquU2Q4n9WJd1R5Iuu3kDGF
 OVfTMk2fD3xlUvOms3LuiulRYHHr4MyXjuHIMI6iwKdk6wYCo2+qiRf5jm0QTDr8
 CkV81fSkB5XTpl3y+ssE2CCexWCaehvS2Cxs/K66TqPVIYWWR/1Uuo2gd9dRevIX
 hlIVd4AHafhKPcj9OMMLxJODqeEXifyJnzefJOzHppy/7HB622/T8lYt96LYg6Hs
 TT52QhASs2i/GarKMj30S7NKU23/bYUIDGLTB52JmNDnLwKHuIHt2LRZjaUsDc5O
 PA1Lq3mY8WAW03LrPkVIF7yx9RX4VYd/sOr1xn8xGTQBn0YM1pHBxC9n40jfiLCb
 qK3IDk7XyWbYFc6MJzohmWb0m7UFGykNyxOHWaU95T86E39umzdKKuGFQXcjDXeX
 tTmsuxH6lataQnpDEo9jOp7t+mO1e8E4yJ3H8Ce1hbgL4D4/xKcXHiQatDKBbTgR
 wXxeW8dYiWMIdUvNcg==
 =wjk2
 -----END PGP SIGNATURE-----

Merge tag 'exfat-for-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat

Pull exfat updates from Namjae Jeon:

 - Fix a remount failure caused by differing process masks by inheriting
   the original mount options during the remount process

 - Fix a potential divide-by-zero error and system crash in
   exfat_allocate_bitmap that occurred when the readahead count was zero

 - Add validation for directory cluster bitmap bits to prevent directory
   and root cluster from being incorrectly zeroed out on corrupted
   images

 - Clear the post-EOF page cache when extending a file to prevent stale
   mmap data from becoming visible, addressing an generic/363 failure

 - Fix a reference count leak in exfat_find by properly releasing the
   dentry set in specific error paths

* tag 'exfat-for-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat:
  exfat: fix remount failure in different process environments
  exfat: fix divide-by-zero in exfat_allocate_bitmap
  exfat: validate the cluster bitmap bits of directory
  exfat: zero out post-EOF page cache on file extension
  exfat: fix refcount leak in exfat_find
This commit is contained in:
Linus Torvalds 2025-12-05 15:48:09 -08:00
commit e40e023591
7 changed files with 77 additions and 24 deletions

View File

@ -106,7 +106,7 @@ static int exfat_allocate_bitmap(struct super_block *sb,
(PAGE_SHIFT - sb->s_blocksize_bits); (PAGE_SHIFT - sb->s_blocksize_bits);
for (i = 0; i < sbi->map_sectors; i++) { for (i = 0; i < sbi->map_sectors; i++) {
/* Trigger the next readahead in advance. */ /* Trigger the next readahead in advance. */
if (0 == (i % max_ra_count)) { if (max_ra_count && 0 == (i % max_ra_count)) {
blk_start_plug(&plug); blk_start_plug(&plug);
for (j = i; j < min(max_ra_count, sbi->map_sectors - i) + i; j++) for (j = i; j < min(max_ra_count, sbi->map_sectors - i) + i; j++)
sb_breadahead(sb, sector + j); sb_breadahead(sb, sector + j);
@ -183,11 +183,10 @@ void exfat_free_bitmap(struct exfat_sb_info *sbi)
kvfree(sbi->vol_amap); kvfree(sbi->vol_amap);
} }
int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync) int exfat_set_bitmap(struct super_block *sb, unsigned int clu, bool sync)
{ {
int i, b; int i, b;
unsigned int ent_idx; unsigned int ent_idx;
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb); struct exfat_sb_info *sbi = EXFAT_SB(sb);
if (!is_valid_cluster(sbi, clu)) if (!is_valid_cluster(sbi, clu))
@ -202,11 +201,10 @@ int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync)
return 0; return 0;
} }
int exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync) int exfat_clear_bitmap(struct super_block *sb, unsigned int clu, bool sync)
{ {
int i, b; int i, b;
unsigned int ent_idx; unsigned int ent_idx;
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb); struct exfat_sb_info *sbi = EXFAT_SB(sb);
if (!is_valid_cluster(sbi, clu)) if (!is_valid_cluster(sbi, clu))
@ -226,6 +224,28 @@ int exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync)
return 0; return 0;
} }
bool exfat_test_bitmap(struct super_block *sb, unsigned int clu)
{
int i, b;
unsigned int ent_idx;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
if (!sbi->vol_amap)
return true;
if (!is_valid_cluster(sbi, clu))
return false;
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 false;
return true;
}
/* /*
* If the value of "clu" is 0, it means cluster 2 which is the first cluster of * If the value of "clu" is 0, it means cluster 2 which is the first cluster of
* the cluster heap. * the cluster heap.

View File

@ -604,6 +604,11 @@ static int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir
if (ret) if (ret)
return ret; return ret;
if (!exfat_test_bitmap(sb, clu)) {
exfat_err(sb, "failed to test cluster bit(%u)", clu);
return -EIO;
}
/* byte offset in cluster */ /* byte offset in cluster */
off = EXFAT_CLU_OFFSET(off, sbi); off = EXFAT_CLU_OFFSET(off, sbi);

View File

@ -452,8 +452,9 @@ int exfat_count_num_clusters(struct super_block *sb,
/* balloc.c */ /* balloc.c */
int exfat_load_bitmap(struct super_block *sb); int exfat_load_bitmap(struct super_block *sb);
void exfat_free_bitmap(struct exfat_sb_info *sbi); void exfat_free_bitmap(struct exfat_sb_info *sbi);
int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync); int exfat_set_bitmap(struct super_block *sb, unsigned int clu, bool sync);
int exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync); int exfat_clear_bitmap(struct super_block *sb, unsigned int clu, bool sync);
bool exfat_test_bitmap(struct super_block *sb, unsigned int clu);
unsigned int exfat_find_free_bitmap(struct super_block *sb, unsigned int clu); 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_count_used_clusters(struct super_block *sb, unsigned int *ret_count);
int exfat_trim_fs(struct inode *inode, struct fstrim_range *range); int exfat_trim_fs(struct inode *inode, struct fstrim_range *range);

View File

@ -205,7 +205,7 @@ static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain
cur_cmap_i = next_cmap_i; cur_cmap_i = next_cmap_i;
} }
err = exfat_clear_bitmap(inode, clu, (sync && IS_DIRSYNC(inode))); err = exfat_clear_bitmap(sb, clu, (sync && IS_DIRSYNC(inode)));
if (err) if (err)
break; break;
clu++; clu++;
@ -233,7 +233,7 @@ static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain
cur_cmap_i = next_cmap_i; cur_cmap_i = next_cmap_i;
} }
if (exfat_clear_bitmap(inode, clu, (sync && IS_DIRSYNC(inode)))) if (exfat_clear_bitmap(sb, clu, (sync && IS_DIRSYNC(inode))))
break; break;
if (sbi->options.discard) { if (sbi->options.discard) {
@ -409,7 +409,7 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
} }
/* update allocation bitmap */ /* update allocation bitmap */
if (exfat_set_bitmap(inode, new_clu, sync_bmap)) { if (exfat_set_bitmap(sb, new_clu, sync_bmap)) {
ret = -EIO; ret = -EIO;
goto free_cluster; goto free_cluster;
} }

View File

@ -25,6 +25,8 @@ static int exfat_cont_expand(struct inode *inode, loff_t size)
struct exfat_sb_info *sbi = EXFAT_SB(sb); struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_chain clu; struct exfat_chain clu;
truncate_pagecache(inode, i_size_read(inode));
ret = inode_newsize_ok(inode, size); ret = inode_newsize_ok(inode, size);
if (ret) if (ret)
return ret; return ret;
@ -639,6 +641,9 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter)
inode_lock(inode); inode_lock(inode);
if (pos > i_size_read(inode))
truncate_pagecache(inode, i_size_read(inode));
valid_size = ei->valid_size; valid_size = ei->valid_size;
ret = generic_write_checks(iocb, iter); ret = generic_write_checks(iocb, iter);

View File

@ -645,16 +645,6 @@ static int exfat_find(struct inode *dir, const struct qstr *qname,
info->valid_size = le64_to_cpu(ep2->dentry.stream.valid_size); info->valid_size = le64_to_cpu(ep2->dentry.stream.valid_size);
info->size = le64_to_cpu(ep2->dentry.stream.size); info->size = le64_to_cpu(ep2->dentry.stream.size);
if (info->valid_size < 0) {
exfat_fs_error(sb, "data valid size is invalid(%lld)", info->valid_size);
return -EIO;
}
if (unlikely(EXFAT_B_TO_CLU_ROUND_UP(info->size, sbi) > sbi->used_clusters)) {
exfat_fs_error(sb, "data size is invalid(%lld)", info->size);
return -EIO;
}
info->start_clu = le32_to_cpu(ep2->dentry.stream.start_clu); info->start_clu = le32_to_cpu(ep2->dentry.stream.start_clu);
if (!is_valid_cluster(sbi, info->start_clu) && info->size) { if (!is_valid_cluster(sbi, info->start_clu) && info->size) {
exfat_warn(sb, "start_clu is invalid cluster(0x%x)", exfat_warn(sb, "start_clu is invalid cluster(0x%x)",
@ -692,6 +682,16 @@ static int exfat_find(struct inode *dir, const struct qstr *qname,
0); 0);
exfat_put_dentry_set(&es, false); exfat_put_dentry_set(&es, false);
if (info->valid_size < 0) {
exfat_fs_error(sb, "data valid size is invalid(%lld)", info->valid_size);
return -EIO;
}
if (unlikely(EXFAT_B_TO_CLU_ROUND_UP(info->size, sbi) > sbi->used_clusters)) {
exfat_fs_error(sb, "data size is invalid(%lld)", info->size);
return -EIO;
}
if (ei->start_clu == EXFAT_FREE_CLUSTER) { if (ei->start_clu == EXFAT_FREE_CLUSTER) {
exfat_fs_error(sb, exfat_fs_error(sb,
"non-zero size file starts with zero cluster (size : %llu, p_dir : %u, entry : 0x%08x)", "non-zero size file starts with zero cluster (size : %llu, p_dir : %u, entry : 0x%08x)",

View File

@ -629,6 +629,17 @@ static int __exfat_fill_super(struct super_block *sb,
goto free_bh; goto free_bh;
} }
if (!exfat_test_bitmap(sb, sbi->root_dir)) {
exfat_warn(sb, "failed to test first cluster bit of root dir(%u)",
sbi->root_dir);
/*
* The first cluster bit of the root directory should never
* be unset except when storage is corrupted. This bit is
* set to allow operations after mount.
*/
exfat_set_bitmap(sb, sbi->root_dir, false);
}
ret = exfat_count_used_clusters(sb, &sbi->used_clusters); ret = exfat_count_used_clusters(sb, &sbi->used_clusters);
if (ret) { if (ret) {
exfat_err(sb, "failed to scan clusters"); exfat_err(sb, "failed to scan clusters");
@ -813,10 +824,21 @@ static int exfat_init_fs_context(struct fs_context *fc)
ratelimit_state_init(&sbi->ratelimit, DEFAULT_RATELIMIT_INTERVAL, ratelimit_state_init(&sbi->ratelimit, DEFAULT_RATELIMIT_INTERVAL,
DEFAULT_RATELIMIT_BURST); DEFAULT_RATELIMIT_BURST);
if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE && fc->root) {
struct super_block *sb = fc->root->d_sb;
struct exfat_mount_options *cur_opts = &EXFAT_SB(sb)->options;
sbi->options.fs_uid = cur_opts->fs_uid;
sbi->options.fs_gid = cur_opts->fs_gid;
sbi->options.fs_fmask = cur_opts->fs_fmask;
sbi->options.fs_dmask = cur_opts->fs_dmask;
} else {
sbi->options.fs_uid = current_uid(); sbi->options.fs_uid = current_uid();
sbi->options.fs_gid = current_gid(); sbi->options.fs_gid = current_gid();
sbi->options.fs_fmask = current->fs->umask; sbi->options.fs_fmask = current->fs->umask;
sbi->options.fs_dmask = current->fs->umask; sbi->options.fs_dmask = current->fs->umask;
}
sbi->options.allow_utime = -1; sbi->options.allow_utime = -1;
sbi->options.errors = EXFAT_ERRORS_RO; sbi->options.errors = EXFAT_ERRORS_RO;
exfat_set_iocharset(&sbi->options, exfat_default_iocharset); exfat_set_iocharset(&sbi->options, exfat_default_iocharset);