[BUG] ufs: divide-by-zero in ufs_iget() at fs/ufs/inode.c:663 on root-inode load

From: Farhad Alemi

Date: Tue May 26 2026 - 17:58:28 EST


Hello, again, Al and the linux-fsdevel team,

I am reporting a UFS crash found by syzkaller. fs/ufs has no named
M: entry in MAINTAINERS, so I'm routing by commit-history affinity.

Summary:
Mounting a crafted UFS image runs ufs_fill_super() -> ufs_iget() and
traps a #DE at fs/ufs/inode.c:663. The trapping site is the
ufs_inotofsba() macro evaluated on the root inode, which expands to:

(((u64)ufs_cgimin(ufs_inotocg(x))) + ufs_inotocgoff(x) / uspi->s_inopf)

uspi->s_inopf is set in ufs_fill_super() from uspi->s_inopb >>
uspi->s_fpbshift (fs/ufs/super.c:1181), where both right-hand operands
are derived from on-disk superblock fields without consistency checking.
A crafted image can drive s_inopf to zero, and the next inode load
divides by zero.

The kernel's own decoded disassembly pins the trap to
"divl (%r15)" with RDX pre-zeroed; %r15 was loaded by
"lea 0x158(%rcx), %r15". The macro chain above is the only divisor in
ufs_iget()'s prologue path, so the field at offset 0x158 from the uspi
pointer is most likely s_inopf (worth confirming via pahole on
struct ufs_sb_private_info).

This is related to but distinct from the earlier UBSAN report at
fs/ufs/super.c:1181 (where s_fpbshift itself is the out-of-range shift
exponent): the same on-disk fragment-shift field that drives the
UBSAN-tripping shift can also drive s_inopf to zero and trap the
divide-by-zero here on the very first inode load.

Observed on:
- Linux v7.1-rc3-200-g70eda68668d1-dirty, x86_64, QEMU Q35
- KASAN enabled; panic_on_warn set
- The only local dirty file in my tree is drivers/tty/serial/serial_core.c,
containing a local ttyS0 console guard for the fuzzing harness. It is
unrelated to fs/ufs/.
- Trigger requires the ability to mount a crafted filesystem image,
normally CAP_SYS_ADMIN/root or an equivalent syzkaller test environment.

Impact:
A crafted UFS image crashes the kernel on mount:

