[merged mm-nonmm-stable] ocfs2-bound-check-dir-entries-in-the-readdir-re-validation-scan.patch removed from -mm tree

Andrew Morton <[email protected]>
Newsgroups org.kernel.vger.mm-commits
Message-ID <[email protected]>
The quilt patch titled
     Subject: ocfs2: bound-check dir entries in the readdir re-validation scan
has been removed from the -mm tree.  Its filename was
     ocfs2-bound-check-dir-entries-in-the-readdir-re-validation-scan.patch

This patch was dropped because it was merged into the mm-nonmm-stable branch
of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm

------------------------------------------------------
From: Zhan Xusheng <[email protected]>
Subject: ocfs2: bound-check dir entries in the readdir re-validation scan
Date: Tue, 11 Aug 2026 10:43:36 +0800

Patch series "ocfs2: bound-check both readdir re-validation scans", v2.


This patch (of 2):

When the inode version changed since the last readdir(),
ocfs2_dir_foreach_blk_el() re-scans the directory block from its start to
relocate the current position:

	for (i = 0; i < sb->s_blocksize && i < offset; ) {
		de = (struct ocfs2_dir_entry *)(bh->b_data + i);
		if (le16_to_cpu(de->rec_len) < OCFS2_DIR_REC_LEN(1))
			break;
		i += le16_to_cpu(de->rec_len);
	}

i walks the block on rec_len values taken from the block itself and the
only thing tested is that rec_len is not too small, so a single bogus
rec_len leaves i anywhere in the block, including its last
OCFS2_DIR_REC_LEN(1) - 1 bytes.  @offset comes from ctx->pos, which
userspace moves with lseek() on the directory fd, and decides how far the
walk gets.

Two bounds are missing, both of which ocfs2_check_dir_entry() applies for
the emit loop below.

de->rec_len sits at byte offset 8 within the entry, so dereferencing de in
that tail reads past the s_blocksize buffer.  ocfs2_check_dir_entry()
declines to look at an entry that close to the end:

	size - buf_offset < OCFS2_DIR_REC_LEN(1)

Nothing bounds i += rec_len either, so i can end up past the block.  The
emit loop that follows is guarded by offset < sb->s_blocksize and does not
run, but

	offset = i;
	ctx->pos = (ctx->pos & ~((loff_t)sb->s_blocksize - 1)) | offset;

runs first and ORs a value with bits above the block mask into ctx->pos,
corrupting the block number readdir() resumes from. 
ocfs2_check_dir_entry() rejects that as "directory entry overrun":

	next_offset = buf_offset + rlen;
	... next_offset > size

Apply both bounds.  For a consistent directory this changes nothing:
entries are at least OCFS2_DIR_REC_LEN(1) bytes and do not cross the end
of the block, so no valid entry is skipped.

Found by the sashiko review tool; fix approach suggested by Joseph Qi.

Link: https://lore.kernel.org/[email protected]
Link: https://sashiko.dev/#/patchset/[email protected]
Link: https://lore.kernel.org/[email protected]
Signed-off-by: Zhan Xusheng <[email protected]>
Suggested-by: Joseph Qi <[email protected]>
Reviewed-by: Joseph Qi <[email protected]>
Cc: Mark Fasheh <[email protected]>
Cc: Joel Becker <[email protected]>
Cc: Junxiao Bi <[email protected]>
Cc: Changwei Ge <[email protected]>
Cc: Jun Piao <[email protected]>
Cc: Heming Zhao <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
---

 fs/ocfs2/dir.c |   12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

--- a/fs/ocfs2/dir.c~ocfs2-bound-check-dir-entries-in-the-readdir-re-validation-scan
+++ a/fs/ocfs2/dir.c
@@ -1945,7 +1945,10 @@ static int ocfs2_dir_foreach_blk_el(stru
 		 * dirent right now.  Scan from the start of the block
 		 * to make sure. */
 		if (!inode_eq_iversion(inode, *f_version)) {
-			for (i = 0; i < sb->s_blocksize && i < offset; ) {
+			for (i = 0; i + OCFS2_DIR_REC_LEN(1) <= sb->s_blocksize &&
+			     i < offset;) {
+				unsigned int rec_len;
+
 				de = (struct ocfs2_dir_entry *) (bh->b_data + i);
 				/* It's too expensive to do a full
 				 * dirent test each time round this
@@ -1953,10 +1956,11 @@ static int ocfs2_dir_foreach_blk_el(stru
 				 * least that it is non-zero.  A
 				 * failure will be detected in the
 				 * dirent test below. */
-				if (le16_to_cpu(de->rec_len) <
-				    OCFS2_DIR_REC_LEN(1))
+				rec_len = le16_to_cpu(de->rec_len);
+				if (rec_len < OCFS2_DIR_REC_LEN(1) ||
+				    i + rec_len > sb->s_blocksize)
 					break;
-				i += le16_to_cpu(de->rec_len);
+				i += rec_len;
 			}
 			offset = i;
 			ctx->pos = (ctx->pos & ~((loff_t)sb->s_blocksize - 1))
_

Patches currently in -mm which might be from [email protected] are
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.