linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
From: "Jianzhou Zhao" <luckd0g@163.com>
To: urezki@gmail.com, akpm@linux-foundation.org
Cc: linux-mm@kvack.org, linux-kernel@vger.kernel.org
Subject: KASAN: vmalloc-out-of-bounds Write in vfree_atomic
Date: Wed, 11 Mar 2026 16:49:23 +0800 (CST)	[thread overview]
Message-ID: <57fac928.7af6.19cdc162036.Coremail.luckd0g@163.com> (raw)



To: linkinjeon@kernel.org, sj1557.seo@samsung.com
Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [BUG] exfat: KASAN: vmalloc-out-of-bounds Write in delayed_free due to double-free of vol_utbl

Dear exFAT Maintainers,

Our custom fuzzing tool, RacePilot, has detected a vmalloc-out-of-bounds write inside the `delayed_free` function. The underlying issue is a double-free vulnerability regarding the upcase table (`vol_utbl`).

### Call Trace:
```
BUG: KASAN: vmalloc-out-of-bounds in llist_add_batch+0x15f/0x180 lib/llist.c:32
Write of size 8 at addr ffffc90005319000 by task syz.4.373/14262

Call Trace:
 <IRQ>
 ...
 kasan_report+0x96/0xd0 mm/kasan/report.c:634
 llist_add_batch+0x15f/0x180 lib/llist.c:32
 llist_add include/linux/llist.h:248 [inline]
 vfree_atomic+0x5e/0xe0 mm/vmalloc.c:3326
 vfree+0x708/0x8a0 mm/vmalloc.c:3353
 delayed_free+0x49/0xb0 fs/exfat/super.c:799
 rcu_do_batch kernel/rcu/tree.c:2568 [inline]
 ...
```

### Underlying Root Cause:

The issue occurs when `exfat_create_upcase_table()` fails to load an upcase table and subsequently fails to load the default fallback due to memory exhaustion (-ENOMEM), causing `sbi->vol_utbl` to be double-freed.

1. During `exfat_create_upcase_table()`, if `exfat_load_upcase_table()` returns an error other than `-EIO` (e.g. `-EINVAL` from a checksum mismatch), the code triggers `exfat_free_upcase_table()` to free the invalid table before jumping to `load_default`.
2. Inside `exfat_free_upcase_table()`, `kvfree(sbi->vol_utbl)` is executed, but `sbi->vol_utbl` is **not set to NULL**.
3. In `load_default:`, the execution calls `exfat_load_default_upcase_table(sb)`.
4. If the system is under memory pressure, `kvcalloc()` inside the default loader fails and returns `-ENOMEM`. Because of this failure, `sbi->vol_utbl` is not overwritten with a new pointer, leaving it holding the previously freed vmalloc address.
5. The `-ENOMEM` failure propagates up, aborting the filesystem mount. The VFS superblock teardown eventually processes to `exfat_kill_sb()`, queuing the RCU callback `delayed_free()`.
6. When `delayed_free()` asynchronously runs, it again calls `exfat_free_upcase_table(sbi)`.
7. `kvfree()` is called for a second time on the old `sbi->vol_utbl`, constituting a double free.
8. Because `delayed_free` executes in a softirq context, `kvfree()` delegates the cleanup to `vfree_atomic()`. `vfree_atomic` casts the virtual address to an `llist_node` and writes its `next` pointer. KASAN catches this illicit write operation on previously freed, unmapped vmalloc memory, emitting the `vmalloc-out-of-bounds` exception.

### Key Code Snippets:

In `fs/exfat/nls.c`, `exfat_create_upcase_table()`:
```c
            ret = exfat_load_upcase_table(sb, sector, num_sectors, ...);

            brelse(bh);
            if (ret && ret != -EIO) {
                /* free memory from exfat_load_upcase_table call */
                exfat_free_upcase_table(sbi);  // [1] Frees ->vol_utbl but leaves a dangling pointer
                goto load_default;
            }
...
load_default:
    /* load default upcase table */
    return exfat_load_default_upcase_table(sb); // [2] Fails on kvcalloc and returns -ENOMEM
```

In `fs/exfat/super.c`, inside `delayed_free()`:
```c
static void delayed_free(struct rcu_head *p)
{
    ...
    exfat_free_upcase_table(sbi); // [3] Second invocation leads to double free UAF
    exfat_free_sbi(sbi);
}
```

### Proposed Fix:

The solution is natively simple: nullify `sbi->vol_utbl` directly inside `exfat_free_upcase_table()` after freeing it. This safely converts the secondary asynchronous `kvfree` into a no-op.

```c
 void exfat_free_upcase_table(struct exfat_sb_info *sbi)
 {
 	kvfree(sbi->vol_utbl);
+	sbi->vol_utbl = NULL;
 }
```

Best regards,

The RacePilot Team

             reply	other threads:[~2026-03-11  8:49 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-11  8:49 Jianzhou Zhao [this message]
2026-03-11 10:48 ` Namjae Jeon

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=57fac928.7af6.19cdc162036.Coremail.luckd0g@163.com \
    --to=luckd0g@163.com \
    --cc=akpm@linux-foundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=urezki@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