ufs: ufstype=old is supported read-only
Oops: divide error: 0000 [#1] SMP KASAN NOPTI
RIP: 0010:ufs_iget+0x354/0x2440 fs/ufs/inode.c:663

Relevant stack:

ufs_iget+0x354/0x2440 fs/ufs/inode.c:663
ufs_fill_super+0x47c6/0x7360 fs/ufs/super.c:1202
get_tree_bdev_flags+0x436/0x500 fs/super.c:1694
vfs_get_tree+0x97/0x2b0 fs/super.c:1754
do_new_mount+0x346/0xd30 fs/namespace.c:3834
__se_sys_mount+0x322/0x420 fs/namespace.c:4360

Expected behavior:
ufs_fill_super() should validate that uspi->s_inopf is non-zero (and in
a sane range) after deriving it from the on-disk fields, and reject the
mount with -EINVAL/-EIO before any inode-load path can divide by it.

Reproducer:
I attached the generated C reproducer as reproducer.c. I also attached the
syzkaller program as reproducer.syz and the console report as
crash-report.txt.

Novelty check:
I searched syzbot dashboard data across upstream, fixed, invalid, stable,
and Android namespaces, and searched lore.kernel.org for "divide error"
+ "ufs_iget" and "fs/ufs/inode.c:663". I did not find an exact match.
Historical "divide error in ocfs2_iget" / "ext4" / "squashfs" reports
are unrelated.

I appreciate your time and consideration.

Regards,
Farhad
mount -t ufs -o ufstype=sun|sunx86|44bsd|ufs2|5xbsd|old|hp|nextstep|nextstep-cd|openstep ...
>>>WARNING<<< Wrong ufstype may corrupt your filesystem, default is ufstype=old
ufs: ufstype=old is supported read-only
Oops: divide error: 0000 [#1] SMP KASAN NOPTI
CPU: 0 UID: 0 PID: 3591 Comm: syz.2.17 Not tainted 7.1.0-rc3-00200-g70eda68668d1-dirty #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:ufs_iget+0x354/0x2440 fs/ufs/inode.c:663
Code: f7 b1 0c 01 00 00 89 d0 4c 8d b9 58 01 00 00 4c 89 f9 48 c1 e9 03 48 89 4c 24 30 42 0f b6 0c 29 84 c9 0f 85 4d 17 00 00 31 d2 <41> f7 37 89 c3 4c 03 74 24 40 48 8b 04 24 48 8d b8 40 01 00 00 48
RSP: 0018:ffffc90002a2f998 EFLAGS: 00010246
RAX: 0000000000000002 RBX: ffff88812a85c800 RCX: 0000000000000000
RDX: 0000000000000000 RSI: dffffc0000000000 RDI: ffff88811dd43088
RBP: ffff88812846a618 R08: ffff88812846a983 R09: 1ffff1102508d530
R10: dffffc0000000000 R11: ffffed102508d531 R12: 1ffff1102508d4c3
R13: dffffc0000000000 R14: 0000000000000028 R15: ffff88811dd43158
FS: 000055557d39b500(0000) GS:ffff8882ab6b6000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f2f44ffd000 CR3: 0000000103f89000 CR4: 0000000000750ef0
PKRU: 00000000
Call Trace:
<TASK>
ufs_fill_super+0x47c6/0x7360 fs/ufs/super.c:1202
get_tree_bdev_flags+0x436/0x500 fs/super.c:1694
vfs_get_tree+0x97/0x2b0 fs/super.c:1754
fc_mount fs/namespace.c:1193 [inline]
do_new_mount_fc fs/namespace.c:3758 [inline]
do_new_mount+0x346/0xd30 fs/namespace.c:3834
do_mount fs/namespace.c:4167 [inline]
__do_sys_mount fs/namespace.c:4383 [inline]
__se_sys_mount+0x322/0x420 fs/namespace.c:4360
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x15f/0x560 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f2181e791be
Code: 0f 1f 40 00 48 c7 c2 b0 ff ff ff f7 d8 64 89 02 b8 ff ff ff ff c3 66 0f 1f 44 00 00 f3 0f 1e fa 49 89 ca b8 a5 00 00 00 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:00007fff107f9128 EFLAGS: 00000246 ORIG_RAX: 00000000000000a5
RAX: ffffffffffffffda RBX: 00007fff107f91c0 RCX: 00007f2181e791be
RDX: 0000200000002100 RSI: 0000200000002140 RDI: 00007fff107f9180
RBP: 0000200000002100 R08: 00007fff107f91c0 R09: 0000000000000040
R10: 0000000000000040 R11: 0000000000000246 R12: 0000200000002140
R13: 00007fff107f9180 R14: 00000000000020fe R15: 0000200000002180
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:ufs_iget+0x354/0x2440 fs/ufs/inode.c:663
Code: f7 b1 0c 01 00 00 89 d0 4c 8d b9 58 01 00 00 4c 89 f9 48 c1 e9 03 48 89 4c 24 30 42 0f b6 0c 29 84 c9 0f 85 4d 17 00 00 31 d2 <41> f7 37 89 c3 4c 03 74 24 40 48 8b 04 24 48 8d b8 40 01 00 00 48
RSP: 0018:ffffc90002a2f998 EFLAGS: 00010246
RAX: 0000000000000002 RBX: ffff88812a85c800 RCX: 0000000000000000
RDX: 0000000000000000 RSI: dffffc0000000000 RDI: ffff88811dd43088
RBP: ffff88812846a618 R08: ffff88812846a983 R09: 1ffff1102508d530
R10: dffffc0000000000 R11: ffffed102508d531 R12: 1ffff1102508d4c3
R13: dffffc0000000000 R14: 0000000000000028 R15: ffff88811dd43158
FS: 000055557d39b500(0000) GS:ffff8882ab6b6000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f5814709020 CR3: 0000000103f89000 CR4: 0000000000750ef0
PKRU: 00000000
----------------
Code disassembly (best guess):
0: f7 b1 0c 01 00 00 divl 0x10c(%rcx)
6: 89 d0 mov %edx,%eax
8: 4c 8d b9 58 01 00 00 lea 0x158(%rcx),%r15
f: 4c 89 f9 mov %r15,%rcx
12: 48 c1 e9 03 shr $0x3,%rcx
16: 48 89 4c 24 30 mov %rcx,0x30(%rsp)
1b: 42 0f b6 0c 29 movzbl (%rcx,%r13,1),%ecx
20: 84 c9 test %cl,%cl
22: 0f 85 4d 17 00 00 jne 0x1775
28: 31 d2 xor %edx,%edx
* 2a: 41 f7 37 divl (%r15) <-- trapping instruction
2d: 89 c3 mov %eax,%ebx
2f: 4c 03 74 24 40 add 0x40(%rsp),%r14
34: 48 8b 04 24 mov (%rsp),%rax
38: 48 8d b8 40 01 00 00 lea 0x140(%rax),%rdi
3f: 48 rex.W

Attachment: reproducer.syz
Description: Binary data

Attachment: reproducer.c
Description: Binary data