mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 05:04:02 +02:00
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.
Reported-by: syzbot+c37bed40868932d790e9@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=c37bed40868932d790e9
Fixes: 7942b919f7 ("nilfs2: ioctl operations")
Cc: wuyankun <wuyankun@uniontech.com>
Cc: stable@vger.kernel.org
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
This commit is contained in:
parent
dc59e4fea9
commit
a1735eae55
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user