diff options
author | David Sterba <dsterba@suse.com> | 2015-11-09 11:44:45 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-12-14 21:41:06 -0800 |
commit | f0009492b51e67fc498ab6bcc127c9b418806e71 (patch) | |
tree | 05a84a861a6a59fe95faa9dff5841f3efd82fa00 | |
parent | 82c82fadbd75f62bfb9d83eb3a308b55f4c4507d (diff) | |
download | linux-stable-f0009492b51e67fc498ab6bcc127c9b418806e71.tar.gz linux-stable-f0009492b51e67fc498ab6bcc127c9b418806e71.tar.bz2 linux-stable-f0009492b51e67fc498ab6bcc127c9b418806e71.zip |
btrfs: fix signed overflows in btrfs_sync_file
commit 9dcbeed4d7e11e1dcf5e55475de3754f0855d1c2 upstream.
The calculation of range length in btrfs_sync_file leads to signed
overflow. This was caught by PaX gcc SIZE_OVERFLOW plugin.
https://forums.grsecurity.net/viewtopic.php?f=1&t=4284
The fsync call passes 0 and LLONG_MAX, the range length does not fit to
loff_t and overflows, but the value is converted to u64 so it silently
works as expected.
The minimal fix is a typecast to u64, switching functions to take
(start, end) instead of (start, len) would be more intrusive.
Coccinelle script found that there's one more opencoded calculation of
the length.
<smpl>
@@
loff_t start, end;
@@
* end - start
</smpl>
Signed-off-by: David Sterba <dsterba@suse.com>
Signed-off-by: Chris Mason <clm@fb.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | fs/btrfs/file.c | 10 |
1 files changed, 7 insertions, 3 deletions
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 10b1471e1790..6e80b78f797f 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1876,8 +1876,13 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) struct btrfs_log_ctx ctx; int ret = 0; bool full_sync = 0; - const u64 len = end - start + 1; + u64 len; + /* + * The range length can be represented by u64, we have to do the typecasts + * to avoid signed overflow if it's [0, LLONG_MAX] eg. from fsync() + */ + len = (u64)end - (u64)start + 1; trace_btrfs_sync_file(file, datasync); /* @@ -2065,8 +2070,7 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) } } if (!full_sync) { - ret = btrfs_wait_ordered_range(inode, start, - end - start + 1); + ret = btrfs_wait_ordered_range(inode, start, len); if (ret) { btrfs_end_transaction(trans, root); goto out; |