nilfs2: fix use-after-free of nilfs_root in dirtying inodes via iput
commit f8654743a0e6909dc634cbfad6db6816f10f3399 upstream.
During unmount process of nilfs2, nothing holds nilfs_root structure after
nilfs2 detaches its writer in nilfs_detach_log_writer(). Previously,
nilfs_evict_inode() could cause use-after-free read for nilfs_root if
inodes are left in "garbage_list" and released by nilfs_dispose_list at
the end of nilfs_detach_log_writer(), and this bug was fixed by commit
9b5a04ac3ad9 ("nilfs2: fix use-after-free bug of nilfs_root in
nilfs_evict_inode()").
However, it turned out that there is another possibility of UAF in the
call path where mark_inode_dirty_sync() is called from iput():
nilfs_detach_log_writer()
nilfs_dispose_list()
iput()
mark_inode_dirty_sync()
__mark_inode_dirty()
nilfs_dirty_inode()
__nilfs_mark_inode_dirty()
nilfs_load_inode_block() --> causes UAF of nilfs_root struct
This can happen after commit 0ae45f63d4 ("vfs: add support for a
lazytime mount option"), which changed iput() to call
mark_inode_dirty_sync() on its final reference if i_state has I_DIRTY_TIME
flag and i_nlink is non-zero.
This issue appears after commit 28a65b49eb53 ("nilfs2: do not write dirty
data after degenerating to read-only") when using the syzbot reproducer,
but the issue has potentially existed before.
Fix this issue by adding a "purging flag" to the nilfs structure, setting
that flag while disposing the "garbage_list" and checking it in
__nilfs_mark_inode_dirty().
Unlike commit 9b5a04ac3ad9 ("nilfs2: fix use-after-free bug of nilfs_root
in nilfs_evict_inode()"), this patch does not rely on ns_writer to
determine whether to skip operations, so as not to break recovery on
mount. The nilfs_salvage_orphan_logs routine dirties the buffer of
salvaged data before attaching the log writer, so changing
__nilfs_mark_inode_dirty() to skip the operation when ns_writer is NULL
will cause recovery write to fail. The purpose of using the cleanup-only
flag is to allow for narrowing of such conditions.
Link: https://lkml.kernel.org/r/20230728191318.33047-1-konishi.ryusuke@gmail.com
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Reported-by: syzbot+74db8b3087f293d3a13a@syzkaller.appspotmail.com
Closes: https://lkml.kernel.org/r/000000000000b4e906060113fd63@google.com
Fixes: 0ae45f63d4 ("vfs: add support for a lazytime mount option")
Tested-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Cc: <stable@vger.kernel.org> # 4.0+
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
79a9697029
commit
7532ff6edb
3 changed files with 12 additions and 0 deletions
|
|
@ -1101,9 +1101,17 @@ int nilfs_set_file_dirty(struct inode *inode, unsigned int nr_dirty)
|
|||
|
||||
int __nilfs_mark_inode_dirty(struct inode *inode, int flags)
|
||||
{
|
||||
struct the_nilfs *nilfs = inode->i_sb->s_fs_info;
|
||||
struct buffer_head *ibh;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Do not dirty inodes after the log writer has been detached
|
||||
* and its nilfs_root struct has been freed.
|
||||
*/
|
||||
if (unlikely(nilfs_purging(nilfs)))
|
||||
return 0;
|
||||
|
||||
err = nilfs_load_inode_block(inode, &ibh);
|
||||
if (unlikely(err)) {
|
||||
nilfs_warn(inode->i_sb,
|
||||
|
|
|
|||
|
|
@ -2843,6 +2843,7 @@ void nilfs_detach_log_writer(struct super_block *sb)
|
|||
nilfs_segctor_destroy(nilfs->ns_writer);
|
||||
nilfs->ns_writer = NULL;
|
||||
}
|
||||
set_nilfs_purging(nilfs);
|
||||
|
||||
/* Force to free the list of dirty files */
|
||||
spin_lock(&nilfs->ns_inode_lock);
|
||||
|
|
@ -2855,4 +2856,5 @@ void nilfs_detach_log_writer(struct super_block *sb)
|
|||
up_write(&nilfs->ns_segctor_sem);
|
||||
|
||||
nilfs_dispose_list(nilfs, &garbage_list, 1);
|
||||
clear_nilfs_purging(nilfs);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ enum {
|
|||
THE_NILFS_DISCONTINUED, /* 'next' pointer chain has broken */
|
||||
THE_NILFS_GC_RUNNING, /* gc process is running */
|
||||
THE_NILFS_SB_DIRTY, /* super block is dirty */
|
||||
THE_NILFS_PURGING, /* disposing dirty files for cleanup */
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -208,6 +209,7 @@ THE_NILFS_FNS(INIT, init)
|
|||
THE_NILFS_FNS(DISCONTINUED, discontinued)
|
||||
THE_NILFS_FNS(GC_RUNNING, gc_running)
|
||||
THE_NILFS_FNS(SB_DIRTY, sb_dirty)
|
||||
THE_NILFS_FNS(PURGING, purging)
|
||||
|
||||
/*
|
||||
* Mount option operations
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue