Re: [PATCH] nilfs2: reject invalid block index in GC ioctl
Viacheslav Dubeyko <[email protected]> Thu, 02 Jul 2026 16:09:22 -0700
| Newsgroups | org.kernel.vger.linux-nilfs,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
On Fri, 2026-07-03 at 01:07 +0900, Ryusuke Konishi wrote: > Syzbot reported list corruption caused by a double list_add_tail() > call on > bh->b_assoc_buffers within nilfs_lookup_dirty_data_buffers(). >=20 > 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. >=20 > 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.=C2=A0 Normally, this > does > not cause issues because the user-space GC library configures the > nilfs_vdesc structure properly.=C2=A0 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. >=20 > 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. >=20 > Reported-by: [email protected] > Closes: https://syzkaller.appspot.com/bug?extid=3Dc37bed40868932d790e9 > Fixes: 7942b919f732 ("nilfs2: ioctl operations") > Cc: wuyankun <[email protected]> > Cc: [email protected] > Signed-off-by: Ryusuke Konishi <[email protected]> > --- > Hi Viacheslav, >=20 > Please apply this one. >=20 > This fixes the list corruption issue recently detected by syzbot, > that > can occur when out-of-range values are intentionally passed to > certain > GC ioctl parameters. >=20 > Thanks, > Ryusuke Konishi >=20 > =C2=A0fs/nilfs2/ioctl.c | 20 ++++++++++++++++++-- > =C2=A01 file changed, 18 insertions(+), 2 deletions(-) >=20 > diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c > index b73f2c5d10f0..0957316e58b8 100644 > --- a/fs/nilfs2/ioctl.c > +++ b/fs/nilfs2/ioctl.c > @@ -527,6 +527,7 @@ static int nilfs_ioctl_get_bdescs(struct inode > *inode, struct file *filp, > =C2=A0 * Return: 0 on success, or one of the following negative error > codes on > =C2=A0 * failure: > =C2=A0 * * %-EEXIST - Block conflict detected. > + * * %-EINVAL - Invalid virtual block descriptor. > =C2=A0 * * %-EIO - I/O error. > =C2=A0 * * %-ENOENT - Requested block doesn't exist. > =C2=A0 * * %-ENOMEM - Insufficient memory available. > @@ -536,15 +537,30 @@ static int nilfs_ioctl_move_inode_block(struct > inode *inode, > =C2=A0 struct list_head *buffers) > =C2=A0{ > =C2=A0 struct buffer_head *bh; > + __u64 limit_blkidx =3D (__u64)inode->i_sb->s_maxbytes >> > inode->i_blkbits; > =C2=A0 int ret; > =C2=A0 > - if (vdesc->vd_flags =3D=3D 0) > + /* > + * vblocknr 0 is reserved as an invalid pointer.=C2=A0 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 =3D=3D 0 || > + vdesc->vd_vblocknr >=3D limit_blkidx)) > + return -EINVAL; > + > + if (vdesc->vd_flags =3D=3D 0) { > + if (unlikely(vdesc->vd_offset >=3D limit_blkidx)) > + return -EINVAL; > + > =C2=A0 ret =3D nilfs_gccache_submit_read_data( > =C2=A0 inode, vdesc->vd_offset, vdesc->vd_blocknr, > =C2=A0 vdesc->vd_vblocknr, &bh); > - else > + } else { > =C2=A0 ret =3D nilfs_gccache_submit_read_node( > =C2=A0 inode, vdesc->vd_blocknr, vdesc- > >vd_vblocknr, &bh); > + } > =C2=A0 > =C2=A0 if (unlikely(ret < 0)) { > =C2=A0 if (ret =3D=3D -ENOENT) Applied. Thanks, Slava.