| From ccccf3d67294714af2d72a6fd6fd7d73b01c9329 Mon Sep 17 00:00:00 2001 |
| From: Filipe Manana <fdmanana@suse.com> |
| Date: Mon, 30 Mar 2015 18:23:59 +0100 |
| Subject: Btrfs: fix inode eviction infinite loop after cloning into it |
| |
| From: Filipe Manana <fdmanana@suse.com> |
| |
| commit ccccf3d67294714af2d72a6fd6fd7d73b01c9329 upstream. |
| |
| If we attempt to clone a 0 length region into a file we can end up |
| inserting a range in the inode's extent_io tree with a start offset |
| that is greater then the end offset, which triggers immediately the |
| following warning: |
| |
| [ 3914.619057] WARNING: CPU: 17 PID: 4199 at fs/btrfs/extent_io.c:435 insert_state+0x4b/0x10b [btrfs]() |
| [ 3914.620886] BTRFS: end < start 4095 4096 |
| (...) |
| [ 3914.638093] Call Trace: |
| [ 3914.638636] [<ffffffff81425fd9>] dump_stack+0x4c/0x65 |
| [ 3914.639620] [<ffffffff81045390>] warn_slowpath_common+0xa1/0xbb |
| [ 3914.640789] [<ffffffffa03ca44f>] ? insert_state+0x4b/0x10b [btrfs] |
| [ 3914.642041] [<ffffffff810453f0>] warn_slowpath_fmt+0x46/0x48 |
| [ 3914.643236] [<ffffffffa03ca44f>] insert_state+0x4b/0x10b [btrfs] |
| [ 3914.644441] [<ffffffffa03ca729>] __set_extent_bit+0x107/0x3f4 [btrfs] |
| [ 3914.645711] [<ffffffffa03cb256>] lock_extent_bits+0x65/0x1bf [btrfs] |
| [ 3914.646914] [<ffffffff8142b2fb>] ? _raw_spin_unlock+0x28/0x33 |
| [ 3914.648058] [<ffffffffa03cbac4>] ? test_range_bit+0xcc/0xde [btrfs] |
| [ 3914.650105] [<ffffffffa03cb3c3>] lock_extent+0x13/0x15 [btrfs] |
| [ 3914.651361] [<ffffffffa03db39e>] lock_extent_range+0x3d/0xcd [btrfs] |
| [ 3914.652761] [<ffffffffa03de1fe>] btrfs_ioctl_clone+0x278/0x388 [btrfs] |
| [ 3914.654128] [<ffffffff811226dd>] ? might_fault+0x58/0xb5 |
| [ 3914.655320] [<ffffffffa03e0909>] btrfs_ioctl+0xb51/0x2195 [btrfs] |
| (...) |
| [ 3914.669271] ---[ end trace 14843d3e2e622fc1 ]--- |
| |
| This later makes the inode eviction handler enter an infinite loop that |
| keeps dumping the following warning over and over: |
| |
| [ 3915.117629] WARNING: CPU: 22 PID: 4228 at fs/btrfs/extent_io.c:435 insert_state+0x4b/0x10b [btrfs]() |
| [ 3915.119913] BTRFS: end < start 4095 4096 |
| (...) |
| [ 3915.137394] Call Trace: |
| [ 3915.137913] [<ffffffff81425fd9>] dump_stack+0x4c/0x65 |
| [ 3915.139154] [<ffffffff81045390>] warn_slowpath_common+0xa1/0xbb |
| [ 3915.140316] [<ffffffffa03ca44f>] ? insert_state+0x4b/0x10b [btrfs] |
| [ 3915.141505] [<ffffffff810453f0>] warn_slowpath_fmt+0x46/0x48 |
| [ 3915.142709] [<ffffffffa03ca44f>] insert_state+0x4b/0x10b [btrfs] |
| [ 3915.143849] [<ffffffffa03ca729>] __set_extent_bit+0x107/0x3f4 [btrfs] |
| [ 3915.145120] [<ffffffffa038c1e3>] ? btrfs_kill_super+0x17/0x23 [btrfs] |
| [ 3915.146352] [<ffffffff811548f6>] ? deactivate_locked_super+0x3b/0x50 |
| [ 3915.147565] [<ffffffffa03cb256>] lock_extent_bits+0x65/0x1bf [btrfs] |
| [ 3915.148785] [<ffffffff8142b7e2>] ? _raw_write_unlock+0x28/0x33 |
| [ 3915.149931] [<ffffffffa03bc325>] btrfs_evict_inode+0x196/0x482 [btrfs] |
| [ 3915.151154] [<ffffffff81168904>] evict+0xa0/0x148 |
| [ 3915.152094] [<ffffffff811689e5>] dispose_list+0x39/0x43 |
| [ 3915.153081] [<ffffffff81169564>] evict_inodes+0xdc/0xeb |
| [ 3915.154062] [<ffffffff81154418>] generic_shutdown_super+0x49/0xef |
| [ 3915.155193] [<ffffffff811546d1>] kill_anon_super+0x13/0x1e |
| [ 3915.156274] [<ffffffffa038c1e3>] btrfs_kill_super+0x17/0x23 [btrfs] |
| (...) |
| [ 3915.167404] ---[ end trace 14843d3e2e622fc2 ]--- |
| |
| So just bail out of the clone ioctl if the length of the region to clone |
| is zero, without locking any extent range, in order to prevent this issue |
| (same behaviour as a pwrite with a 0 length for example). |
| |
| This is trivial to reproduce. For example, the steps for the test I just |
| made for fstests: |
| |
| mkfs.btrfs -f SCRATCH_DEV |
| mount SCRATCH_DEV $SCRATCH_MNT |
| |
| touch $SCRATCH_MNT/foo |
| touch $SCRATCH_MNT/bar |
| |
| $CLONER_PROG -s 0 -d 4096 -l 0 $SCRATCH_MNT/foo $SCRATCH_MNT/bar |
| umount $SCRATCH_MNT |
| |
| A test case for fstests follows soon. |
| |
| Signed-off-by: Filipe Manana <fdmanana@suse.com> |
| Reviewed-by: Omar Sandoval <osandov@osandov.com> |
| Signed-off-by: Chris Mason <clm@fb.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/btrfs/ioctl.c | 5 +++++ |
| 1 file changed, 5 insertions(+) |
| |
| --- a/fs/btrfs/ioctl.c |
| +++ b/fs/btrfs/ioctl.c |
| @@ -3226,6 +3226,11 @@ static noinline long btrfs_ioctl_clone(s |
| if (off + len == src->i_size) |
| len = ALIGN(src->i_size, bs) - off; |
| |
| + if (len == 0) { |
| + ret = 0; |
| + goto out_unlock; |
| + } |
| + |
| /* verify the end result is block aligned */ |
| if (!IS_ALIGNED(off, bs) || !IS_ALIGNED(off + len, bs) || |
| !IS_ALIGNED(destoff, bs)) |