* [syzbot] [udf?] WARNING in udf_free_blocks (3)
@ 2026-06-20 3:58 syzbot
2026-06-21 2:28 ` Forwarded: [PATCH] udf: avoid recursive s_alloc_mutex deadlock when freeing AED blocks syzbot
0 siblings, 1 reply; 3+ messages in thread
From: syzbot @ 2026-06-20 3:58 UTC (permalink / raw)
To: jack, linux-kernel, syzkaller-bugs
Hello,
syzbot found the following issue on:
HEAD commit: e771677c937d Merge tag 'for-linus-iommufd' of git://git.ke..
git tree: upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=135bffec580000
kernel config: https://syzkaller.appspot.com/x/.config?x=d4ababa576f9d171
dashboard link: https://syzkaller.appspot.com/bug?extid=6a680377e13041c19d50
compiler: Debian clang version 22.1.6 (++20260514074242+fc4aad7b5db3-1~exp1~20260514074407.73), Debian LLD 22.1.6
syz repro: https://syzkaller.appspot.com/x/repro.syz?x=10bcccfe580000
C reproducer: https://syzkaller.appspot.com/x/repro.c?x=14cfe3a1580000
Downloadable assets:
disk image: https://storage.googleapis.com/syzbot-assets/24dc401a7e7b/disk-e771677c.raw.xz
vmlinux: https://storage.googleapis.com/syzbot-assets/d8e85f151384/vmlinux-e771677c.xz
kernel image: https://storage.googleapis.com/syzbot-assets/7072bfe66fc6/bzImage-e771677c.xz
mounted in repro #1: https://storage.googleapis.com/syzbot-assets/a7804ba81935/mount_0.gz
mounted in repro #2: https://storage.googleapis.com/syzbot-assets/c40d18e63806/mount_8.gz
fsck result: failed (log: https://syzkaller.appspot.com/x/fsck.log?x=15645b7a580000)
IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+6a680377e13041c19d50@syzkaller.appspotmail.com
------------[ cut here ]------------
rtmutex deadlock detected
WARNING: kernel/locking/rtmutex.c:1698 at rt_mutex_handle_deadlock+0x21/0xb0 kernel/locking/rtmutex.c:1698, CPU#1: syz.1.71/6167
Modules linked in:
CPU: 1 UID: 0 PID: 6167 Comm: syz.1.71 Not tainted syzkaller #0 PREEMPT_{RT,(full)}
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/09/2026
RIP: 0010:rt_mutex_handle_deadlock+0x21/0xb0 kernel/locking/rtmutex.c:1698
Code: 90 90 90 90 90 90 90 90 90 41 57 41 56 41 55 41 54 53 83 ff dd 0f 85 81 00 00 00 48 89 f7 e8 e6 3f 01 00 48 8d 3d 2f 77 6c 04 <67> 48 0f b9 3a 4c 8d 3d 00 00 00 00 65 48 8b 1d 43 1f 4c 07 4c 8d
RSP: 0018:ffffc900047be8d0 EFLAGS: 00010286
RAX: 0000000080000000 RBX: 00000000ffffffdd RCX: 0000000000000000
RDX: 0000000000000000 RSI: ffffffff8ba7bb00 RDI: ffffffff8f8c97e0
RBP: ffffc900047bea80 R08: ffffffff8f892ef7 R09: 1ffffffff1f125de
R10: dffffc0000000000 R11: fffffbfff1f125df R12: 1ffff920008f7d28
R13: ffffffff8b201af2 R14: ffff88804d827110 R15: dffffc0000000000
FS: 00007fc2fb7de6c0(0000) GS:ffff88812625c000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f17bfbcda08 CR3: 000000002c6ba000 CR4: 00000000003526f0
Call Trace:
<TASK>
__rt_mutex_slowlock kernel/locking/rtmutex.c:1760 [inline]
__rt_mutex_slowlock_locked kernel/locking/rtmutex.c:1787 [inline]
rt_mutex_slowlock+0x73c/0x780 kernel/locking/rtmutex.c:1827
__rt_mutex_lock kernel/locking/rtmutex.c:1842 [inline]
__mutex_lock_common kernel/locking/rtmutex_api.c:560 [inline]
mutex_lock_nested+0x168/0x1d0 kernel/locking/rtmutex_api.c:578
udf_table_free_blocks fs/udf/balloc.c:376 [inline]
udf_free_blocks+0xa8c/0x1900 fs/udf/balloc.c:678
udf_delete_aext+0x4f5/0xc00 fs/udf/inode.c:2381
udf_table_prealloc_blocks fs/udf/balloc.c:544 [inline]
udf_prealloc_blocks+0xbd4/0x10e0 fs/udf/balloc.c:702
udf_prealloc_extents fs/udf/inode.c:1058 [inline]
inode_getblk fs/udf/inode.c:916 [inline]
udf_map_block+0x1e85/0x4280 fs/udf/inode.c:453
__udf_get_block+0x52/0x250 fs/udf/inode.c:467
__block_write_begin_int+0x6c2/0x1900 fs/buffer.c:2123
block_write_begin+0x8d/0x120 fs/buffer.c:2234
udf_write_begin+0x11a/0x270 fs/udf/inode.c:261
generic_perform_write+0x2ad/0x8b0 mm/filemap.c:4336
__generic_file_write_iter+0x1b1/0x240 mm/filemap.c:4450
udf_file_write_iter+0x2a6/0x630 fs/udf/file.c:112
iter_file_splice_write+0xa36/0x1240 fs/splice.c:736
do_splice_from fs/splice.c:936 [inline]
direct_splice_actor+0x104/0x160 fs/splice.c:1159
splice_direct_to_actor+0x586/0xcc0 fs/splice.c:1103
do_splice_direct_actor fs/splice.c:1202 [inline]
do_splice_direct+0x19b/0x2a0 fs/splice.c:1228
do_sendfile+0x540/0x7e0 fs/read_write.c:1371
__do_sys_sendfile64 fs/read_write.c:1432 [inline]
__se_sys_sendfile64+0x144/0x1a0 fs/read_write.c:1418
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fc2fc17ce59
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 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 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007fc2fb7de028 EFLAGS: 00000246 ORIG_RAX: 0000000000000028
RAX: ffffffffffffffda RBX: 00007fc2fc3f5fa0 RCX: 00007fc2fc17ce59
RDX: 0000000000000000 RSI: 0000000000000004 RDI: 0000000000000004
RBP: 00007fc2fc212e6f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000800000009 R11: 0000000000000246 R12: 0000000000000000
R13: 00007fc2fc3f6038 R14: 00007fc2fc3f5fa0 R15: 00007ffe10d9c6e8
</TASK>
----------------
Code disassembly (best guess):
0: 90 nop
1: 90 nop
2: 90 nop
3: 90 nop
4: 90 nop
5: 90 nop
6: 90 nop
7: 90 nop
8: 90 nop
9: 41 57 push %r15
b: 41 56 push %r14
d: 41 55 push %r13
f: 41 54 push %r12
11: 53 push %rbx
12: 83 ff dd cmp $0xffffffdd,%edi
15: 0f 85 81 00 00 00 jne 0x9c
1b: 48 89 f7 mov %rsi,%rdi
1e: e8 e6 3f 01 00 call 0x14009
23: 48 8d 3d 2f 77 6c 04 lea 0x46c772f(%rip),%rdi # 0x46c7759
* 2a: 67 48 0f b9 3a ud1 (%edx),%rdi <-- trapping instruction
2f: 4c 8d 3d 00 00 00 00 lea 0x0(%rip),%r15 # 0x36
36: 65 48 8b 1d 43 1f 4c mov %gs:0x74c1f43(%rip),%rbx # 0x74c1f81
3d: 07
3e: 4c rex.WR
3f: 8d .byte 0x8d
---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@googlegroups.com.
syzbot will keep track of this issue. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
If the report is already addressed, let syzbot know by replying with:
#syz fix: exact-commit-title
If you want syzbot to run the reproducer, reply with:
#syz test: git://repo/address.git branch-or-commit-hash
If you attach or paste a git patch, syzbot will apply it before testing.
If you want to overwrite report's subsystems, reply with:
#syz set subsystems: new-subsystem
(See the list of subsystem names on the web dashboard)
If the report is a duplicate of another one, reply with:
#syz dup: exact-subject-of-another-report
If you want to undo deduplication, reply with:
#syz undup
^ permalink raw reply [flat|nested] 3+ messages in thread
* Forwarded: [PATCH] udf: avoid recursive s_alloc_mutex deadlock when freeing AED blocks
2026-06-20 3:58 [syzbot] [udf?] WARNING in udf_free_blocks (3) syzbot
@ 2026-06-21 2:28 ` syzbot
0 siblings, 0 replies; 3+ messages in thread
From: syzbot @ 2026-06-21 2:28 UTC (permalink / raw)
To: linux-kernel, syzkaller-bugs
For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.
***
Subject: [PATCH] udf: avoid recursive s_alloc_mutex deadlock when freeing AED blocks
Author: kartikey406@gmail.com
#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master
udf_table_prealloc_blocks() and udf_table_new_block() call
udf_delete_aext() on the unallocated-space-table inode while holding
sbi->s_alloc_mutex. When the deleted allocation descriptor empties an
allocation-extent (AED) block, udf_delete_aext() returns that block to
free space via udf_free_blocks(). For a table-managed partition that
path is udf_free_blocks() -> udf_table_free_blocks() ->
mutex_lock(&sbi->s_alloc_mutex), i.e. the task tries to acquire a mutex
it already holds.
On a PREEMPT_RT kernel the rtmutex deadlock detector reports this as
-EDEADLK:
rtmutex deadlock detected
WARNING: kernel/locking/rtmutex.c:1698 at rt_mutex_handle_deadlock
udf_table_free_blocks fs/udf/balloc.c:376 [inline]
udf_free_blocks+0xa8c/0x1900 fs/udf/balloc.c:678
udf_delete_aext+0x4f5/0xc00 fs/udf/inode.c:2381
udf_table_prealloc_blocks fs/udf/balloc.c:544 [inline]
udf_prealloc_blocks+0xbd4/0x10e0 fs/udf/balloc.c:702
On a non-RT kernel the same path is a hard self-deadlock (or a lockdep
recursive-locking splat). It is reachable from a crafted UDF image via
the write/sendfile path.
The allocator already refuses to recurse on the add side: see the
comment in udf_table_free_blocks() explaining why it must not call
udf_add_aext() while holding s_alloc_mutex. Apply the same rule to the
delete side. Let udf_delete_aext() report the AED block it would
otherwise free through a new out-parameter, and have the two callers
that hold s_alloc_mutex free it after dropping the lock. The block is
already unlinked from the inode's descriptor chain by then, so nothing
can reference it in the meantime. All other callers pass NULL and keep
freeing the block inline, exactly as before.
The out-parameter is pre-initialised with the reserved partition number
0xFFFF, which can never name a real block, so the caller can tell
whether a block was handed back.
Reported-by: syzbot+6a680377e13041c19d50@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=6a680377e13041c19d50
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
fs/udf/balloc.c | 12 ++++++++++--
fs/udf/inode.c | 19 ++++++++++++++++---
fs/udf/truncate.c | 2 +-
fs/udf/udfdecl.h | 3 ++-
4 files changed, 29 insertions(+), 7 deletions(-)
diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c
index cc6dc6e1d84d..30cec5600149 100644
--- a/fs/udf/balloc.c
+++ b/fs/udf/balloc.c
@@ -502,6 +502,8 @@ static int udf_table_prealloc_blocks(struct super_block *sb,
int8_t etype = -1;
struct udf_inode_info *iinfo;
int ret = 0;
+ /* AED block freed by udf_delete_aext(), released after unlock */
+ struct kernel_lb_addr freed = { .partitionReferenceNum = 0xFFFF };
if (first_block >= sbi->s_partmaps[partition].s_partition_len)
return 0;
@@ -541,7 +543,7 @@ static int udf_table_prealloc_blocks(struct super_block *sb,
udf_write_aext(table, &epos, &eloc,
(etype << 30) | elen, 1);
} else
- udf_delete_aext(table, epos);
+ udf_delete_aext(table, epos, &freed);
} else {
alloc_count = 0;
}
@@ -552,6 +554,8 @@ static int udf_table_prealloc_blocks(struct super_block *sb,
if (alloc_count)
udf_add_free_space(sb, partition, -alloc_count);
mutex_unlock(&sbi->s_alloc_mutex);
+ if (freed.partitionReferenceNum != 0xFFFF)
+ udf_free_blocks(sb, table, &freed, 0, 1);
return alloc_count;
}
@@ -560,6 +564,8 @@ static udf_pblk_t udf_table_new_block(struct super_block *sb,
uint32_t goal, int *err)
{
struct udf_sb_info *sbi = UDF_SB(sb);
+ /* AED block freed by udf_delete_aext(), released after unlock */
+ struct kernel_lb_addr freed = { .partitionReferenceNum = 0xFFFF };
uint32_t spread = 0xFFFFFFFF, nspread = 0xFFFFFFFF;
udf_pblk_t newblock = 0;
uint32_t adsize;
@@ -643,12 +649,14 @@ static udf_pblk_t udf_table_new_block(struct super_block *sb,
if (goal_elen)
udf_write_aext(table, &goal_epos, &goal_eloc, goal_elen, 1);
else
- udf_delete_aext(table, goal_epos);
+ udf_delete_aext(table, goal_epos, &freed);
brelse(goal_epos.bh);
udf_add_free_space(sb, partition, -1);
mutex_unlock(&sbi->s_alloc_mutex);
+ if (freed.partitionReferenceNum != 0xFFFF)
+ udf_free_blocks(sb, table, &freed, 0, 1);
*err = 0;
return newblock;
}
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index 67bcf83758c8..ebb67ce0aed7 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -1204,7 +1204,7 @@ static int udf_update_extents(struct inode *inode, struct kernel_long_ad *laarr,
if (startnum > endnum) {
for (i = 0; i < (startnum - endnum); i++)
- udf_delete_aext(inode, *epos);
+ udf_delete_aext(inode, *epos, NULL);
} else if (startnum < endnum) {
for (i = 0; i < (endnum - startnum); i++) {
err = udf_insert_aext(inode, *epos,
@@ -2328,7 +2328,8 @@ static int udf_insert_aext(struct inode *inode, struct extent_position epos,
return ret;
}
-int8_t udf_delete_aext(struct inode *inode, struct extent_position epos)
+int8_t udf_delete_aext(struct inode *inode, struct extent_position epos,
+ struct kernel_lb_addr *freed)
{
struct extent_position oepos;
int adsize;
@@ -2378,7 +2379,19 @@ int8_t udf_delete_aext(struct inode *inode, struct extent_position epos)
elen = 0;
if (epos.bh != oepos.bh) {
- udf_free_blocks(inode->i_sb, inode, &epos.block, 0, 1);
+ /*
+ * The block that held the now-empty allocation extent must be
+ * returned to free space. When the caller already holds
+ * s_alloc_mutex (the space-table allocator in balloc.c),
+ * freeing it inline would recurse through udf_free_blocks()
+ * into udf_table_free_blocks() and deadlock re-acquiring
+ * s_alloc_mutex. In that case report the block to the caller,
+ * which frees it after dropping the lock.
+ */
+ if (freed)
+ *freed = epos.block;
+ else
+ udf_free_blocks(inode->i_sb, inode, &epos.block, 0, 1);
udf_write_aext(inode, &oepos, &eloc, elen, 1);
udf_write_aext(inode, &oepos, &eloc, elen, 1);
if (!oepos.bh) {
diff --git a/fs/udf/truncate.c b/fs/udf/truncate.c
index 41b2bfd30449..0990f94b8551 100644
--- a/fs/udf/truncate.c
+++ b/fs/udf/truncate.c
@@ -159,7 +159,7 @@ void udf_discard_prealloc(struct inode *inode)
if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) {
lbcount -= elen;
- udf_delete_aext(inode, prev_epos);
+ udf_delete_aext(inode, prev_epos, NULL);
udf_free_blocks(inode->i_sb, inode, &eloc, 0,
DIV_ROUND_UP(elen, bsize));
}
diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h
index 6d951e05c004..01e4ce8644a9 100644
--- a/fs/udf/udfdecl.h
+++ b/fs/udf/udfdecl.h
@@ -170,7 +170,8 @@ extern int udf_add_aext(struct inode *, struct extent_position *,
struct kernel_lb_addr *, uint32_t, int);
extern void udf_write_aext(struct inode *, struct extent_position *,
struct kernel_lb_addr *, uint32_t, int);
-extern int8_t udf_delete_aext(struct inode *, struct extent_position);
+extern int8_t udf_delete_aext(struct inode *, struct extent_position,
+ struct kernel_lb_addr *);
extern int udf_next_aext(struct inode *inode, struct extent_position *epos,
struct kernel_lb_addr *eloc, uint32_t *elen,
int8_t *etype, int inc);
--
2.43.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [syzbot] [udf?] WARNING in udf_free_blocks (3)
[not found] <20260621022819.55018-1-kartikey406@gmail.com>
@ 2026-06-21 2:54 ` syzbot
0 siblings, 0 replies; 3+ messages in thread
From: syzbot @ 2026-06-21 2:54 UTC (permalink / raw)
To: kartikey406, linux-kernel, syzkaller-bugs
Hello,
syzbot has tested the proposed patch but the reproducer is still triggering an issue:
lost connection to test machine
Tested on:
commit: 1a3746cc Merge tag 'strncpy-removal-v7.2-rc1' of git:/..
git tree: upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=14fd6cde580000
kernel config: https://syzkaller.appspot.com/x/.config?x=7d60cd58d53b939b
dashboard link: https://syzkaller.appspot.com/bug?extid=6a680377e13041c19d50
compiler: Debian clang version 22.1.6 (++20260514074242+fc4aad7b5db3-1~exp1~20260514074407.73), Debian LLD 22.1.6
patch: https://syzkaller.appspot.com/x/patch.diff?x=1250f4fe580000
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-06-21 2:54 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-20 3:58 [syzbot] [udf?] WARNING in udf_free_blocks (3) syzbot
2026-06-21 2:28 ` Forwarded: [PATCH] udf: avoid recursive s_alloc_mutex deadlock when freeing AED blocks syzbot
[not found] <20260621022819.55018-1-kartikey406@gmail.com>
2026-06-21 2:54 ` [syzbot] [udf?] WARNING in udf_free_blocks (3) syzbot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox