btrfs-progs: check: do early check for read_tree_block
authorQu Wenruo <quwenruo@cn.fujitsu.com>
Tue, 30 Aug 2016 03:29:32 +0000 (11:29 +0800)
committerDavid Sterba <dsterba@suse.com>
Mon, 5 Sep 2016 08:04:24 +0000 (10:04 +0200)
Although we have enhanced read_tree_block() from a lot of different
aspects, it lacks the early bytenr/blocksize alignment check.

And the lack of such check can lead to strange use-after-free bugs, due
to the fact that alloc_extent_buffer() will free overlapping extent
buffers, and allocate new eb for the usage.

So we should not allow invalid bytenr/blocksize even passed to
btrfs_find_create_tree_block().

This patch will add such check so we won't trigger use-after-free bug
then.

Reported-by: Lukas Lueg <lukas.lueg@gmail.com>
Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
Signed-off-by: David Sterba <dsterba@suse.com>
disk-io.c

index ca4578f..f5340c3 100644 (file)
--- a/disk-io.c
+++ b/disk-io.c
@@ -313,11 +313,29 @@ struct extent_buffer* read_tree_block_fs_info(
        int ret;
        struct extent_buffer *eb;
        u64 best_transid = 0;
+       u32 sectorsize = btrfs_super_sectorsize(fs_info->super_copy);
+       u32 nodesize = btrfs_super_nodesize(fs_info->super_copy);
        int mirror_num = 0;
        int good_mirror = 0;
        int num_copies;
        int ignore = 0;
 
+       /*
+        * Don't even try to create tree block for unaligned tree block
+        * bytenr.
+        * Such unaligned tree block will free overlapping extent buffer,
+        * causing use-after-free bugs for fuzzed images.
+        */
+       if (!IS_ALIGNED(bytenr, sectorsize)) {
+               error("tree block bytenr %llu is not aligned to sectorsize %u",
+                     bytenr, sectorsize);
+               return ERR_PTR(-EIO);
+       }
+       if (!IS_ALIGNED(blocksize, nodesize)) {
+               error("tree block size %u is not aligned to nodesize %u",
+                     blocksize, nodesize);
+               return ERR_PTR(-EIO);
+       }
        eb = btrfs_find_create_tree_block(fs_info, bytenr, blocksize);
        if (!eb)
                return ERR_PTR(-ENOMEM);