Btrfs: add checks to verify dir items are correct
authorJosef Bacik <josef@redhat.com>
Wed, 16 Mar 2011 20:47:17 +0000 (16:47 -0400)
committerJosef Bacik <josef@redhat.com>
Thu, 17 Mar 2011 18:21:41 +0000 (14:21 -0400)
We need to make sure the dir items we get are valid dir items.  So any time we
try and read one check it with verify_dir_item, which will do various sanity
checks to make sure it looks sane.  Thanks,

Signed-off-by: Josef Bacik <josef@redhat.com>
fs/btrfs/ctree.h
fs/btrfs/dir-item.c
fs/btrfs/inode.c
fs/btrfs/tree-log.c
fs/btrfs/xattr.c

index 841330f3d68d33ad55a2f96edc2005b18aa92be4..6036fdb88c53a4942509401bf045d5a2dc7f640b 100644 (file)
@@ -2393,6 +2393,9 @@ struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_trans_handle *trans,
                                          struct btrfs_path *path, u64 dir,
                                          const char *name, u16 name_len,
                                          int mod);
+int verify_dir_item(struct btrfs_root *root,
+                   struct extent_buffer *leaf,
+                   struct btrfs_dir_item *dir_item);
 
 /* orphan.c */
 int btrfs_insert_orphan_item(struct btrfs_trans_handle *trans,
index f0cad5ae5be75679c273583b6c6f535118acc093..02c97ad61b6dda5651199ff95d706dcb6763de3c 100644 (file)
@@ -377,6 +377,9 @@ struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_root *root,
 
        leaf = path->nodes[0];
        dir_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item);
+       if (verify_dir_item(root, leaf, dir_item))
+               return NULL;
+
        total_len = btrfs_item_size_nr(leaf, path->slots[0]);
        while (cur < total_len) {
                this_len = sizeof(*dir_item) +
@@ -429,3 +432,35 @@ int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans,
        }
        return ret;
 }
+
+int verify_dir_item(struct btrfs_root *root,
+                   struct extent_buffer *leaf,
+                   struct btrfs_dir_item *dir_item)
+{
+       u16 namelen = BTRFS_NAME_LEN;
+       u8 type = btrfs_dir_type(leaf, dir_item);
+
+       if (type >= BTRFS_FT_MAX) {
+               printk(KERN_CRIT "btrfs: invalid dir item type: %d\n",
+                      (int)type);
+               return 1;
+       }
+
+       if (type == BTRFS_FT_XATTR)
+               namelen = XATTR_NAME_MAX;
+
+       if (btrfs_dir_name_len(leaf, dir_item) > namelen) {
+               printk(KERN_CRIT "btrfS: invalid dir item name len: %u\n",
+                      (unsigned)btrfs_dir_data_len(leaf, dir_item));
+               return 1;
+       }
+
+       /* BTRFS_MAX_XATTR_SIZE is the same for all dir items */
+       if (btrfs_dir_data_len(leaf, dir_item) > BTRFS_MAX_XATTR_SIZE(root)) {
+               printk(KERN_CRIT "btrfs: invalid dir item data len: %u\n",
+                      (unsigned)btrfs_dir_data_len(leaf, dir_item));
+               return 1;
+       }
+
+       return 0;
+}
index 888dbdb3b128beec3fc3c173045da6f92c73e79a..e010000d4bc9b382bb4578a7561cc333adfdc4d8 100644 (file)
@@ -4272,6 +4272,9 @@ static int btrfs_real_readdir(struct file *filp, void *dirent,
                while (di_cur < di_total) {
                        struct btrfs_key location;
 
+                       if (verify_dir_item(root, leaf, di))
+                               break;
+
                        name_len = btrfs_dir_name_len(leaf, di);
                        if (name_len <= sizeof(tmp_name)) {
                                name_ptr = tmp_name;
index a4bbb854dfd21eea39614d5f79b66bd6d237564e..429cfcfadf90949157a6f37977ba1d1881dddd7d 100644 (file)
@@ -1286,6 +1286,8 @@ static noinline int replay_one_dir_item(struct btrfs_trans_handle *trans,
        ptr_end = ptr + item_size;
        while (ptr < ptr_end) {
                di = (struct btrfs_dir_item *)ptr;
+               if (verify_dir_item(root, eb, di))
+                       return -EIO;
                name_len = btrfs_dir_name_len(eb, di);
                ret = replay_one_name(trans, root, path, eb, di, key);
                BUG_ON(ret);
@@ -1412,6 +1414,11 @@ again:
        ptr_end = ptr + item_size;
        while (ptr < ptr_end) {
                di = (struct btrfs_dir_item *)ptr;
+               if (verify_dir_item(root, eb, di)) {
+                       ret = -EIO;
+                       goto out;
+               }
+
                name_len = btrfs_dir_name_len(eb, di);
                name = kmalloc(name_len, GFP_NOFS);
                if (!name) {
index a5776531dc2bacdbbc9ab2dc22d9e5361a1bf2f9..e5d22f280956c99a33e1f6039ca99285878fa7c4 100644 (file)
@@ -242,6 +242,8 @@ ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
                        break;
 
                di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
+               if (verify_dir_item(root, leaf, di))
+                       continue;
 
                name_len = btrfs_dir_name_len(leaf, di);
                total_size += name_len + 1;