Skip to content

Commit 512c5ca

Browse files
Chen Zhongjinakpm00
authored andcommitted
nilfs2: fix nilfs_sufile_mark_dirty() not set segment usage as dirty
When extending segments, nilfs_sufile_alloc() is called to get an unassigned segment, then mark it as dirty to avoid accidentally allocating the same segment in the future. But for some special cases such as a corrupted image it can be unreliable. If such corruption of the dirty state of the segment occurs, nilfs2 may reallocate a segment that is in use and pick the same segment for writing twice at the same time. This will cause the problem reported by syzkaller: https://syzkaller.appspot.com/bug?id=c7c4748e11ffcc367cef04f76e02e931833cbd24 This case started with segbuf1.segnum = 3, nextnum = 4 when constructed. It supposed segment 4 has already been allocated and marked as dirty. However the dirty state was corrupted and segment 4 usage was not dirty. For the first time nilfs_segctor_extend_segments() segment 4 was allocated again, which made segbuf2 and next segbuf3 had same segment 4. sb_getblk() will get same bh for segbuf2 and segbuf3, and this bh is added to both buffer lists of two segbuf. It makes the lists broken which causes NULL pointer dereference. Fix the problem by setting usage as dirty every time in nilfs_sufile_mark_dirty(), which is called during constructing current segment to be written out and before allocating next segment. [[email protected]: add lock protection per Ryusuke] Link: https://lkml.kernel.org/r/[email protected] Link: https://lkml.kernel.org/r/[email protected] Fixes: 9ff0512 ("nilfs2: segment constructor") Signed-off-by: Chen Zhongjin <[email protected]> Reported-by: <[email protected]> Reported-by: Liu Shixin <[email protected]> Acked-by: Ryusuke Konishi <[email protected]> Tested-by: Ryusuke Konishi <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 81a70c2 commit 512c5ca

File tree

1 file changed

+8
-0
lines changed

1 file changed

+8
-0
lines changed

fs/nilfs2/sufile.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,14 +495,22 @@ void nilfs_sufile_do_free(struct inode *sufile, __u64 segnum,
495495
int nilfs_sufile_mark_dirty(struct inode *sufile, __u64 segnum)
496496
{
497497
struct buffer_head *bh;
498+
void *kaddr;
499+
struct nilfs_segment_usage *su;
498500
int ret;
499501

502+
down_write(&NILFS_MDT(sufile)->mi_sem);
500503
ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, 0, &bh);
501504
if (!ret) {
502505
mark_buffer_dirty(bh);
503506
nilfs_mdt_mark_dirty(sufile);
507+
kaddr = kmap_atomic(bh->b_page);
508+
su = nilfs_sufile_block_get_segment_usage(sufile, segnum, bh, kaddr);
509+
nilfs_segment_usage_set_dirty(su);
510+
kunmap_atomic(kaddr);
504511
brelse(bh);
505512
}
513+
up_write(&NILFS_MDT(sufile)->mi_sem);
506514
return ret;
507515
}
508516

0 commit comments

Comments
 (0)