[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <neqzjkw3ylaqghldgkl7jzlcl4o3vofbm6aa3a2kojfaakzjhm@ele2brknwzwm>
Date: Sat, 13 Dec 2025 14:25:29 +0530
From: Prithvi <activprithvi@...il.com>
To: Joseph Qi <joseph.qi@...ux.alibaba.com>
Cc: mark@...heh.com, jlbec@...lplan.org,
Heming Zhao <heming.zhao@...e.com>, ocfs2-devel@...ts.linux.dev, linux-kernel@...r.kernel.org,
linux-kernel-mentees@...ts.linux.dev, skhan@...uxfoundation.org, david.hunter.linux@...il.com,
khalid@...nel.org, syzbot+c818e5c4559444f88aa0@...kaller.appspotmail.com,
stable@...r.kernel.org
Subject: Re: [PATCH v2] ocfs2: fix kernel BUG in ocfs2_write_block
On Thu, Dec 11, 2025 at 10:17:21AM +0800, Joseph Qi wrote:
>
>
> On 2025/12/11 03:32, Prithvi Tambewagh wrote:
> > When the filesystem is being mounted, the kernel panics while the data
> > regarding slot map allocation to the local node, is being written to the
> > disk. This occurs because the value of slot map buffer head block
> > number, which should have been greater than or equal to
> > `OCFS2_SUPER_BLOCK_BLKNO` (evaluating to 2) is less than it, indicative
> > of disk metadata corruption. This triggers
> > BUG_ON(bh->b_blocknr < OCFS2_SUPER_BLOCK_BLKNO) in ocfs2_write_block(),
> > causing the kernel to panic.
> >
> > This is fixed by introducing an if condition block in
> > ocfs2_update_disk_slot(), right before calling ocfs2_write_block(), which
> > checks if `bh->b_blocknr` is lesser than `OCFS2_SUPER_BLOCK_BLKNO`; if
> > yes, then ocfs2_error is called, which prints the error log, for
> > debugging purposes, and the return value of ocfs2_error() is returned
> > back to caller of ocfs2_update_disk_slot() i.e. ocfs2_find_slot(). If
> > the return value is zero. then error code EIO is returned.
> >
> > Reported-by: syzbot+c818e5c4559444f88aa0@...kaller.appspotmail.com
> > Closes: https://syzkaller.appspot.com/bug?extid=c818e5c4559444f88aa0
> > Tested-by: syzbot+c818e5c4559444f88aa0@...kaller.appspotmail.com
> > Cc: stable@...r.kernel.org
> > Signed-off-by: Prithvi Tambewagh <activprithvi@...il.com>
> > ---
> > v1->v2:
> > - Remove usage of le16_to_cpu() from ocfs2_error()
> > - Cast bh->b_blocknr to unsigned long long
> > - Remove type casting for OCFS2_SUPER_BLOCK_BLKNO
> > - Fix Sparse warnings reported in v1 by kernel test robot
> > - Update title from 'ocfs2: Fix kernel BUG in ocfs2_write_block' to
> > 'ocfs2: fix kernel BUG in ocfs2_write_block'
> >
> > v1 link: https://lore.kernel.org/all/20251206154819.175479-1-activprithvi@gmail.com/T/
> >
> > fs/ocfs2/slot_map.c | 10 ++++++++++
> > 1 file changed, 10 insertions(+)
> >
> > diff --git a/fs/ocfs2/slot_map.c b/fs/ocfs2/slot_map.c
> > index e544c704b583..e916a2e8f92d 100644
> > --- a/fs/ocfs2/slot_map.c
> > +++ b/fs/ocfs2/slot_map.c
> > @@ -193,6 +193,16 @@ static int ocfs2_update_disk_slot(struct ocfs2_super *osb,
> > else
> > ocfs2_update_disk_slot_old(si, slot_num, &bh);
> > spin_unlock(&osb->osb_lock);
> > + if (bh->b_blocknr < OCFS2_SUPER_BLOCK_BLKNO) {
> > + status = ocfs2_error(osb->sb,
> > + "Invalid Slot Map Buffer Head "
> > + "Block Number : %llu, Should be >= %d",
> > + (unsigned long long)bh->b_blocknr,
> > + OCFS2_SUPER_BLOCK_BLKNO);
> > + if (!status)
> > + return -EIO;
> > + return status;
> > + }
> >
> > status = ocfs2_write_block(osb, bh, INODE_CACHE(si->si_inode));
> > if (status < 0)
> >
>
> Ummm... The 'bh' is from ocfs2_slot_info, which is load from crafted
> image during mount.
> So IIUC, the root cause is we read slot info without validating, see
> ocfs2_refresh_slot_info().
> So I'd prefer to implement a validate func and pass it into
> ocfs2_read_blocks() to do this job.
>
> Thanks,
> Joseph
I checked using GDB and learnt that ocfs2_refresh_slot() is not called
until the bug is triggered. Checking further, I obtained this backtrace
just before the bug was triggered, with a breakpoint set at
ocfs2_read_blocks():
#0 ocfs2_read_blocks (ci=0xffff88804b6e3cc0, block=0, nr=1, bhs=0xffffc9000f37efe0,
flags=1, validate=0x0) at fs/ocfs2/buffer_head_io.c:197
#1 0xffffffff83a1fb44 in ocfs2_map_slot_buffers (osb=0xffff888028d2c000,
si=0xffff888027329d00) at fs/ocfs2/slot_map.c:385
#2 ocfs2_init_slot_info (osb=0xffff888028d2c000) at fs/ocfs2/slot_map.c:424
#3 0xffffffff83a91b93 in ocfs2_initialize_super (sb=0xffff88802912a000, bh=<optimized out>,
sector_size=<optimized out>, stats=<optimized out>) at fs/ocfs2/super.c:2220
#4 ocfs2_fill_super (sb=0xffff88802912a000, fc=<optimized out>) at fs/ocfs2/super.c:993
#5 0xffffffff822db16e in get_tree_bdev_flags (fc=0xffff888029dac800,
fill_super=0xffffffff83a8d5c0 <ocfs2_fill_super>, flags=<optimized out>)
at fs/super.c:1691
#6 0xffffffff822db3b2 in vfs_get_tree (fc=0xffff888029dac800) at fs/super.c:1751
#7 0xffffffff82366f32 in fc_mount (fc=0xffff888029dac800) at fs/namespace.c:1208
#8 do_new_mount_fc (fc=0xffff888029dac800, mountpoint=0xffffc9000f37fe60, mnt_flags=48)
at fs/namespace.c:3651
#9 do_new_mount (path=0xffffc9000f37fe60, fstype=<optimized out>, sb_flags=<optimized out>,
mnt_flags=48, name=0xffff888024bf93a0 "/dev/loop0", data=0xffff888028b54000)
at fs/namespace.c:3727
#10 0xffffffff823659c7 in path_mount (
dev_name=0x1 <error: Cannot access memory at address 0x1>, path=0xffff888024bf93a0,
type_page=0xffff888028b54000 "user_xattr", flags=<optimized out>,
data_page=0x7fffc7d5cb00) at fs/namespace.c:4037
#11 0xffffffff82369253 in do_mount (dev_name=0xffff888024bf93a0 "/dev/loop0",
dir_name=0x200000000040 "./file1", type_page=<optimized out>, flags=2240,
--Type <RET> for more, q to quit, c to continue without paging--
data_page=0xffff888028b54000) at fs/namespace.c:4050
#12 __do_sys_mount (type=<optimized out>, dev_name=<optimized out>, dir_name=<optimized out>, flags=<optimized out>,
data=<optimized out>) at fs/namespace.c:4238
#13 __se_sys_mount (dev_name=<optimized out>, dir_name=35184372088896, type=<optimized out>, flags=2240,
data=140736546065152) at fs/namespace.c:4215
#14 0xffffffff82368f2c in __x64_sys_mount (regs=<optimized out>) at fs/namespace.c:4215
#15 0xffffffff81328ced in x64_sys_call (regs=0xffff88804b6e3cc0, nr=0) at arch/x86/entry/syscall_64.c:37
#16 0xffffffff8ac0015a in do_syscall_x64 (regs=0xffffc9000f37ff48, nr=165) at arch/x86/entry/syscall_64.c:63
#17 do_syscall_64 (regs=0xffffc9000f37ff48, nr=165) at arch/x86/entry/syscall_64.c:94
#18 0xffffffff81000130 in entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:121
#19 0x0000000000000000 in ?? ()
Hence, I think the function to fix would be ocfs2_map_slot_buffers() i.e.
the call to ocfs2_read_blocks() in it. I came up with following changes:
diff --git a/fs/ocfs2/slot_map.c b/fs/ocfs2/slot_map.c
index e544c704b583..299751617919 100644
--- a/fs/ocfs2/slot_map.c
+++ b/fs/ocfs2/slot_map.c
@@ -332,6 +332,26 @@ int ocfs2_clear_slot(struct ocfs2_super *osb, int slot_num)
return ocfs2_update_disk_slot(osb, osb->slot_info, slot_num);
}
+static int ocfs2_validate_slot_map_block(struct super_block *sb,
+ struct buffer_head *bh)
+{
+ int rc;
+
+ BUG_ON(!buffer_uptodate(bh));
+
+ if (bh->b_blocknr < OCFS2_SUPER_BLOCK_BLKNO) {
+ rc = ocfs2_error(osb->sb,
+ "Invalid Slot Map Buffer Head "
+ "Block Number : %llu, Should be >= %d",
+ (unsigned long long)bh->b_blocknr,
+ OCFS2_SUPER_BLOCK_BLKNO);
+ if (!rc)
+ return -EIO;
+ return status;
+ }
+ return 0;
+}
+
static int ocfs2_map_slot_buffers(struct ocfs2_super *osb,
struct ocfs2_slot_info *si)
{
@@ -383,7 +403,8 @@ static int ocfs2_map_slot_buffers(struct ocfs2_super *osb,
bh = NULL; /* Acquire a fresh bh */
status = ocfs2_read_blocks(INODE_CACHE(si->si_inode), blkno,
- 1, &bh, OCFS2_BH_IGNORE_CACHE, NULL);
+ 1, &bh, OCFS2_BH_IGNORE_CACHE,
+ ocfs2_validate_slot_map_block);
if (status < 0) {
mlog_errno(status);
goto bail;
What do you think? Is this the correct approach?
Thank You,
Prithvi
Powered by blists - more mailing lists