KASAN: user-memory-access Read in dqput

Status: upstream: reported C repro on 2025/02/24 07:19
Subsystems: jfs
Labels: prio:high
[Documentation on labels]
Reported-by: syzbot+49423de13afd058db22f@syzkaller.appspotmail.com
First crash: 625d, last: 212d
Cause bisection: the issue happens on the oldest tested release (bisect log)
Crash: KASAN: user-memory-access Read in dqput (log)
Repro: C syz .config
  
Fix bisection: failed (error log, bisect log)
  
✨ AI-generated root cause summary:

In the JFS filesystem, directory B+-tree pages maintain a free slot list via header.freelist and slot next pointers. When inserting a directory entry (e.g., during jfs_create -> dtInsert), dtInsert and dtInsertEntry allocate slots from p->header.freelist without validating that slot indices are within bounds (1 <= fsi < DTROOTMAXSLOT for in-inode root pages, or < maxslot for regular pages). On a corrupted filesystem image where freelist or a slot's next pointer equals or exceeds DTROOTMAXSLOT (9), dtInsertEntry writes directory data into slot 9, which immediately follows jfs_ip->i_dtroot at byte offset 1040 in struct jfs_inode_info and overlays jfs_ip->i_dquot[0]. This corrupts the quota pointer with directory entry bytes (0x00000000007400ff), causing dqput() to crash on a wild pointer access when dquot_drop() is called during inode eviction.

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

Technical Analysis of Root Cause

1. Relevant Subsystem, Files, and Structs


2. Memory Layout of struct jfs_inode_info

In struct jfs_inode_info, the directory information union u.dir consists of:

  • struct dir_table_slot _table[12] (96 bytes, offset 656..751)
  • dtroot_t _dtroot (288 bytes, offset 752..1039)

dtroot_t consists of 9 slots of 32 bytes each (DTROOTMAXSLOT = 9). Slot 0 overlays the 32-byte header, while slots 1 through 8 hold directory entry data.

Immediately adjacent to the end of union u (byte offset 1040) is:

#ifdef CONFIG_QUOTA
	struct dquot __rcu *i_dquot[MAXQUOTAS];
#endif

i_dquot[0] is located at byte offset 1040 of struct jfs_inode_info. When directory operations operate on the in-inode root page (BT_ROOT), i_dtroot is cast to (dtpage_t *)&jfs_ip->i_dtroot. Accessing p->slot[9] corresponds to offset 752 + 9 * 32 = 1040 bytes, which overlays jfs_ip->i_dquot[0].


3. Bug Mechanism and Trigger Flow

  1. Missing Freelist Bounds Validation in dtInsert() / dtInsertEntry(): Directory B+-tree pages manage available 32-byte slots via a free list: p->header.freelist contains the head slot index, and each free slot's next field links to the next available slot index. In fs/jfs/jfs_dtree.c:

    • DT_GETPAGE() verifies p->header.nextindex, but does not validate p->header.freelist or p->header.freecnt.
    • dtInsert() only checks if (p->header.freelist == 0) return -EINVAL;. It never verifies that freelist or subsequent slot next pointers satisfy 1 <= fsi < DTROOTMAXSLOT (DTROOTMAXSLOT = 9) for BT_ROOT, or < p->header.maxslot for non-root pages.
  2. Trigger via Multi-Slot Directory Insertion: When creating a file in the directory (e.g. openat(..., "devices.list", O_CREAT) -> jfs_create() -> dtInsert()):

    • The filename "devices.list" has length 12.
    • In dtInsertEntry(), the first slot (hsi = p->header.freelist) stores the leaf header and the first 11 characters (DTLHDRDATALEN = 11, storing "devices.lis").
    • The remaining 1 character ('t') requires an additional slot. The loop executes:
      fsi = p->header.freelist;
      t = &p->slot[fsi];
      p->header.freelist = t->next;
      ...
      UniStrncpy_to_le(t->name, kname, len);
      ...
      t->next = -1;
      
    • On a corrupted disk image where freelist (or h->next) is 9, fsi evaluates to 9.
    • t = &p->slot[9] points to byte offset 1040 of struct jfs_inode_info (jfs_ip->i_dquot[0]).
    • UniStrncpy_to_le() copies the character 't' (0x0074 in little-endian UTF-16) into t->name[0].
    • t->next = -1 (0xff) and t->cnt = 0 (0x00) are written to the head of slot 9.
    • This writes the 8 bytes [0xff, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00] directly into jfs_ip->i_dquot[0], corrupting the quota pointer into 0x00000000007400ff.
  3. Crash at Inode Eviction:

    • During filesystem unmount or task exit cleanup (cleanup_mnt -> deactivate_locked_super -> generic_shutdown_super -> evict_inodes -> jfs_evict_inode), JFS calls dquot_drop(inode).
    • dquot_drop() reads i_dquot(inode) (jfs_ip->i_dquot). Expecting NULL for an uninitialized quota, it instead finds the corrupted pointer 0x00000000007400ff.
    • dquot_drop() proceeds to __dquot_drop() -> dqput_all() -> dqput(dquot).
    • dqput() attempts to read atomic_read(&dquot->dq_count). offsetof(struct dquot, dq_count) is 272 (0x110).
    • Reading 0x00000000007400ff + 0x110 = 0x000000000074020f results in a wild read of user-space memory from kernel mode, triggering KASAN's user-memory-access detection (or an unhandled page fault / GPF crash on non-debug kernels).
▶ ▼ ✨ AI Jobs (10)
ID Workflow Result Correct Bug Created Started Finished Revision
6e92d84e assessment-security DenialOfService: ✅ Exploitable: ✅ FilesystemTrigger: ✅ FuzzingArtifact: ❌ MemoryCorruption: ✅ NetworkNeedsSetup: ❌ NetworkTrigger: ❌ PeripheralTrigger: ❌ RemoteTrigger: ❌ Unprivileged: ❌ UserNamespace: ❌ VMGuestTrigger: ❌ VMHostTrigger: ❌ ❓ KASAN: user-memory-access Read in dqput 2026/11/09 06:16 2026/11/09 06:16 2026/11/09 07:05 5cecaf1f
79b66662 assessment-security 💥 KASAN: user-memory-access Read in dqput 2026/10/09 05:14 2026/10/09 05:14 2026/10/09 05:49 b2734a59
ea262126 assessment-security 💥 KASAN: user-memory-access Read in dqput 2026/09/09 04:01 2026/09/09 04:01 2026/09/09 04:28 243b72f1
008d36c7 assessment-security 💥 KASAN: user-memory-access Read in dqput 2026/08/09 22:49 2026/08/09 22:49 2026/08/09 23:06 5d63f80b
d5c83e99 assessment-security 💥 KASAN: user-memory-access Read in dqput 2026/07/09 01:49 2026/07/09 01:49 2026/07/09 02:19 93f57954
1a5d5d2e assessment-security 💥 KASAN: user-memory-access Read in dqput 2026/06/22 03:45 2026/06/22 03:45 2026/06/22 04:11 43bfcdb0
31a7383b assessment-security 💥 KASAN: user-memory-access Read in dqput 2026/06/14 02:54 2026/06/14 02:54 2026/06/14 03:24 4f06757f
7e7f989b assessment-security 💥 KASAN: user-memory-access Read in dqput 2026/06/09 18:43 2026/06/09 18:43 2026/06/09 19:12 c36c07f6
a1fd0c3b assessment-security 💥 KASAN: user-memory-access Read in dqput 2026/06/02 17:27 2026/06/02 17:27 2026/06/02 17:53 62fe1528
1dc394b4 assessment-security 💥 KASAN: user-memory-access Read in dqput 2026/05/26 04:43 2026/05/26 04:43 2026/05/26 05:17 c69befb3
▶ ▼ Discussions (1)
Title Replies (including bot) Last reply
[syzbot] [jfs?] KASAN: user-memory-access Read in dqput 0 (1) 2025/02/24 07:19
▶ ▼ Similar bugs (1)
Kernel Title Rank 🛈 Repro Cause bisect Fix bisect Count Last Reported Patched Status
linux-4.19 BUG: unable to handle kernel paging request in dqput (2) 8 syz, C error 5 1429d 1522d 0/1 upstream: reported C repro on 2022/09/11 16:38
▶ ▼ Last patch testing requests (7)
Created Duration User Patch Repo Result
2026/09/01 14:55 18m retest repro upstream report job log log
2026/07/06 10:27 32m retest repro upstream no crash log
2026/04/27 06:11 21m retest repro upstream error
2026/04/13 05:23 13m retest repro upstream report log
2026/02/09 10:17 17m retest repro upstream log
2025/12/01 09:25 14m retest repro upstream report log
2025/03/16 22:56 15m retest repro upstream report log
▶ ▼ Fix bisection attempts (9)
Created Duration User Patch Repo Result
2026/02/02 04:09 2m bisect fix upstream error job log
2026/01/03 02:02 1h57m bisect fix upstream OK (0) job log log
2025/11/12 04:29 2h37m bisect fix upstream OK (0) job log log
2025/10/06 20:52 2h03m bisect fix upstream OK (0) job log log
2025/08/28 23:23 2h08m bisect fix upstream OK (0) job log log
2025/07/25 01:47 2h11m bisect fix upstream OK (0) job log log
2025/06/22 00:09 2h01m bisect fix upstream OK (0) job log log
2025/05/21 23:11 2h00m bisect fix upstream OK (0) job log log
2025/04/18 19:14 3h29m bisect fix upstream OK (0) job log log

Sample crash report:
==================================================================
BUG: KASAN: user-memory-access in instrument_atomic_read include/linux/instrumented.h:68 [inline]
BUG: KASAN: user-memory-access in atomic_read include/linux/atomic/atomic-instrumented.h:32 [inline]
BUG: KASAN: user-memory-access in dqput+0x79/0x490 fs/quota/dquot.c:868
Read of size 4 at addr 000000000074020f by task syz-executor195/5828

