Skip to content

Commit 3387206

Browse files
trofichrismason-xx
authored andcommitted
btrfs: properly handle overlapping areas in memmove_extent_buffer
Fix data corruption caused by memcpy() usage on overlapping data. I've observed it first when found out usermode linux crash on btrfs. ?all chain is the following: ------------[ cut here ]------------ WARNING: at /home/slyfox/linux-2.6/fs/btrfs/extent_io.c:3900 memcpy_extent_buffer+0x1a5/0x219() Call Trace: 6fa39a5: [<601b495e>] _raw_spin_unlock_irqrestore+0x18/0x1c 6fa39a68: [<60029ad9>] warn_slowpath_common+0x59/0x70 6fa39aa8: [<60029b05>] warn_slowpath_null+0x15/0x17 6fa39ab8: [<600efc97>] memcpy_extent_buffer+0x1a5/0x219 6fa39b48: [<600efd9f>] memmove_extent_buffer+0x94/0x208 6fa39bc8: [<600becbf>] btrfs_del_items+0x214/0x473 6fa39c78: [<600ce1b0>] btrfs_delete_one_dir_name+0x7c/0xda 6fa39cc8: [<600dad6b>] __btrfs_unlink_inode+0xad/0x25d 6fa39d08: [<600d7864>] btrfs_start_transaction+0xe/0x10 6fa39d48: [<600dc9ff>] btrfs_unlink_inode+0x1b/0x3b 6fa39d78: [<600e04bc>] btrfs_unlink+0x70/0xef 6fa39dc8: [<6007f0d0>] vfs_unlink+0x58/0xa3 6fa39df8: [<60080278>] do_unlinkat+0xd4/0x162 6fa39e48: [<600517db>] call_rcu_sched+0xe/0x10 6fa39e5: [<600452a8>] __put_cred+0x58/0x5a 6fa39e78: [<6007446c>] sys_faccessat+0x154/0x166 6fa39ed8: [<60080317>] sys_unlink+0x11/0x13 6fa39ee8: [<60016b80>] handle_syscall+0x58/0x70 6fa39f08: [<60021377>] userspace+0x2d4/0x381 6fa39fc8: [<60014507>] fork_handler+0x62/0x69 ---[ end trace 70b0ca2ef0266b93 ]--- http://www.mail-archive.com/linux-btrfs@vger.kernel.org/msg09302.html Signed-off-by: Sergei Trofimovich <slyfox@gentoo.org> Reviewed-by: Josef Bacik <josef@redhat.com> Signed-off-by: Chris Mason <chris.mason@oracle.com>
1 parent 8fb2764 commit 3387206

File tree

1 file changed

+11
-3
lines changed

1 file changed

+11
-3
lines changed

fs/btrfs/extent_io.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3885,17 +3885,25 @@ static void move_pages(struct page *dst_page, struct page *src_page,
38853885
kunmap_atomic(dst_kaddr, KM_USER0);
38863886
}
38873887

3888+
static inline bool areas_overlap(unsigned long src, unsigned long dst, unsigned long len)
3889+
{
3890+
unsigned long distance = (src > dst) ? src - dst : dst - src;
3891+
return distance < len;
3892+
}
3893+
38883894
static void copy_pages(struct page *dst_page, struct page *src_page,
38893895
unsigned long dst_off, unsigned long src_off,
38903896
unsigned long len)
38913897
{
38923898
char *dst_kaddr = kmap_atomic(dst_page, KM_USER0);
38933899
char *src_kaddr;
38943900

3895-
if (dst_page != src_page)
3901+
if (dst_page != src_page) {
38963902
src_kaddr = kmap_atomic(src_page, KM_USER1);
3897-
else
3903+
} else {
38983904
src_kaddr = dst_kaddr;
3905+
BUG_ON(areas_overlap(src_off, dst_off, len));
3906+
}
38993907

39003908
memcpy(dst_kaddr + dst_off, src_kaddr + src_off, len);
39013909
kunmap_atomic(dst_kaddr, KM_USER0);
@@ -3970,7 +3978,7 @@ void memmove_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset,
39703978
"len %lu len %lu\n", dst_offset, len, dst->len);
39713979
BUG_ON(1);
39723980
}
3973-
if (dst_offset < src_offset) {
3981+
if (!areas_overlap(src_offset, dst_offset, len)) {
39743982
memcpy_extent_buffer(dst, dst_offset, src_offset, len);
39753983
return;
39763984
}

0 commit comments

Comments
 (0)