memory leak in f2fs_apply_options

Status: upstream: reported syz repro on 2026/10/30 02:36
Subsystems: f2fs
Labels: prio:low
[Documentation on labels]
Reported-by: syzbot+6c000dea1a93b4f5905c@syzkaller.appspotmail.com
First crash: 2d11h, last: 2d09h
✨ AI-generated root cause summary:

In F2FS, when remounting a filesystem with an empty journalled quota option (such as 'usrjquota='), f2fs_apply_quota_options() overwrites F2FS_OPTION(sbi).s_qf_names[i] with NULL without freeing the previously allocated quota filename string. While __f2fs_remount() duplicates the existing quota file names into org_mount_opt via kstrdup() at the start of remount and frees them upon successful completion, this only frees the temporary backup copy, leaving the original allocated string orphaned. This results in a kernel heap memory leak on every remount that clears a journalled quota filename option.

▶ ▼ ✨ Full AI root cause analysis
Based on kernel commit c339f4010dea:

Root Cause Analysis

1. Functions, Structs, Fields, and Files Involved

  • File: fs/f2fs/super.c
  • Functions:
    • f2fs_apply_quota_options()
    • __f2fs_remount()
    • f2fs_unnote_qf_name()
    • f2fs_fill_super()
  • Structs & Fields:
    • struct f2fs_sb_info: sbi->mount_opt.s_qf_names[MAXQUOTAS] (F2FS_OPTION(sbi).s_qf_names)
    • struct f2fs_mount_info: org_mount_opt.s_qf_names[MAXQUOTAS]
    • struct f2fs_fs_context: ctx->qname_mask, ctx->info.s_qf_names[MAXQUOTAS] (F2FS_CTX_INFO(ctx).s_qf_names)

2. Trigger Mechanism and Lifecycle Invariant Violation

During initial mount with journalled quota specified (e.g., usrjquota=f2fs), the option parser notes the quota filename, and f2fs_apply_quota_options() (called via f2fs_fill_super() -> f2fs_apply_options()) allocates an active copy of the filename string via kstrdup():

/* fs/f2fs/super.c:1660-1665 */
qname = F2FS_CTX_INFO(ctx).s_qf_names[i];
if (qname) {
    qname = kstrdup(F2FS_CTX_INFO(ctx).s_qf_names[i], GFP_KERNEL | __GFP_NOFAIL);
    set_opt(sbi, QUOTA);
}
F2FS_OPTION(sbi).s_qf_names[i] = qname;

This string pointer (F2FS_OPTION(sbi).s_qf_names[i]) is held by the superblock info and expected to be freed either on unmount (f2fs_put_super()) or when the option is updated/cleared.