CPU: 0 UID: 0 PID: 5828 Comm: syz-executor195 Not tainted 6.14.0-rc3-syzkaller-00295-g27102b38b8ca #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 02/12/2025
Call Trace:
 <TASK>
 __dump_stack lib/dump_stack.c:94 [inline]
 dump_stack_lvl+0x241/0x360 lib/dump_stack.c:120
 print_report+0xe3/0x5b0 mm/kasan/report.c:524
 kasan_report+0x143/0x180 mm/kasan/report.c:634
 kasan_check_range+0x282/0x290 mm/kasan/generic.c:189
 instrument_atomic_read include/linux/instrumented.h:68 [inline]
 atomic_read include/linux/atomic/atomic-instrumented.h:32 [inline]
 dqput+0x79/0x490 fs/quota/dquot.c:868
 dqput_all fs/quota/dquot.c:422 [inline]
 __dquot_drop+0x20b/0x420 fs/quota/dquot.c:1608
 jfs_evict_inode+0xbd/0x440 fs/jfs/inode.c:167
 evict+0x4e8/0x9a0 fs/inode.c:796
 dispose_list fs/inode.c:845 [inline]
 evict_inodes+0x6f6/0x790 fs/inode.c:899
 generic_shutdown_super+0xa0/0x2d0 fs/super.c:627
 kill_block_super+0x44/0x90 fs/super.c:1710
 deactivate_locked_super+0xc4/0x130 fs/super.c:473
 cleanup_mnt+0x41f/0x4b0 fs/namespace.c:1413
 task_work_run+0x24f/0x310 kernel/task_work.c:227
 ptrace_notify+0x2d9/0x380 kernel/signal.c:2522
 ptrace_report_syscall include/linux/ptrace.h:415 [inline]
 ptrace_report_syscall_exit include/linux/ptrace.h:477 [inline]
 syscall_exit_work+0xc7/0x1d0 kernel/entry/common.c:173
 syscall_exit_to_user_mode_prepare kernel/entry/common.c:200 [inline]
 __syscall_exit_to_user_mode_work kernel/entry/common.c:205 [inline]
 syscall_exit_to_user_mode+0x24a/0x340 kernel/entry/common.c:218
 do_syscall_64+0x100/0x230 arch/x86/entry/common.c:89
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fcd7fefe407
Code: 07 00 48 83 c4 08 5b 5d c3 66 2e 0f 1f 84 00 00 00 00 00 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 b8 a6 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 01 c3 48 c7 c2 b8 ff ff ff f7 d8 64 89 02 b8
RSP: 002b:00007ffc5f71e678 EFLAGS: 00000206 ORIG_RAX: 00000000000000a6
RAX: 0000000000000000 RBX: 0000000000000000 RCX: 00007fcd7fefe407
RDX: 0000000000000000 RSI: 0000000000000009 RDI: 00007ffc5f71e730
RBP: 00007ffc5f71e730 R08: 0000000000000000 R09: 0000000000000000
R10: 00000000ffffffff R11: 0000000000000206 R12: 00007ffc5f71f7a0
R13: 00005555914236c0 R14: 0000000000000001 R15: 431bde82d7b634db
 </TASK>
==================================================================

Crashes (6):
Time Kernel Commit Syzkaller Config Log Report Syz repro C repro VM info Assets (help?) Manager Title
2025/02/23 23:00 upstream 27102b38b8ca d34966d1 .config strace log report syz / log C [disk image] [vmlinux] [kernel image] [mounted in repro (corrupt fs)] ci2-upstream-fs KASAN: user-memory-access Read in dqput
2025/02/23 18:27 upstream 27102b38b8ca d34966d1 .config console log report info [disk image] [vmlinux] [kernel image] ci2-upstream-fs KASAN: user-memory-access Read in dqput
2025/02/23 18:26 upstream 27102b38b8ca d34966d1 .config console log report info [disk image] [vmlinux] [kernel image] ci2-upstream-fs KASAN: user-memory-access Read in dqput
2025/11/17 08:54 upstream 6a23ae0a96a6 f7988ea4 .config console log report syz / log C [disk image (non-bootable)] [vmlinux] [kernel image] [mounted in repro (corrupt fs)] ci-snapshot-upstream-root KASAN: user-memory-access Read in dqput
2025/03/02 12:37 upstream ece144f151ac c3901742 .config console log report info [disk image] [vmlinux] [kernel image] ci2-upstream-fs KASAN: wild-memory-access Read in dqput
2025/02/28 07:58 upstream 1e15510b71c9 6a8fcbc4 .config console log report info [disk image] [vmlinux] [kernel image] ci2-upstream-fs KASAN: wild-memory-access Read in dqput
* Struck through repros no longer work on HEAD.