add patch check-EA-value-offset-when-loading
[ext4-patch-queue.git] / check-EA-value-offset-when-loading
blob7b8f27347968d143eadae997621ea396cd40f09b
1 ext4: check EA value offset when loading
3 From: "Darrick J. Wong" <darrick.wong@oracle.com>
5 When loading extended attributes, check each entry's value offset to
6 make sure it doesn't collide with the entries.
8 Without this check it is easy to crash the kernel by mounting a
9 malicious FS containing a file with an EA wherein e_value_offs = 0 and
10 e_value_size > 0 and then deleting the EA, which corrupts the name
11 list.
13 (See the f_ea_value_crash test's FS image in e2fsprogs for an example.)
15 Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
16 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
17 Cc: stable@vger.kernel.org
18 ---
19  fs/ext4/xattr.c |   34 +++++++++++++++++++++++++---------
20  1 file changed, 25 insertions(+), 9 deletions(-)
23 diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
24 index e738733..c11738e 100644
25 --- a/fs/ext4/xattr.c
26 +++ b/fs/ext4/xattr.c
27 @@ -189,15 +189,29 @@ ext4_listxattr(struct dentry *dentry, char *buffer, size_t size)
28         return ext4_xattr_list(dentry, buffer, size);
29  }
31 -static int
32 -ext4_xattr_check_names(struct ext4_xattr_entry *entry, void *end)
33 +int
34 +ext4_xattr_check_names(struct ext4_xattr_entry *entry, void *end,
35 +                      void *value_start)
36  {
37 -       while (!IS_LAST_ENTRY(entry)) {
38 -               struct ext4_xattr_entry *next = EXT4_XATTR_NEXT(entry);
39 +       struct ext4_xattr_entry *e = entry;
41 +       while (!IS_LAST_ENTRY(e)) {
42 +               struct ext4_xattr_entry *next = EXT4_XATTR_NEXT(e);
43                 if ((void *)next >= end)
44                         return -EIO;
45 -               entry = next;
46 +               e = next;
47         }
49 +       while (!IS_LAST_ENTRY(entry)) {
50 +               if (entry->e_value_size != 0 &&
51 +                   (value_start + le16_to_cpu(entry->e_value_offs) <
52 +                    (void *)e + sizeof(__u32) ||
53 +                    value_start + le16_to_cpu(entry->e_value_offs) +
54 +                   le32_to_cpu(entry->e_value_size) > end))
55 +                       return -EIO;
56 +               entry = EXT4_XATTR_NEXT(entry);
57 +       }
59         return 0;
60  }
62 @@ -214,7 +228,8 @@ ext4_xattr_check_block(struct inode *inode, struct buffer_head *bh)
63                 return -EIO;
64         if (!ext4_xattr_block_csum_verify(inode, bh->b_blocknr, BHDR(bh)))
65                 return -EIO;
66 -       error = ext4_xattr_check_names(BFIRST(bh), bh->b_data + bh->b_size);
67 +       error = ext4_xattr_check_names(BFIRST(bh), bh->b_data + bh->b_size,
68 +                                      bh->b_data);
69         if (!error)
70                 set_buffer_verified(bh);
71         return error;
72 @@ -331,7 +346,7 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
73         header = IHDR(inode, raw_inode);
74         entry = IFIRST(header);
75         end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
76 -       error = ext4_xattr_check_names(entry, end);
77 +       error = ext4_xattr_check_names(entry, end, entry);
78         if (error)
79                 goto cleanup;
80         error = ext4_xattr_find_entry(&entry, name_index, name,
81 @@ -463,7 +478,7 @@ ext4_xattr_ibody_list(struct dentry *dentry, char *buffer, size_t buffer_size)
82         raw_inode = ext4_raw_inode(&iloc);
83         header = IHDR(inode, raw_inode);
84         end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
85 -       error = ext4_xattr_check_names(IFIRST(header), end);
86 +       error = ext4_xattr_check_names(IFIRST(header), end, IFIRST(header));
87         if (error)
88                 goto cleanup;
89         error = ext4_xattr_list_entries(dentry, IFIRST(header),
90 @@ -986,7 +1001,8 @@ int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
91         is->s.here = is->s.first;
92         is->s.end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
93         if (ext4_test_inode_state(inode, EXT4_STATE_XATTR)) {
94 -               error = ext4_xattr_check_names(IFIRST(header), is->s.end);
95 +               error = ext4_xattr_check_names(IFIRST(header), is->s.end,
96 +                                              IFIRST(header));
97                 if (error)
98                         return error;
99                 /* Find the named attribute. */