### 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.