]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
btrfs: fix signed overflows in btrfs_sync_file
authorDavid Sterba <dsterba@suse.com>
Mon, 9 Nov 2015 10:44:45 +0000 (11:44 +0100)
committerLuis Henriques <luis.henriques@canonical.com>
Tue, 5 Jan 2016 12:08:26 +0000 (12:08 +0000)
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>
[ kamal: backport to 3.13-stable: use len in both btrfs_wait_ordered_range
  calls, like
  b659ef0 Btrfs: avoid syncing log in the fast fsync path when not necessary ]
Signed-off-by: Kamal Mostafa <kamal@canonical.com>
Signed-off-by: Luis Henriques <luis.henriques@canonical.com>
fs/btrfs/file.c

index 7a71c78858351d9e17a75d1f08a7abfa39310d56..8d60cf5195788967d8d7dba750e0f68c27b34dfe 100644 (file)
@@ -1868,7 +1868,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;
+       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);
 
        /*
@@ -1896,7 +1902,7 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
        full_sync = test_bit(BTRFS_INODE_NEEDS_FULL_SYNC,
                             &BTRFS_I(inode)->runtime_flags);
        if (full_sync) {
-               ret = btrfs_wait_ordered_range(inode, start, end - start + 1);
+               ret = btrfs_wait_ordered_range(inode, start, len);
                if (ret) {
                        mutex_unlock(&inode->i_mutex);
                        goto out;
@@ -2001,8 +2007,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;