[PATCH] ocfs2: fix out-of-bounds write in ocfs2_write_end_inline

From: Joseph Qi

Date: Fri Apr 03 2026 - 02:40:18 EST


KASAN reports a use-after-free write of 4086 bytes in
ocfs2_write_end_inline, called from ocfs2_write_end_nolock during a
copy_file_range splice fallback on a corrupted ocfs2 filesystem mounted
on a loop device. The actual bug is an out-of-bounds write past the
inode block buffer, not a true use-after-free. The write overflows into
an adjacent freed page, which KASAN reports as UAF.

The root cause is that ocfs2_try_to_write_inline_data trusts the
on-disk id_count field to determine whether a write fits in inline
data. On a corrupted filesystem, id_count can exceed the physical
maximum inline data capacity, causing writes to overflow the inode
block buffer.

Call trace (crash path):

vfs_copy_file_range (fs/read_write.c:1634)
do_splice_direct
splice_direct_to_actor
iter_file_splice_write
ocfs2_file_write_iter
generic_perform_write
ocfs2_write_end
ocfs2_write_end_nolock (fs/ocfs2/aops.c:1949)
ocfs2_write_end_inline (fs/ocfs2/aops.c:1915)
memcpy_from_folio <-- KASAN: write OOB

So add id_count upper bound check in ocfs2_validate_inode_block() to
alongside the existing i_size check to fix it.

Reported-by: syzbot+62c1793956716ea8b28a@xxxxxxxxxxxxxxxxxxxxxxxxx
Closes: https://syzkaller.appspot.com/bug?extid=62c1793956716ea8b28a
Cc: <stable@xxxxxxxxxxxxxxx>
Signed-off-by: Joseph Qi <joseph.qi@xxxxxxxxxxxxxxxxx>
---
fs/ocfs2/inode.c | 10 ++++++++++
1 file changed, 10 insertions(+)

diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c
index 03a51662ea8e..a2ccd8011706 100644
--- a/fs/ocfs2/inode.c
+++ b/fs/ocfs2/inode.c
@@ -1505,6 +1505,16 @@ int ocfs2_validate_inode_block(struct super_block *sb,
goto bail;
}

+ if (le16_to_cpu(data->id_count) >
+ ocfs2_max_inline_data_with_xattr(sb, di)) {
+ rc = ocfs2_error(sb,
+ "Invalid dinode #%llu: inline data id_count %u exceeds max %d\n",
+ (unsigned long long)bh->b_blocknr,
+ le16_to_cpu(data->id_count),
+ ocfs2_max_inline_data_with_xattr(sb, di));
+ goto bail;
+ }
+
if (le64_to_cpu(di->i_size) > le16_to_cpu(data->id_count)) {
rc = ocfs2_error(sb,
"Invalid dinode #%llu: inline data i_size %llu exceeds id_count %u\n",
--
2.39.3