From: sashiko-bot@kernel.org
To: "Eric Biggers" <ebiggers@kernel.org>
Cc: linux-ext4@vger.kernel.org, tytso@mit.edu
Subject: Re: [PATCH 6.12] ext4: validate readdir offset before accessing dirent
Date: Wed, 28 Oct 2026 01:02:25 +0000 [thread overview]
Message-ID: <sashiko-outbox-187298@kernel.org> (raw)
In-Reply-To: <20261028003746.31943-1-ebiggers@kernel.org>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] ext4: unvalidated rescan rec_len in ext4_readdir() bypasses tail check
--
commit f6671320411a20d7f17d0b2fabcaf53f7a52ec83
Author: Yao Kai <yaokai34@huawei.com>
ext4: validate readdir offset before accessing dirent
This commit bounds the directory entry rescan loop in ext4_readdir() using the
minimum directory entry record length and validates that offset does not land
in a truncated block tail before entering the main entry-processing loop. If
offset falls into the trailing bytes at the end of the block, it logs a
directory error and advances ctx->pos to the next block.
Link: https://patch.msgid.link/20260706041313.708346-1-yaokai34@huawei.com
> diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c
> index b278b5703c197..265e544cc3482 100644
> --- a/fs/ext4/dir.c
> +++ b/fs/ext4/dir.c
[ ... ]
> @@ -234,7 +235,10 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
> * dirent right now. Scan from the start of the block
> * to make sure. */
> if (!inode_eq_iversion(inode, info->cookie)) {
> - for (i = 0; i < sb->s_blocksize && i < offset; ) {
> + for (i = 0;
> + i <= sb->s_blocksize -
> + ext4_dir_rec_len(1, has_csum ? NULL : inode) &&
> + i < offset;) {
> de = (struct ext4_dir_entry_2 *)
> (bh->b_data + i);
> /* It's too expensive to do a full
> @@ -256,6 +260,17 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
> info->cookie = inode_query_iversion(inode);
> }
>
> + if (unlikely(offset < sb->s_blocksize &&
> + offset > sb->s_blocksize -
> + ext4_dir_rec_len(1, has_csum ? NULL : inode))) {
> + EXT4_ERROR_FILE(file, bh->b_blocknr,
> + "bad entry in directory: %s - offset=%u, size=%lu",
> + "directory entry too close to block end",
> + offset, sb->s_blocksize);
> + ctx->pos = round_up(ctx->pos, sb->s_blocksize);
> + goto next_block;
> + }
[Severity: Medium]
What happens in ext4_readdir() if a corrupted de->rec_len in the rescan loop
advances i past sb->s_blocksize?
In fs/ext4/dir.c:ext4_readdir(), when !inode_eq_iversion(inode, info->cookie)
triggers a rescan (for instance after ext4_dir_llseek()), the loop only checks
the lower bound of de->rec_len before adding it to i and updating ctx->pos:
if (!inode_eq_iversion(inode, info->cookie)) {
for (i = 0;
i <= sb->s_blocksize -
ext4_dir_rec_len(1, has_csum ? NULL : inode) &&
i < offset;) {
...
if (ext4_rec_len_from_disk(de->rec_len,
sb->s_blocksize) < ext4_dir_rec_len(1,
inode))
break;
i += ext4_rec_len_from_disk(de->rec_len,
sb->s_blocksize);
}
offset = i;
ctx->pos = (ctx->pos & ~(sb->s_blocksize - 1))
| offset;
info->cookie = inode_query_iversion(inode);
}
Because de->rec_len is a 16-bit on-disk value, a corrupted entry can set
offset > sb->s_blocksize. That value is bitwise-ORed into ctx->pos, which
corrupts the upper block-number bits of ctx->pos, and info->cookie is updated
to match inode->i_version.
Then, because the new tail check requires offset < sb->s_blocksize:
if (unlikely(offset < sb->s_blocksize &&
offset > sb->s_blocksize -
ext4_dir_rec_len(1, has_csum ? NULL : inode))) {
and the subsequent main loop also checks offset < sb->s_blocksize, an
oversized offset bypasses both EXT4_ERROR_FILE() and ext4_check_dir_entry() on
the corrupted block. On the next iteration of the outer loop, ext4_readdir()
will read the block corresponding to the corrupted ctx->pos at an arbitrary
mid-block offset = ctx->pos & (sb->s_blocksize - 1) without rescanning, since
info->cookie now matches inode->i_version.
Should the tail check (or the rescan loop) also catch offset > sb->s_blocksize
before bitwise-ORing offset into ctx->pos and validating info->cookie?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261028003746.31943-1-ebiggers@kernel.org?part=1
next prev parent reply other threads:[~2026-10-28 1:02 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-28 0:37 [PATCH 6.12] ext4: validate readdir offset before accessing dirent Eric Biggers
2026-10-28 1:02 ` sashiko-bot [this message]
2026-10-28 1:13 ` Eric Biggers
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-187298@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=ebiggers@kernel.org \
--cc=linux-ext4@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=tytso@mit.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.