ntfs3: fix boundary check in ntfs_dir_count()

ntfs_dir_emit() skips index entries whose fname does not fit in e->size,
but ntfs_dir_count() still accepted them via de_get_fname() alone.
dir_is_empty() can then disagree with readdir: a malformed directory
appears empty in ls while rmdir fails with ENOTEMPTY.

Factor the fname/key bounds check into de_fname_fits() and use it from
ntfs_dir_emit() and de_countable_fname() so count/readdir share the same
entry acceptance rules.

Signed-off-by: Xixin Liu <liuxixin@kylinos.cn>
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
This commit is contained in:
Konstantin Komarov 2026-07-02 13:03:30 +02:00
parent 2064bc663f
commit 71a25f2593
No known key found for this signature in database
GPG Key ID: A9B0331F832407B6

View File

@ -273,6 +273,12 @@ struct inode *dir_search_u(struct inode *dir, const struct cpu_str *uni,
return err == -ENOENT ? NULL : err ? ERR_PTR(err) : inode;
}
static inline bool de_fname_fits(const struct NTFS_DE *e, u32 e_size,
const struct ATTR_FILE_NAME *fname)
{
return sizeof(struct NTFS_DE) + fname_full_size(fname) <= e_size;
}
/*
* returns false if 'ctx' if full
*/
@ -305,9 +311,7 @@ static inline bool ntfs_dir_emit(struct ntfs_sb_info *sbi,
if (sbi->options->nohidden && (fname->dup.fa & FILE_ATTRIBUTE_HIDDEN))
return true;
if (sizeof(struct NTFS_DE) + offsetof(struct ATTR_FILE_NAME, name) +
fname->name_len * sizeof(short) >
le16_to_cpu(e->size))
if (!de_fname_fits(e, le16_to_cpu(e->size), fname))
return true;
name_len = ntfs_utf16_to_nls(sbi, fname->name, fname->name_len, name,
@ -576,6 +580,23 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx)
return err;
}
/*
* Return fname when @e passes the same checks as ntfs_dir_emit() before
* exposing an entry (valid key, non-DOS, fname fits in e->size).
*/
static inline const struct ATTR_FILE_NAME *
de_countable_fname(const struct NTFS_DE *e, u32 e_size)
{
const struct ATTR_FILE_NAME *fname;
fname = de_get_fname(e);
if (!fname || fname->type == FILE_NAME_DOS ||
!de_fname_fits(e, e_size, fname))
return NULL;
return fname;
}
static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs,
size_t *files)
{
@ -615,13 +636,10 @@ static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs,
if (de_is_last(e))
break;
fname = de_get_fname(e);
fname = de_countable_fname(e, e_size);
if (!fname)
continue;
if (fname->type == FILE_NAME_DOS)
continue;
if (is_empty) {
*is_empty = false;
if (!dirs && !files)