On Tue, May 02, 2017 at 01:36:33AM +0800, Eryu Guan wrote: > Commit 28b783e47ad7 ("xfs: bufferhead chains are invalid after > end_page_writeback") fixed one use-after-free issue by > pre-calculating the loop conditionals before calling bh->b_end_io() > in the end_io processing loop, but it assigned 'next' pointer before > checking end offset boundary & breaking the loop, at which point the > bh might be freed already, and caused use-after-free. > > This is caught by KASAN when running fstests generic/127 on sub-page > block size XFS. > > [ 2517.244502] run fstests generic/127 at 2017-04-27 07:30:50 > [ 2747.868840] ================================================================== > [ 2747.876949] BUG: KASAN: use-after-free in xfs_destroy_ioend+0x3d3/0x4e0 [xfs] at addr ffff8801395ae698 > ... > [ 2747.918245] Call Trace: > [ 2747.920975] dump_stack+0x63/0x84 > [ 2747.924673] kasan_object_err+0x21/0x70 > [ 2747.928950] kasan_report+0x271/0x530 > [ 2747.933064] ? xfs_destroy_ioend+0x3d3/0x4e0 [xfs] > [ 2747.938409] ? end_page_writeback+0xce/0x110 > [ 2747.943171] __asan_report_load8_noabort+0x19/0x20 > [ 2747.948545] xfs_destroy_ioend+0x3d3/0x4e0 [xfs] > [ 2747.953724] xfs_end_io+0x1af/0x2b0 [xfs] > [ 2747.958197] process_one_work+0x5ff/0x1000 > [ 2747.962766] worker_thread+0xe4/0x10e0 > [ 2747.966946] kthread+0x2d3/0x3d0 > [ 2747.970546] ? process_one_work+0x1000/0x1000 > [ 2747.975405] ? kthread_create_on_node+0xc0/0xc0 > [ 2747.980457] ? syscall_return_slowpath+0xe6/0x140 > [ 2747.985706] ? do_page_fault+0x30/0x80 > [ 2747.989887] ret_from_fork+0x2c/0x40 > [ 2747.993874] Object at ffff8801395ae690, in cache buffer_head size: 104 > [ 2748.001155] Allocated: > [ 2748.003782] PID = 8327 > [ 2748.006411] save_stack_trace+0x1b/0x20 > [ 2748.010688] save_stack+0x46/0xd0 > [ 2748.014383] kasan_kmalloc+0xad/0xe0 > [ 2748.018370] kasan_slab_alloc+0x12/0x20 > [ 2748.022648] kmem_cache_alloc+0xb8/0x1b0 > [ 2748.027024] alloc_buffer_head+0x22/0xc0 > [ 2748.031399] alloc_page_buffers+0xd1/0x250 > [ 2748.035968] create_empty_buffers+0x30/0x410 > [ 2748.040730] create_page_buffers+0x120/0x1b0 > [ 2748.045493] __block_write_begin_int+0x17a/0x1800 > [ 2748.050740] iomap_write_begin+0x100/0x2f0 > [ 2748.055308] iomap_zero_range_actor+0x253/0x5c0 > [ 2748.060362] iomap_apply+0x157/0x270 > [ 2748.064347] iomap_zero_range+0x5a/0x80 > [ 2748.068624] iomap_truncate_page+0x6b/0xa0 > [ 2748.073227] xfs_setattr_size+0x1f7/0xa10 [xfs] > [ 2748.078312] xfs_vn_setattr_size+0x68/0x140 [xfs] > [ 2748.083589] xfs_file_fallocate+0x4ac/0x820 [xfs] > [ 2748.088838] vfs_fallocate+0x2cf/0x780 > [ 2748.093021] SyS_fallocate+0x48/0x80 > [ 2748.097006] do_syscall_64+0x18a/0x430 > [ 2748.101186] return_from_SYSCALL_64+0x0/0x6a > [ 2748.105948] Freed: > [ 2748.108189] PID = 8327 > [ 2748.110816] save_stack_trace+0x1b/0x20 > [ 2748.115093] save_stack+0x46/0xd0 > [ 2748.118788] kasan_slab_free+0x73/0xc0 > [ 2748.122969] kmem_cache_free+0x7a/0x200 > [ 2748.127247] free_buffer_head+0x41/0x80 > [ 2748.131524] try_to_free_buffers+0x178/0x250 > [ 2748.136316] xfs_vm_releasepage+0x2e9/0x3d0 [xfs] > [ 2748.141563] try_to_release_page+0x100/0x180 > [ 2748.146325] invalidate_inode_pages2_range+0x7da/0xcf0 > [ 2748.152087] xfs_shift_file_space+0x37d/0x6e0 [xfs] > [ 2748.157557] xfs_collapse_file_space+0x49/0x120 [xfs] > [ 2748.163223] xfs_file_fallocate+0x2a7/0x820 [xfs] > [ 2748.168462] vfs_fallocate+0x2cf/0x780 > [ 2748.172642] SyS_fallocate+0x48/0x80 > [ 2748.176629] do_syscall_64+0x18a/0x430 > [ 2748.180810] return_from_SYSCALL_64+0x0/0x6a > > Fixed it by checking on offset against end & breaking out first, > dereference bh only if there're still bufferheads to process. > > Signed-off-by: Eryu Guan <eguan@xxxxxxxxxx> Looks ok, will test; Reviewed-by: Darrick J. Wong <darrick.wong@xxxxxxxxxx> --D > --- > fs/xfs/xfs_aops.c | 4 ++-- > 1 file changed, 2 insertions(+), 2 deletions(-) > > diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c > index 6149429..a7645be 100644 > --- a/fs/xfs/xfs_aops.c > +++ b/fs/xfs/xfs_aops.c > @@ -111,11 +111,11 @@ xfs_finish_page_writeback( > > bsize = bh->b_size; > do { > + if (off > end) > + break; > next = bh->b_this_page; > if (off < bvec->bv_offset) > goto next_bh; > - if (off > end) > - break; > bh->b_end_io(bh, !error); > next_bh: > off += bsize; > -- > 2.9.3 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html