When userspace subsequently issues a remount clearing the quota filename (e.g., mount(..., MS_REMOUNT, "usrjquota="), as seen in the reproducer):

  1. In f2fs_parse_param(), f2fs_unnote_qf_name() is invoked, which sets ctx->info.s_qf_names[USRQUOTA] = NULL and records the change in ctx->qname_mask |= (1 << USRQUOTA).
  2. In __f2fs_remount(), backup options are prepared in org_mount_opt. Crucially, __f2fs_remount() allocates a new duplicate of the existing filename into org_mount_opt.s_qf_names[i] via kstrdup():
    /* fs/f2fs/super.c:2872-2876 */
    for (i = 0; i < MAXQUOTAS; i++) {
        if (F2FS_OPTION(sbi).s_qf_names[i]) {
            org_mount_opt.s_qf_names[i] =
                kstrdup(F2FS_OPTION(sbi).s_qf_names[i], GFP_KERNEL);
            ...
    
  3. Then f2fs_apply_options() calls f2fs_apply_quota_options():
    /* fs/f2fs/super.c:1655-1666 */
    for (i = 0; i < MAXQUOTAS; i++) {
        if (!(ctx->qname_mask & (1 << i)))
            continue;
    
        qname = F2FS_CTX_INFO(ctx).s_qf_names[i];
        if (qname) {
            qname = kstrdup(F2FS_CTX_INFO(ctx).s_qf_names[i],
                    GFP_KERNEL | __GFP_NOFAIL);
            set_opt(sbi, QUOTA);
        }
        F2FS_OPTION(sbi).s_qf_names[i] = qname;
    }
    
    Because qname is NULL, F2FS_OPTION(sbi).s_qf_names[i] is overwritten with NULL without freeing the old pointer in F2FS_OPTION(sbi).s_qf_names[i].
  4. When __f2fs_remount() finishes successfully:
    /* fs/f2fs/super.c:3100-3101 */
    for (i = 0; i < MAXQUOTAS; i++)
        kfree(org_mount_opt.s_qf_names[i]);
    
    __f2fs_remount() frees org_mount_opt.s_qf_names[i], which is the backup copy allocated at step 2.
  5. The original string allocated during initial mount at f2fs_apply_quota_options() is now completely unreferenced by any kernel pointer and is permanently leaked.

3. Runtime Consequences in a Production Kernel

  • Memory Corruption (false): The defect is an omitted kfree(). Pointers are dropped without being freed; there is no use-after-free, double free, wild pointer dereference, or out-of-bounds access.
  • Information Leak (none): The orphaned heap buffer is not exposed or copied to userspace.
  • Raw Denial of Service (true): Repeatedly mounting and remounting filesystems with cleared quota options permanently leaks slab memory in the kernel (size-8 or larger kmalloc cache), constituting a kernel resource/memory leak DoS.
  • Networking / KVM Candidate (false): The issue is entirely confined to the F2FS filesystem mount option reconfiguration logic.
▶ ▼ ✨ AI Jobs (1)
ID Workflow Result Correct Bug Created Started Finished Revision
6b39e801 assessment-security DenialOfService: ✅ Exploitable: ❌ FilesystemTrigger: ❌ FuzzingArtifact: ❌ MemoryCorruption: ❌ NetworkNeedsSetup: ❌ NetworkTrigger: ❌ PeripheralTrigger: ❌ RemoteTrigger: ❌ Unprivileged: ❌ UserNamespace: ❌ VMGuestTrigger: ❌ VMHostTrigger: ❌ ❓ memory leak in f2fs_apply_options 2026/10/29 06:33 2026/10/29 06:33 2026/10/29 07:31 b1d00f76
▶ ▼ Discussions (2)
Title Replies (including bot) Last reply
[PATCH 1/2] f2fs: fix use-after-free of quota file names on failed remount 2 (2) 2026/10/30 07:24
[syzbot] [f2fs?] memory leak in f2fs_apply_options 0 (1) 2026/10/30 02:36

Sample crash report:
BUG: memory leak
unreferenced object 0xffff888128b26a10 (size 8):
  comm "syz.0.17", pid 5977, jiffies 4294944034
  hex dump (first 8 bytes):
    66 32 66 73 00 00 00 00                          f2fs....
  backtrace (crc 49a76b1f):
    kmemleak_alloc_recursive include/linux/kmemleak.h:44 [inline]
    slab_post_alloc_hook mm/slub.c:4733 [inline]
    slab_alloc_node mm/slub.c:5033 [inline]
    __do_kmalloc_node mm/slub.c:5530 [inline]
    __kmalloc_node_track_caller_noprof+0x3e0/0x530 mm/slub.c:5662
    __kmemdup_nul mm/util.c:64 [inline]
    kstrdup+0x3c/0x80 mm/util.c:84
    f2fs_apply_quota_options fs/f2fs/super.c:1661 [inline]
    f2fs_apply_options+0x99c/0xb70 fs/f2fs/super.c:1804
    f2fs_fill_super+0xfc4/0x43f0 fs/f2fs/super.c:5288
    get_tree_bdev_flags+0x1c0/0x290 fs/super.c:1883
    vfs_get_tree+0x30/0x120 fs/super.c:1943
    fc_mount fs/namespace.c:1282 [inline]
    do_new_mount_fc fs/namespace.c:3910 [inline]
    do_new_mount fs/namespace.c:3986 [inline]
    path_mount+0x5a9/0x1580 fs/namespace.c:4337
    do_mount fs/namespace.c:4350 [inline]
    __do_sys_mount fs/namespace.c:4565 [inline]
    __se_sys_mount fs/namespace.c:4542 [inline]
    __x64_sys_mount+0x1a3/0x1e0 fs/namespace.c:4542
    do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
    do_syscall_64+0xe9/0x530 arch/x86/entry/syscall_64.c:84
    entry_SYSCALL_64_after_hwframe+0x77/0x7f

connection error: failed to recv *flatrpc.ExecutorMessageRawT: EOF

Crashes (3):
Time Kernel Commit Syzkaller Config Log Report Syz repro C repro VM info Assets (help?) Manager Title
2026/10/29 09:04 upstream c339f4010dea b1d00f76 .config console log report syz / log [disk image] [vmlinux] [kernel image] [mounted in repro (corrupt fs)] ci-upstream-gce-leak memory leak in f2fs_apply_options
2026/10/29 07:52 upstream c339f4010dea b1d00f76 .config console log report syz / log [disk image] [vmlinux] [kernel image] [mounted in repro (corrupt fs)] ci-upstream-gce-leak memory leak in f2fs_apply_options
2026/10/29 06:33 upstream c339f4010dea b1d00f76 .config console log report syz / log [disk image] [vmlinux] [kernel image] [mounted in repro (corrupt fs)] ci-upstream-gce-leak memory leak in f2fs_apply_options
* Struck through repros no longer work on HEAD.