btrfs: add more checks to btrfs_read_sys_array
authorDavid Sterba <dsterba@suse.cz>
Wed, 5 Nov 2014 14:24:51 +0000 (15:24 +0100)
committerChris Mason <clm@fb.com>
Tue, 3 Feb 2015 03:24:39 +0000 (19:24 -0800)
Verify that the sys_array has enough bytes to read the next item.

Signed-off-by: David Sterba <dsterba@suse.cz>
Signed-off-by: Chris Mason <clm@fb.com>
fs/btrfs/volumes.c

index b33c83be2a978f738ee67a802d8b38dc35eeb091..2c4cab2dbd1a2da8c9f0e24ae34aede9a4346436 100644 (file)
@@ -6296,20 +6296,34 @@ int btrfs_read_sys_array(struct btrfs_root *root)
 
        while (cur_offset < array_size) {
                disk_key = (struct btrfs_disk_key *)array_ptr;
+               len = sizeof(*disk_key);
+               if (cur_offset + len > array_size)
+                       goto out_short_read;
+
                btrfs_disk_key_to_cpu(&key, disk_key);
 
-               len = sizeof(*disk_key);
                array_ptr += len;
                sb_array_offset += len;
                cur_offset += len;
 
                if (key.type == BTRFS_CHUNK_ITEM_KEY) {
                        chunk = (struct btrfs_chunk *)sb_array_offset;
+                       /*
+                        * At least one btrfs_chunk with one stripe must be
+                        * present, exact stripe count check comes afterwards
+                        */
+                       len = btrfs_chunk_item_size(1);
+                       if (cur_offset + len > array_size)
+                               goto out_short_read;
+
+                       num_stripes = btrfs_chunk_num_stripes(sb, chunk);
+                       len = btrfs_chunk_item_size(num_stripes);
+                       if (cur_offset + len > array_size)
+                               goto out_short_read;
+
                        ret = read_one_chunk(root, &key, sb, chunk);
                        if (ret)
                                break;
-                       num_stripes = btrfs_chunk_num_stripes(sb, chunk);
-                       len = btrfs_chunk_item_size(num_stripes);
                } else {
                        ret = -EIO;
                        break;
@@ -6320,6 +6334,12 @@ int btrfs_read_sys_array(struct btrfs_root *root)
        }
        free_extent_buffer(sb);
        return ret;
+
+out_short_read:
+       printk(KERN_ERR "BTRFS: sys_array too short to read %u bytes at offset %u\n",
+                       len, cur_offset);
+       free_extent_buffer(sb);
+       return -EIO;
 }
 
 int btrfs_read_chunk_tree(struct btrfs_root *root)