From: ZhengYuan Huang <gality369@gmail.com>
To: jaegeuk@kernel.org, chao@kernel.org, cm224.lee@samsung.com
Cc: linux-f2fs-devel@lists.sourceforge.net,
linux-kernel@vger.kernel.org, baijiaju1990@gmail.com,
r33s3n6@gmail.com, zzzccc427@gmail.com,
ZhengYuan Huang <gality369@gmail.com>,
stable@vger.kernel.org
Subject: [PATCH] f2fs: reject non-directory inode in f2fs_get_parent() to prevent null-ptr-deref
Date: Wed, 18 Mar 2026 17:04:10 +0800 [thread overview]
Message-ID: <20260318090410.3368669-1-gality369@gmail.com> (raw)
[BUG]
When accessing a crafted f2fs filesystem via open_by_handle_at(2), a
KASAN null-pointer dereference is triggered deep inside the fscrypt
inline-encryption path:
KASAN: null-ptr-deref in range [0x0000000000000010-0x0000000000000017]
RIP: 0010:__fscrypt_inode_uses_inline_crypto fs/crypto/inline_crypt.c:266
RIP: 0010:fscrypt_set_bio_crypt_ctx+0x200/0x300 fs/crypto/inline_crypt.c:308
Call Trace:
f2fs_set_bio_crypt_ctx fs/f2fs/data.c:492 [inline]
f2fs_grab_read_bio+0x262/0x7d0 fs/f2fs/data.c:1056
f2fs_submit_page_read+0xb2/0x180 fs/f2fs/data.c:1095
f2fs_get_read_data_folio+0x633/0xbe0 fs/f2fs/data.c:1263
f2fs_find_data_folio+0x146/0x330 fs/f2fs/data.c:1286
find_in_level fs/f2fs/dir.c:302 [inline]
__f2fs_find_entry+0x651/0xe10 fs/f2fs/dir.c:377
f2fs_find_entry+0xc6/0x200 fs/f2fs/dir.c:418
f2fs_inode_by_name+0x2a/0x1d0 fs/f2fs/dir.c:435
f2fs_get_parent+0x9b/0x160 fs/f2fs/namei.c:451
reconnect_one fs/exportfs/expfs.c:130 [inline]
reconnect_path+0x1f6/0x8c0 fs/exportfs/expfs.c:220
exportfs_decode_fh_raw+0x3f3/0x780 fs/exportfs/expfs.c:535
do_handle_to_path fs/fhandle.c:276 [inline]
handle_to_path fs/fhandle.c:400 [inline]
do_handle_open+0x62f/0xb30 fs/fhandle.c:415
__do_sys_open_by_handle_at fs/fhandle.c:455 [inline]
__se_sys_open_by_handle_at fs/fhandle.c:446 [inline]
__x64_sys_open_by_handle_at+0x7a/0xc0 fs/fhandle.c:446
x64_sys_call+0x1cd5/0x26a0 arch/x86/include/generated/asm/syscalls_64.h:305
...
The bug is reproducible on next-20260313 with our dynamic
metadata fuzzing tool that corrupts f2fs metadata at runtime.
[CAUSE]
The export reconnect path (exportfs_decode_fh_raw -> reconnect_path ->
reconnect_one -> f2fs_get_parent) does not validate that the inode
embedded in a file handle is actually a directory before treating it
as one.
A crafted file handle can therefore supply a non-directory inode that
has IS_ENCRYPTED set and S_IFREG in i_mode. f2fs_get_parent() calls
f2fs_inode_by_name() on this inode, which eventually calls
f2fs_grab_read_bio() to read the inode's data blocks.
f2fs_grab_read_bio() calls fscrypt_set_bio_crypt_ctx(), which detects:
fscrypt_needs_contents_encryption() == true (IS_ENCRYPTED && S_ISREG)
and proceeds to call __fscrypt_inode_uses_inline_crypto(), which does:
fscrypt_get_inode_info_raw(inode)->ci_inlinecrypt
Because the inode was never opened through the normal f2fs_file_open()
path, fscrypt_file_open() was never called, so i_crypt_info is NULL.
fscrypt_get_inode_info_raw() returns NULL unconditionally (only
printing a VFS_WARN_ON_ONCE), and the subsequent dereference of
NULL->ci_inlinecrypt triggers the null-ptr-deref.
[FIX]
Add an S_ISDIR() check at the top of f2fs_get_parent(). The function
is the f2fs implementation of export_operations::get_parent, which by
contract is only supposed to receive directory dentries. Reject any
non-directory inode with -ENOTDIR before attempting to search for the
".." entry, so that a crafted file handle carrying a non-dir inode
cannot reach the fscrypt bio setup path without a properly initialised
i_crypt_info.
After this fix, the same crafted file handle will instead be rejected by
f2fs_get_parent() with -ENOTDIR, and the export reconnect path will not
proceed to call f2fs_inode_by_name() on the non-directory inode.
Fixes: 57397d86c62d ("f2fs: add inode operations for special inodes")
Cc: stable@vger.kernel.org
Signed-off-by: ZhengYuan Huang <gality369@gmail.com>
---
fs/f2fs/namei.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index b882771e4699..0e81a2124a50 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -447,8 +447,14 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *f2fs_get_parent(struct dentry *child)
{
+ struct inode *inode = d_inode(child);
struct folio *folio;
- unsigned long ino = f2fs_inode_by_name(d_inode(child), &dotdot_name, &folio);
+ unsigned long ino;
+
+ if (!S_ISDIR(inode->i_mode))
+ return ERR_PTR(-ENOTDIR);
+
+ ino = f2fs_inode_by_name(inode, &dotdot_name, &folio);
if (!ino) {
if (IS_ERR(folio))
--
2.43.0
next reply other threads:[~2026-03-18 9:04 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-18 9:04 ZhengYuan Huang [this message]
2026-03-18 10:08 ` [PATCH] f2fs: reject non-directory inode in f2fs_get_parent() to prevent null-ptr-deref Greg KH
2026-03-19 10:34 ` ZhengYuan Huang
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=20260318090410.3368669-1-gality369@gmail.com \
--to=gality369@gmail.com \
--cc=baijiaju1990@gmail.com \
--cc=chao@kernel.org \
--cc=cm224.lee@samsung.com \
--cc=jaegeuk@kernel.org \
--cc=linux-f2fs-devel@lists.sourceforge.net \
--cc=linux-kernel@vger.kernel.org \
--cc=r33s3n6@gmail.com \
--cc=stable@vger.kernel.org \
--cc=zzzccc427@gmail.com \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox