[BUG] gfs2: list_del corruption of i_rwsem.wait_list during unlinkat

From: Farhad Alemi

Date: Thu May 28 2026 - 17:02:26 EST


Hello Andreas and locking/gfs2 maintainers,

I am reporting a DEBUG_LIST list_del corruption on a gfs2 unlinkat()
path found by syzkaller. The slab the kernel implicates is gfs2_inode,
but the corrupted list is the rw_semaphore's own wait_list, so I am
Cc'ing the locking maintainers as well.

Summary:
On a contended unlinkat() against an inode on a mounted gfs2 image,
do_unlinkat() takes the target inode's i_rwsem via inode_lock_nested(),
falls into rwsem_down_write_slowpath(), and reaches
rwsem_try_write_lock(). The list_del(&waiter->list) at
kernel/locking/rwsem.c:662 (on v6.17.8) trips DEBUG_LIST in
__list_del_entry_valid_or_report() at lib/list_debug.c:62. The kernel
prints:

slab gfs2_inode start ffff8881283f9aa0 pointer offset 304 size 1576
list_del corruption. prev->next should be ffffc900075ffc40, but was
ffff8881283f9bd0. (prev=ffff8881283f9bd0)

The trapping entry is the queued waiter at ffffc900075ffc40; the
prev pointer it carries names slab+304 = ffff8881283f9bd0; and the
printed prev->next equals that same address (i.e. prev == prev->next).
Since list_del at kernel/locking/rwsem.c:662 operates on a waiter
queued on the rwsem's wait_list (rwsem_waiter carries no other list
membership in v6.17), and waiters live on the stack of the blocked
task, the prev pointer being in the gfs2_inode slab identifies
slab+304 as the i_rwsem.wait_list head itself.

Observed on:
- Linux 6.17.8, x86_64, QEMU Q35
- KASAN + DEBUG_LIST enabled
- Crash context: Comm: syz.2.171, PID 4766
- Reached on the fuzzed unlinkat() path: inode_lock_nested ->
down_write_nested -> rwsem_down_write_slowpath ->
rwsem_try_write_lock -> list_del(&waiter->list)
- As the fuzzer didn't have a reproducer for this,
I have NOT verified this bug against linus/master at commit
e8c2f9fdadee (v7.1-rc4-754-ge8c2f9fdadee); the relevant
data structure was restructured in commit 1ea4b473504b
("locking/rwsem: Remove the list_head from struct rw_semaphore",
Matthew Wilcox (Oracle), 2026-03-05, first appearing in v7.1-rc1).
After that commit struct rw_semaphore no longer contains a
list_head wait_list; waiters are linked via sem->first_waiter
plus inter-waiter waiter->list.

Impact:
A user with the privileges to mount a gfs2 image and perform an
unlinkat() on a contended path trips kernel BUG, killing the kernel.
The full Oops header, register dump, and call trace are in
crash-report.txt. The trapping RIP is
__list_del_entry_valid_or_report+0x15a/0x190 (lib/list_debug.c:62);
the kernel BUG fires at lib/list_debug.c:64.

Expected behavior:
Either the rwsem's wait_list invariant (no list_del observing a
self-referencing prev while a waiter is queued) was violated, or an
overlapping lifecycle operation on the gfs2_inode placed the rwsem
into an inconsistent state. The maintainers are best placed to
identify the root cause.

Reproducer:
A standalone .syz or C reproducer was not produced for this seed;
the crash fired during automated gfs2 unlink fuzzing. The console
report is attached as crash-report.txt.

Novelty check:
I searched the syzbot dashboard's upstream open, fixed, stable, and
invalid (per-subsystem gfs2/locking/fs) namespaces, the Android
dashboard, and the marc.info linux-kernel archive, for
"__list_del_entry_valid_or_report" + "rwsem", "list_del corruption"
+ "gfs2_inode", "rwsem_try_write_lock" + "gfs2", "rwsem" +
"wait_list" + "corruption", and "corrupted list in do_unlinkat". I
did not find an exact match. The adjacent gfs2 + do_unlinkat tickets
in the invalid namespace are task-hung / soft-lockup / deadlock
variants, not DEBUG_LIST list_del trips. The adjacent
__list_del_entry_valid_or_report ticket is a KCSAN data-race in
nfc_llcp_register_device, a different code path.


I appreciate your time and consideration, and I'm grateful for your
work on this subsystem. I'd be glad to test any candidate patches.

Regards,
slab gfs2_inode start ffff8881283f9aa0 pointer offset 304 size 1576
list_del corruption. prev->next should be ffffc900075ffc40, but was ffff8881283f9bd0. (prev=ffff8881283f9bd0)
------------[ cut here ]------------
kernel BUG at lib/list_debug.c:64!
Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI
CPU: 0 UID: 0 PID: 4766 Comm: syz.2.171 Not tainted 6.17.8 #1 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:__list_del_entry_valid_or_report+0x15a/0x190 lib/list_debug.c:62
Code: e8 5b cb fe fd 43 80 3c 2c 00 74 08 4c 89 ff e8 ac 9f 1a fe 49 8b 17 48 c7 c7 a0 2a 98 86 48 89 de 4c 89 f9 e8 e7 d9 59 fd 90 <0f> 0b 4c 89 f7 e8 2c cb fe fd 43 80 3c 2c 00 74 08 4c 89 ff e8 7d
RSP: 0018:ffffc900075ffad0 EFLAGS: 00010046
RAX: 000000000000006d RBX: ffffc900075ffc40 RCX: d2eb43b776ff1b00
RDX: 0000000000000000 RSI: 0000000080000002 RDI: 0000000000000000
RBP: ffffc900075ffd10 R08: ffff888235c240d3 R09: 1ffff11046b8481a
R10: dffffc0000000000 R11: ffffed1046b8481b R12: 1ffff1102507f37a
R13: dffffc0000000000 R14: ffff8881283f9bd0 R15: ffff8881283f9bd0
FS: 00007f989d6556c0(0000) GS:ffff8882abbe8000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffe8d86df80 CR3: 000000011da46000 CR4: 0000000000750ef0
PKRU: 80000000
Call Trace:
<TASK>
__list_del_entry_valid include/linux/list.h:124 [inline]
__list_del_entry include/linux/list.h:215 [inline]
list_del include/linux/list.h:229 [inline]
rwsem_try_write_lock kernel/locking/rwsem.c:662 [inline]
rwsem_down_write_slowpath+0x94e/0x1040 kernel/locking/rwsem.c:1159
__down_write_common kernel/locking/rwsem.c:1317 [inline]
__down_write kernel/locking/rwsem.c:1326 [inline]
down_write_nested+0x1cb/0x210 kernel/locking/rwsem.c:1707
inode_lock_nested include/linux/fs.h:916 [inline]
do_unlinkat+0x1dc/0x590 fs/namei.c:4645
__do_sys_unlinkat fs/namei.c:4699 [inline]
__se_sys_unlinkat fs/namei.c:4692 [inline]
__x64_sys_unlinkat+0xd8/0xf0 fs/namei.c:4692
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xf0/0x390 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f989ec4778d
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 b0 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f989d655018 EFLAGS: 00000246 ORIG_RAX: 0000000000000107
RAX: ffffffffffffffda RBX: 00007f989eed6180 RCX: 00007f989ec4778d
RDX: 0000000000000000 RSI: 00002000000398c0 RDI: ffffffffffffff9c
RBP: 00007f989eceeb3d R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f989eed6218 R14: 00007f989eed6180 R15: 00007ffe8d86e850
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:__list_del_entry_valid_or_report+0x15a/0x190 lib/list_debug.c:62
Code: e8 5b cb fe fd 43 80 3c 2c 00 74 08 4c 89 ff e8 ac 9f 1a fe 49 8b 17 48 c7 c7 a0 2a 98 86 48 89 de 4c 89 f9 e8 e7 d9 59 fd 90 <0f> 0b 4c 89 f7 e8 2c cb fe fd 43 80 3c 2c 00 74 08 4c 89 ff e8 7d
RSP: 0018:ffffc900075ffad0 EFLAGS: 00010046
RAX: 000000000000006d RBX: ffffc900075ffc40 RCX: d2eb43b776ff1b00
RDX: 0000000000000000 RSI: 0000000080000002 RDI: 0000000000000000
RBP: ffffc900075ffd10 R08: ffff888235c240d3 R09: 1ffff11046b8481a
R10: dffffc0000000000 R11: ffffed1046b8481b R12: 1ffff1102507f37a
R13: dffffc0000000000 R14: ffff8881283f9bd0 R15: ffff8881283f9bd0
FS: 00007f989d6556c0(0000) GS:ffff8882abbe8000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffe8d86df80 CR3: 000000011da46000 CR4: 0000000000750ef0
PKRU: 80000000

<<<<<<<<<<<<<<< tail report >>>>>>>>>>>>>>>