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: 1d08h, last: 1d05h
✨ 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()`: ```c /* 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()`: ```c /* 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()`: ```c /* 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: ```c /* 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.