* [PATCH] f2fs: reject non-directory inode in f2fs_get_parent() to prevent null-ptr-deref
@ 2026-03-18 9:04 ZhengYuan Huang
2026-03-18 10:08 ` Greg KH
0 siblings, 1 reply; 3+ messages in thread
From: ZhengYuan Huang @ 2026-03-18 9:04 UTC (permalink / raw)
To: jaegeuk, chao, cm224.lee
Cc: linux-f2fs-devel, linux-kernel, baijiaju1990, r33s3n6, zzzccc427,
ZhengYuan Huang, stable
[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
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH] f2fs: reject non-directory inode in f2fs_get_parent() to prevent null-ptr-deref
2026-03-18 9:04 [PATCH] f2fs: reject non-directory inode in f2fs_get_parent() to prevent null-ptr-deref ZhengYuan Huang
@ 2026-03-18 10:08 ` Greg KH
2026-03-19 10:34 ` ZhengYuan Huang
0 siblings, 1 reply; 3+ messages in thread
From: Greg KH @ 2026-03-18 10:08 UTC (permalink / raw)
To: ZhengYuan Huang
Cc: jaegeuk, chao, cm224.lee, linux-f2fs-devel, linux-kernel,
baijiaju1990, r33s3n6, zzzccc427, stable
On Wed, Mar 18, 2026 at 05:04:10PM +0800, ZhengYuan Huang wrote:
> [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:
Does the f2fs fsck tool catch this issue when run on the corrupted
image?
> The bug is reproducible on next-20260313 with our dynamic
> metadata fuzzing tool that corrupts f2fs metadata at runtime.
That is not a valid threat model, sorry. If you can modify a filesystem
image while it is mounted, this is the least of your worries :)
thanks,
greg k-h
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] f2fs: reject non-directory inode in f2fs_get_parent() to prevent null-ptr-deref
2026-03-18 10:08 ` Greg KH
@ 2026-03-19 10:34 ` ZhengYuan Huang
0 siblings, 0 replies; 3+ messages in thread
From: ZhengYuan Huang @ 2026-03-19 10:34 UTC (permalink / raw)
To: Greg KH
Cc: jaegeuk, chao, cm224.lee, linux-f2fs-devel, linux-kernel,
baijiaju1990, r33s3n6, zzzccc427, stable
On Wed, Mar 18, 2026 at 7:39 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> Does the f2fs fsck tool catch this issue when run on the corrupted
> image?
This issue was found by our fuzzing tool. We are still minimizing the
reproducer and checking whether fsck.f2fs catches this case, so that
part is not concluded yet. I would appreciate a bit more time on that.
> That is not a valid threat model, sorry. If you can modify a filesystem
> image while it is mounted, this is the least of your worries :)
Thank you very much for taking the time to review the report and for
your reply. I really appreciate it.
I understand your point about runtime corruption not necessarily being
the primary threat model. I just wanted to provide a bit more context
on why I thought this case might still be worth reporting.
Our fuzzing tool does not aim to model an attacker with arbitrary
write access to a mounted filesystem image. What it does is apply
small metadata mutations during runtime in order to test filesystem
robustness when the kernel is exposed to inconsistent on-disk state.
I thought this might still be relevant from two angles:
1. Accidental media corruption at runtime
In practice, storage may return inconsistent metadata while mounted
due to bit flips, faulty firmware, transient I/O issues, or partial
writes. In that situation, even if the filesystem metadata is no
longer trustworthy, it still seems desirable that the kernel reject
the invalid state cleanly rather than hit a NULL dereference in a
deeper path.
2. Remote / distributed storage setups
In some cloud or distributed-storage environments, the machine serving
the backing data may be buggy or compromised, or the data may become
corrupted in transit. From the client kernel’s perspective, this is
again similar to receiving malformed metadata from the storage layer,
so I thought it might be worth ensuring that such cases do not affect
overall kernel stability.
I also noticed that f2fs already seems to contain some defensive
checks for unexpected runtime state. For example, in fs/f2fs/segment.c
there is the comment:
/*
* If checkpoints are off, we must not reuse data that
* was used in the previous checkpoint. If it was used
* before, we must track that to know how much space we
* really have.
*/
and there is also the extra __check_sit_bitmap() verification before
submission. That made me think f2fs does already try to guard against
certain forms of runtime inconsistency or corruption.
So my thought here was only that f2fs_get_parent() might want to
enforce its expected invariant a bit earlier, since
export_operations::get_parent should only operate on directory
dentries. The S_ISDIR() check seemed like a small defensive validation
that prevents the later NULL dereference.
I am still learning filesystem design and implementation, so if my
understanding above is incorrect, I would really appreciate any
correction.
Thanks again for your time.
Best regards,
ZhengYuan Huang
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-03-19 10:34 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-18 9:04 [PATCH] f2fs: reject non-directory inode in f2fs_get_parent() to prevent null-ptr-deref ZhengYuan Huang
2026-03-18 10:08 ` Greg KH
2026-03-19 10:34 ` ZhengYuan Huang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox