* [PATCH 1/4] btrfs: more superblock checks, lower bounds on devices and sectorsize/nodesize
2014-11-07 16:47 [PATCH 0/4] More mount-time checks David Sterba
@ 2014-11-07 16:47 ` David Sterba
2014-11-07 16:47 ` [PATCH 2/4] btrfs: add checks for sys_chunk_array sizes David Sterba
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: David Sterba @ 2014-11-07 16:47 UTC (permalink / raw)
To: linux-btrfs; +Cc: David Sterba
I received a few crafted images from Jiri, all got through the recently
added superblock checks. The lower bounds checks for num_devices and
sector/node -sizes were missing and caused a crash during mount.
Tools for symbolic code execution were used to prepare the images
contents.
Reported-by: Jiri Slaby <jslaby@suse.cz>
Signed-off-by: David Sterba <dsterba@suse.cz>
---
fs/btrfs/disk-io.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 7af9a1978a2f..e97fa11488f1 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -3847,6 +3847,21 @@ static int btrfs_check_super_valid(struct btrfs_fs_info *fs_info,
printk(KERN_WARNING "BTRFS: log_root block unaligned: %llu\n",
btrfs_super_log_root(sb));
+ /*
+ * Check the lower bound, the alignment and other constraints are
+ * checked later.
+ */
+ if (btrfs_super_nodesize(sb) < 4096) {
+ printk(KERN_ERR "BTRFS: nodesize too small: %llu < 4096\n",
+ btrfs_super_nodesize(sb));
+ ret = -EINVAL;
+ }
+ if (btrfs_super_sectorsize(sb) < 4096) {
+ printk(KERN_ERR "BTRFS: sectorsize too small: %llu < 4096\n",
+ btrfs_super_sectorsize(sb));
+ ret = -EINVAL;
+ }
+
if (memcmp(fs_info->fsid, sb->dev_item.fsid, BTRFS_UUID_SIZE) != 0) {
printk(KERN_ERR "BTRFS: dev_item UUID does not match fsid: %pU != %pU\n",
fs_info->fsid, sb->dev_item.fsid);
@@ -3860,6 +3875,10 @@ static int btrfs_check_super_valid(struct btrfs_fs_info *fs_info,
if (btrfs_super_num_devices(sb) > (1UL << 31))
printk(KERN_WARNING "BTRFS: suspicious number of devices: %llu\n",
btrfs_super_num_devices(sb));
+ if (btrfs_super_num_devices(sb) == 0) {
+ printk(KERN_ERR "BTRFS: number of devices is 0\n");
+ ret = -EINVAL;
+ }
if (btrfs_super_bytenr(sb) != BTRFS_SUPER_INFO_OFFSET) {
printk(KERN_ERR "BTRFS: super offset mismatch %llu != %u\n",
--
1.8.4.5
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 2/4] btrfs: add checks for sys_chunk_array sizes
2014-11-07 16:47 [PATCH 0/4] More mount-time checks David Sterba
2014-11-07 16:47 ` [PATCH 1/4] btrfs: more superblock checks, lower bounds on devices and sectorsize/nodesize David Sterba
@ 2014-11-07 16:47 ` David Sterba
2014-11-07 16:47 ` [PATCH 3/4] btrfs: cleanup, rename a few variables in btrfs_read_sys_array David Sterba
2014-11-07 16:47 ` [PATCH 4/4] btrfs: add more checks to btrfs_read_sys_array David Sterba
3 siblings, 0 replies; 5+ messages in thread
From: David Sterba @ 2014-11-07 16:47 UTC (permalink / raw)
To: linux-btrfs; +Cc: David Sterba
Verify that possible minimum and maximum size is set, validity of
contents is checked in btrfs_read_sys_array.
Signed-off-by: David Sterba <dsterba@suse.cz>
---
fs/btrfs/disk-io.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index e97fa11488f1..77f205d82ae9 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -3887,6 +3887,25 @@ static int btrfs_check_super_valid(struct btrfs_fs_info *fs_info,
}
/*
+ * Obvious sys_chunk_array corruptions, it must hold at least one key
+ * and one chunk
+ */
+ if (btrfs_super_sys_array_size(sb) > BTRFS_SYSTEM_CHUNK_ARRAY_SIZE) {
+ printk(KERN_ERR "BTRFS: system chunk array too big %llu > %u\n",
+ btrfs_super_sys_array_size(sb),
+ BTRFS_SYSTEM_CHUNK_ARRAY_SIZE);
+ ret = -EINVAL;
+ }
+ if (btrfs_super_sys_array_size(sb) < sizeof(struct btrfs_disk_key)
+ + sizeof(struct btrfs_chunk)) {
+ printk(KERN_ERR "BTRFS: system chunk array too small %llu < %u\n",
+ btrfs_super_sys_array_size(sb),
+ sizeof(struct btrfs_disk_key)
+ + sizeof(struct btrfs_chunk));
+ ret = -EINVAL;
+ }
+
+ /*
* The generation is a global counter, we'll trust it more than the others
* but it's still possible that it's the one that's wrong.
*/
--
1.8.4.5
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 3/4] btrfs: cleanup, rename a few variables in btrfs_read_sys_array
2014-11-07 16:47 [PATCH 0/4] More mount-time checks David Sterba
2014-11-07 16:47 ` [PATCH 1/4] btrfs: more superblock checks, lower bounds on devices and sectorsize/nodesize David Sterba
2014-11-07 16:47 ` [PATCH 2/4] btrfs: add checks for sys_chunk_array sizes David Sterba
@ 2014-11-07 16:47 ` David Sterba
2014-11-07 16:47 ` [PATCH 4/4] btrfs: add more checks to btrfs_read_sys_array David Sterba
3 siblings, 0 replies; 5+ messages in thread
From: David Sterba @ 2014-11-07 16:47 UTC (permalink / raw)
To: linux-btrfs; +Cc: David Sterba
There's a pointer to buffer, integer offset and offset passed as
pointer, try to find matching names for them.
Signed-off-by: David Sterba <dsterba@suse.cz>
---
fs/btrfs/volumes.c | 31 ++++++++++++++++---------------
1 file changed, 16 insertions(+), 15 deletions(-)
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index d47289c715c8..fbdfed2e0ba8 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -6212,13 +6212,13 @@ int btrfs_read_sys_array(struct btrfs_root *root)
struct extent_buffer *sb;
struct btrfs_disk_key *disk_key;
struct btrfs_chunk *chunk;
- u8 *ptr;
- unsigned long sb_ptr;
+ u8 *array_ptr;
+ unsigned long sb_array_offset;
int ret = 0;
u32 num_stripes;
u32 array_size;
u32 len = 0;
- u32 cur;
+ u32 cur_offset;
struct btrfs_key key;
sb = btrfs_find_create_tree_block(root, BTRFS_SUPER_INFO_OFFSET,
@@ -6245,20 +6245,21 @@ int btrfs_read_sys_array(struct btrfs_root *root)
write_extent_buffer(sb, super_copy, 0, BTRFS_SUPER_INFO_SIZE);
array_size = btrfs_super_sys_array_size(super_copy);
- ptr = super_copy->sys_chunk_array;
- sb_ptr = offsetof(struct btrfs_super_block, sys_chunk_array);
- cur = 0;
+ array_ptr = super_copy->sys_chunk_array;
+ sb_array_offset = offsetof(struct btrfs_super_block, sys_chunk_array);
+ cur_offset = 0;
- while (cur < array_size) {
- disk_key = (struct btrfs_disk_key *)ptr;
+ while (cur_offset < array_size) {
+ disk_key = (struct btrfs_disk_key *)array_ptr;
btrfs_disk_key_to_cpu(&key, disk_key);
- len = sizeof(*disk_key); ptr += len;
- sb_ptr += len;
- cur += len;
+ 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_ptr;
+ chunk = (struct btrfs_chunk *)sb_array_offset;
ret = read_one_chunk(root, &key, sb, chunk);
if (ret)
break;
@@ -6268,9 +6269,9 @@ int btrfs_read_sys_array(struct btrfs_root *root)
ret = -EIO;
break;
}
- ptr += len;
- sb_ptr += len;
- cur += len;
+ array_ptr += len;
+ sb_array_offset += len;
+ cur_offset += len;
}
free_extent_buffer(sb);
return ret;
--
1.8.4.5
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 4/4] btrfs: add more checks to btrfs_read_sys_array
2014-11-07 16:47 [PATCH 0/4] More mount-time checks David Sterba
` (2 preceding siblings ...)
2014-11-07 16:47 ` [PATCH 3/4] btrfs: cleanup, rename a few variables in btrfs_read_sys_array David Sterba
@ 2014-11-07 16:47 ` David Sterba
3 siblings, 0 replies; 5+ messages in thread
From: David Sterba @ 2014-11-07 16:47 UTC (permalink / raw)
To: linux-btrfs; +Cc: David Sterba
Verify that the sys_array has enough bytes to read the next item.
Signed-off-by: David Sterba <dsterba@suse.cz>
---
fs/btrfs/volumes.c | 26 +++++++++++++++++++++++---
1 file changed, 23 insertions(+), 3 deletions(-)
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index fbdfed2e0ba8..92f4f011882e 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -6251,20 +6251,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;
@@ -6275,6 +6289,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)
--
1.8.4.5
^ permalink raw reply related [flat|nested] 5+ messages in thread