]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ocfs2: fix boundary check in ocfs2_check_dir_entry() to use buffer offset
authorJoseph Qi <joseph.qi@linux.alibaba.com>
Fri, 10 Jul 2026 04:05:12 +0000 (12:05 +0800)
committerAndrew Morton <akpm@linux-foundation.org>
Tue, 21 Jul 2026 00:41:28 +0000 (17:41 -0700)
Commit 390ac56cf0f6 ("ocfs2: add boundary check to
ocfs2_check_dir_entry()") added an out-of-bounds guard using the
caller-supplied 'offset' argument:

if (offset > size - OCFS2_DIR_REC_LEN(1))
return 0;

However, 'offset' and 'size' are not measured against the same base for
all callers.  In the block-based lookup path, ocfs2_find_entry_el() passes
'offset' as an absolute offset into the whole directory:

i = ocfs2_search_dirblock(bh, dir, name, namelen,
  block << sb->s_blocksize_bits,
  bh->b_data, sb->s_blocksize, res_dir);

while 'size' is a single block size (sb->s_blocksize).  For any directory
entry located in the second or later block, 'offset' is >=
sb->s_blocksize, so the guard rejects every such entry even though it is
perfectly valid and lies entirely within its block buffer.

This makes mounting fail for filesystems whose system directory spans more
than one block, e.g.  a volume formatted with a small block size:

  mkfs.ocfs2 -b 512 -C 4096 -N 2 -T datafiles --fs-features=usrquota,grpquota

  ocfs2_check_dir_entry:314 ERROR: directory entry (#18: offset=512) too close to end or out-of-bounds
  ocfs2_init_local_system_inodes:496 ERROR: status=-22, sysfile=12, slot=0
  ocfs2_mount_volume:1757 ERROR: status = -22

The dirent's position within the buffer being validated is ((char *)de -
buf), which is what the rest of the function already uses (via
next_offset) and what must be bounds-checked against 'size'.  Compute that
buffer-relative offset and use it for the guard.  The subtraction is
reordered to size - buf_offset < OCFS2_DIR_REC_LEN(1) to avoid an unsigned
underflow when size is smaller than the minimal record length.

Link: https://lore.kernel.org/20260710040512.3310736-1-joseph.qi@linux.alibaba.com
Fixes: 390ac56cf0f6 ("ocfs2: add boundary check to ocfs2_check_dir_entry()")
Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com>
Reviewed-by: Dmitry Antipov <dmantipov@yandex.ru>
Tested-by: Dmitry Antipov <dmantipov@yandex.ru>
Cc: Mark Fasheh <mark@fasheh.com>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: Junxiao Bi <junxiao.bi@oracle.com>
Cc: Changwei Ge <gechangwei@live.cn>
Cc: Jun Piao <piaojun@huawei.com>
Cc: Heming Zhao <heming.zhao@suse.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
fs/ocfs2/dir.c

index 8e6b0323832783aa2414977a32575b306fc6fe6f..d7fc3cccf2f4b4e9efdabdb6cf9e0294855c0022 100644 (file)
@@ -302,10 +302,11 @@ static int ocfs2_check_dir_entry(struct inode *dir,
                                 unsigned long offset)
 {
        const char *error_msg = NULL;
+       unsigned long buf_offset = (char *)de - buf;
        unsigned long next_offset;
        int rlen;
 
-       if (offset > size - OCFS2_DIR_REC_LEN(1)) {
+       if (buf_offset > size || size - buf_offset < OCFS2_DIR_REC_LEN(1)) {
                /* Dirent is (maybe partially) beyond the buffer
                 * boundaries so touching 'de' members is unsafe.
                 */
@@ -316,7 +317,7 @@ static int ocfs2_check_dir_entry(struct inode *dir,
        }
 
        rlen = le16_to_cpu(de->rec_len);
-       next_offset = ((char *) de - buf) + rlen;
+       next_offset = buf_offset + rlen;
 
        if (unlikely(rlen < OCFS2_DIR_REC_LEN(1)))
                error_msg = "rec_len is smaller than minimal";