FAILED: patch "[PATCH] nilfs2: reject invalid block index in GC ioctl" failed to apply to 5.10-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 5.10-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.10.y
git checkout FETCH_HEAD
git cherry-pick -x a1735eae55448bc79c2da6593455791e886f6ed8
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026082455-batch-papyrus-5e86@gregkh' --subject-prefix 'PATCH 5.10.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From a1735eae55448bc79c2da6593455791e886f6ed8 Mon Sep 17 00:00:00 2001
From: Ryusuke Konishi <[email protected]>
Date: Fri, 3 Jul 2026 01:07:19 +0900
Subject: [PATCH] nilfs2: reject invalid block index in GC ioctl
Syzbot reported list corruption caused by a double list_add_tail() call on bh->b_assoc_buffers within nilfs_lookup_dirty_data_buffers().
Analysis revealed that the root cause was the insertion of a page/folio with a page index of ULONG_MAX into the page cache via the GC ioctl. filemap_get_folios_tag(), called by nilfs_lookup_dirty_data_buffers(), repeatedly detects a dirty folio with a page index of ULONG_MAX due to index wrap-around, leading to duplicate processing of dirty buffers.
As a preparatory step, the GC ioctl loads the page/folio of the block to be moved during GC and inserts it into the page cache based on information in the nilfs_vdesc structure passed as an argument. Normally, this does not cause issues because the user-space GC library configures the nilfs_vdesc structure properly. However, since there is no range check on the parameters determining the page index, a request with artificially crafted parameters -- such as those generated by Syzbot -- can result in a page/folio being inserted with a page index of ULONG_MAX, triggering the above problem.
This resolves the issue by checking the ranges of 'vd_offset' and 'vd_vblocknr' in the nilfs_vdesc structure that determine the page index, thereby preventing the invalid page/folio insertions.
fs/nilfs2/ioctl.c +18 -2
@@ -527,6 +527,7 @@ static int nilfs_ioctl_get_bdescs(struct inode *inode, struct file *filp,* Return: 0 on success, or one of the following negative error codes on * failure: * * %-EEXIST - Block conflict detected. +* * %-EINVAL - Invalid virtual block descriptor. * * %-EIO - I/O error. * * %-ENOENT - Requested block doesn't exist. * * %-ENOMEM - Insufficient memory available.
@@ -536,15 +537,30 @@ static int nilfs_ioctl_move_inode_block(struct inode *inode, struct list_head *buffers) {struct buffer_head *bh; +__u64 limit_blkidx = (__u64 )inode->i_sb->s_maxbytes >> inode->i_blkbits;int ret; -if (vdesc->vd_flags ==0 ) +/* +* vblocknr 0 is reserved as an invalid pointer. Also, limit_blkidx +* ensures that the page index converted from vd_vblocknr never +* overflows the page cache limit and respects the architecture's bmap +* key width. +*/ +if (unlikely (vdesc->vd_vblocknr ==0 || + vdesc->vd_vblocknr >= limit_blkidx)) +return -EINVAL ; + +if (vdesc->vd_flags ==0 ) { +if (unlikely (vdesc->vd_offset >= limit_blkidx)) +return -EINVAL ; + ret =nilfs_gccache_submit_read_data ( inode, vdesc->vd_offset, vdesc->vd_blocknr, vdesc->vd_vblocknr, &bh); -else + }else { ret =nilfs_gccache_submit_read_node ( inode, vdesc->vd_blocknr, vdesc->vd_vblocknr, &bh); + }if (unlikely (ret <0 )) {if (ret == -ENOENT )
Sasha Levin
maintainerPATCH 1/1 nilfs2: reject invalid block index in GC ioctl
From: Ryusuke Konishi <[email protected]>
[ Upstream commit a1735eae55448bc79c2da6593455791e886f6ed8 ]
Syzbot reported list corruption caused by a double list_add_tail() call on bh->b_assoc_buffers within nilfs_lookup_dirty_data_buffers().
Analysis revealed that the root cause was the insertion of a page/folio with a page index of ULONG_MAX into the page cache via the GC ioctl. filemap_get_folios_tag(), called by nilfs_lookup_dirty_data_buffers(), repeatedly detects a dirty folio with a page index of ULONG_MAX due to index wrap-around, leading to duplicate processing of dirty buffers.
As a preparatory step, the GC ioctl loads the page/folio of the block to be moved during GC and inserts it into the page cache based on information in the nilfs_vdesc structure passed as an argument. Normally, this does not cause issues because the user-space GC library configures the nilfs_vdesc structure properly. However, since there is no range check on the parameters determining the page index, a request with artificially crafted parameters -- such as those generated by Syzbot -- can result in a page/folio being inserted with a page index of ULONG_MAX, triggering the above problem.
This resolves the issue by checking the ranges of 'vd_offset' and 'vd_vblocknr' in the nilfs_vdesc structure that determine the page index, thereby preventing the invalid page/folio insertions.
--- fs/nilfs2/ioctl.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-)
fs/nilfs2/ioctl.c +19 -2
@@ -544,6 +544,8 @@ static int nilfs_ioctl_get_bdescs(struct inode *inode, struct file *filp,* Return Value: On success, 0 is returned. On error, one of the following * negative error codes is returned. * +* %-EINVAL - Invalid virtual block descriptor. +* * %-EIO - I/O error. * * %-ENOMEM - Insufficient amount of memory available.
@@ -557,15 +559,30 @@ static int nilfs_ioctl_move_inode_block(struct inode *inode, struct list_head *buffers) {struct buffer_head *bh; +__u64 limit_blkidx = (__u64 )inode->i_sb->s_maxbytes >> inode->i_blkbits;int ret; -if (vdesc->vd_flags ==0 ) +/* +* vblocknr 0 is reserved as an invalid pointer. Also, limit_blkidx +* ensures that the page index converted from vd_vblocknr never +* overflows the page cache limit and respects the architecture's bmap +* key width. +*/ +if (unlikely (vdesc->vd_vblocknr ==0 || + vdesc->vd_vblocknr >= limit_blkidx)) +return -EINVAL ; + +if (vdesc->vd_flags ==0 ) { +if (unlikely (vdesc->vd_offset >= limit_blkidx)) +return -EINVAL ; + ret =nilfs_gccache_submit_read_data ( inode, vdesc->vd_offset, vdesc->vd_blocknr, vdesc->vd_vblocknr, &bh); -else + }else { ret =nilfs_gccache_submit_read_node ( inode, vdesc->vd_blocknr, vdesc->vd_vblocknr, &bh); + }if (unlikely (ret <0 )) {if (ret == -ENOENT )
Signature · 1 line
2.53.0