+++ /dev/null
-From d5e206778e96e8667d3bde695ad372c296dc9353 Mon Sep 17 00:00:00 2001
-From: "Acs, Jakub" <acsjakub@amazon.de>
-Date: Thu, 20 Mar 2025 15:46:49 +0000
-Subject: ext4: fix OOB read when checking dotdot dir
-
-From: Acs, Jakub <acsjakub@amazon.de>
-
-commit d5e206778e96e8667d3bde695ad372c296dc9353 upstream.
-
-Mounting a corrupted filesystem with directory which contains '.' dir
-entry with rec_len == block size results in out-of-bounds read (later
-on, when the corrupted directory is removed).
-
-ext4_empty_dir() assumes every ext4 directory contains at least '.'
-and '..' as directory entries in the first data block. It first loads
-the '.' dir entry, performs sanity checks by calling ext4_check_dir_entry()
-and then uses its rec_len member to compute the location of '..' dir
-entry (in ext4_next_entry). It assumes the '..' dir entry fits into the
-same data block.
-
-If the rec_len of '.' is precisely one block (4KB), it slips through the
-sanity checks (it is considered the last directory entry in the data
-block) and leaves "struct ext4_dir_entry_2 *de" point exactly past the
-memory slot allocated to the data block. The following call to
-ext4_check_dir_entry() on new value of de then dereferences this pointer
-which results in out-of-bounds mem access.
-
-Fix this by extending __ext4_check_dir_entry() to check for '.' dir
-entries that reach the end of data block. Make sure to ignore the phony
-dir entries for checksum (by checking name_len for non-zero).
-
-Note: This is reported by KASAN as use-after-free in case another
-structure was recently freed from the slot past the bound, but it is
-really an OOB read.
-
-This issue was found by syzkaller tool.
-
-Call Trace:
-[ 38.594108] BUG: KASAN: slab-use-after-free in __ext4_check_dir_entry+0x67e/0x710
-[ 38.594649] Read of size 2 at addr ffff88802b41a004 by task syz-executor/5375
-[ 38.595158]
-[ 38.595288] CPU: 0 UID: 0 PID: 5375 Comm: syz-executor Not tainted 6.14.0-rc7 #1
-[ 38.595298] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014
-[ 38.595304] Call Trace:
-[ 38.595308] <TASK>
-[ 38.595311] dump_stack_lvl+0xa7/0xd0
-[ 38.595325] print_address_description.constprop.0+0x2c/0x3f0
-[ 38.595339] ? __ext4_check_dir_entry+0x67e/0x710
-[ 38.595349] print_report+0xaa/0x250
-[ 38.595359] ? __ext4_check_dir_entry+0x67e/0x710
-[ 38.595368] ? kasan_addr_to_slab+0x9/0x90
-[ 38.595378] kasan_report+0xab/0xe0
-[ 38.595389] ? __ext4_check_dir_entry+0x67e/0x710
-[ 38.595400] __ext4_check_dir_entry+0x67e/0x710
-[ 38.595410] ext4_empty_dir+0x465/0x990
-[ 38.595421] ? __pfx_ext4_empty_dir+0x10/0x10
-[ 38.595432] ext4_rmdir.part.0+0x29a/0xd10
-[ 38.595441] ? __dquot_initialize+0x2a7/0xbf0
-[ 38.595455] ? __pfx_ext4_rmdir.part.0+0x10/0x10
-[ 38.595464] ? __pfx___dquot_initialize+0x10/0x10
-[ 38.595478] ? down_write+0xdb/0x140
-[ 38.595487] ? __pfx_down_write+0x10/0x10
-[ 38.595497] ext4_rmdir+0xee/0x140
-[ 38.595506] vfs_rmdir+0x209/0x670
-[ 38.595517] ? lookup_one_qstr_excl+0x3b/0x190
-[ 38.595529] do_rmdir+0x363/0x3c0
-[ 38.595537] ? __pfx_do_rmdir+0x10/0x10
-[ 38.595544] ? strncpy_from_user+0x1ff/0x2e0
-[ 38.595561] __x64_sys_unlinkat+0xf0/0x130
-[ 38.595570] do_syscall_64+0x5b/0x180
-[ 38.595583] entry_SYSCALL_64_after_hwframe+0x76/0x7e
-
-Fixes: ac27a0ec112a0 ("[PATCH] ext4: initial copy of files from ext3")
-Signed-off-by: Jakub Acs <acsjakub@amazon.de>
-Cc: Theodore Ts'o <tytso@mit.edu>
-Cc: Andreas Dilger <adilger.kernel@dilger.ca>
-Cc: linux-ext4@vger.kernel.org
-Cc: linux-kernel@vger.kernel.org
-Cc: Mahmoud Adam <mngyadam@amazon.com>
-Cc: stable@vger.kernel.org
-Cc: security@kernel.org
-Link: https://patch.msgid.link/b3ae36a6794c4a01944c7d70b403db5b@amazon.de
-Signed-off-by: Theodore Ts'o <tytso@mit.edu>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- fs/ext4/dir.c | 3 +++
- 1 file changed, 3 insertions(+)
-
---- a/fs/ext4/dir.c
-+++ b/fs/ext4/dir.c
-@@ -89,6 +89,9 @@ int __ext4_check_dir_entry(const char *f
- else if (unlikely(le32_to_cpu(de->inode) >
- le32_to_cpu(EXT4_SB(dir->i_sb)->s_es->s_inodes_count)))
- error_msg = "inode out of bounds";
-+ else if (unlikely(next_offset == size && de->name_len == 1 &&
-+ de->name[0] == '.'))
-+ error_msg = "'.' directory cannot be the last in data block";
- else
- return 0;
-