diff options
author | Liu Bo <bo.li.liu@oracle.com> | 2014-09-16 05:49:30 -0400 |
---|---|---|
committer | Chris Mason <clm@fb.com> | 2014-09-17 16:46:30 -0400 |
commit | 4d1a40c66bed0b3fa43b9da5fbd5cbe332e4eccf (patch) | |
tree | a15223e0f957aebc47460b8de6cc0022bdebfd86 /fs/btrfs/file.c | |
parent | f612496bca664bff6a09a99a9a7506410b6e876e (diff) |
Btrfs: fix up bounds checking in lseek
An user reported this, it is because that lseek's SEEK_SET/SEEK_CUR/SEEK_END
allow a negative value for @offset, but btrfs's SEEK_DATA/SEEK_HOLE don't
prepare for that and convert the negative @offset into unsigned type,
so we get (end < start) warning.
[ 1269.835374] ------------[ cut here ]------------
[ 1269.836809] WARNING: CPU: 0 PID: 1241 at fs/btrfs/extent_io.c:430 insert_state+0x11d/0x140()
[ 1269.838816] BTRFS: end < start 4094 18446744073709551615
[ 1269.840334] CPU: 0 PID: 1241 Comm: a.out Tainted: G W 3.16.0+ #306
[ 1269.858229] Call Trace:
[ 1269.858612] [<ffffffff81801a69>] dump_stack+0x4e/0x68
[ 1269.858952] [<ffffffff8107894c>] warn_slowpath_common+0x8c/0xc0
[ 1269.859416] [<ffffffff81078a36>] warn_slowpath_fmt+0x46/0x50
[ 1269.859929] [<ffffffff813b0fbd>] insert_state+0x11d/0x140
[ 1269.860409] [<ffffffff813b1396>] __set_extent_bit+0x3b6/0x4e0
[ 1269.860805] [<ffffffff813b21c7>] lock_extent_bits+0x87/0x200
[ 1269.861697] [<ffffffff813a5b28>] btrfs_file_llseek+0x148/0x2a0
[ 1269.862168] [<ffffffff811f201e>] SyS_lseek+0xae/0xc0
[ 1269.862620] [<ffffffff8180b212>] system_call_fastpath+0x16/0x1b
[ 1269.862970] ---[ end trace 4d33ea885832054b ]---
This assumes that btrfs starts finding DATA/HOLE from the beginning of file
if the assigned @offset is negative.
Also we add alignment for lock_extent_bits 's range.
Reported-by: Toralf Förster <toralf.foerster@gmx.de>
Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs/btrfs/file.c')
-rw-r--r-- | fs/btrfs/file.c | 25 |
1 files changed, 15 insertions, 10 deletions
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 2287545c5498..d5d5060fe891 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c | |||
@@ -2618,23 +2618,28 @@ static int find_desired_extent(struct inode *inode, loff_t *offset, int whence) | |||
2618 | struct btrfs_root *root = BTRFS_I(inode)->root; | 2618 | struct btrfs_root *root = BTRFS_I(inode)->root; |
2619 | struct extent_map *em = NULL; | 2619 | struct extent_map *em = NULL; |
2620 | struct extent_state *cached_state = NULL; | 2620 | struct extent_state *cached_state = NULL; |
2621 | u64 lockstart = *offset; | 2621 | u64 lockstart; |
2622 | u64 lockend = i_size_read(inode); | 2622 | u64 lockend; |
2623 | u64 start = *offset; | 2623 | u64 start; |
2624 | u64 len = i_size_read(inode); | 2624 | u64 len; |
2625 | int ret = 0; | 2625 | int ret = 0; |
2626 | 2626 | ||
2627 | lockend = max_t(u64, root->sectorsize, lockend); | 2627 | if (inode->i_size == 0) |
2628 | return -ENXIO; | ||
2629 | |||
2630 | /* | ||
2631 | * *offset can be negative, in this case we start finding DATA/HOLE from | ||
2632 | * the very start of the file. | ||
2633 | */ | ||
2634 | start = max_t(loff_t, 0, *offset); | ||
2635 | |||
2636 | lockstart = round_down(start, root->sectorsize); | ||
2637 | lockend = round_up(i_size_read(inode), root->sectorsize); | ||
2628 | if (lockend <= lockstart) | 2638 | if (lockend <= lockstart) |
2629 | lockend = lockstart + root->sectorsize; | 2639 | lockend = lockstart + root->sectorsize; |
2630 | |||
2631 | lockend--; | 2640 | lockend--; |
2632 | len = lockend - lockstart + 1; | 2641 | len = lockend - lockstart + 1; |
2633 | 2642 | ||
2634 | len = max_t(u64, len, root->sectorsize); | ||
2635 | if (inode->i_size == 0) | ||
2636 | return -ENXIO; | ||
2637 | |||
2638 | lock_extent_bits(&BTRFS_I(inode)->io_tree, lockstart, lockend, 0, | 2643 | lock_extent_bits(&BTRFS_I(inode)->io_tree, lockstart, lockend, 0, |
2639 | &cached_state); | 2644 | &cached_state); |
2640 | 2645 | ||