add patch fix-ext4_new_inode-journal-credits-calculation
[ext4-patch-queue.git] / strong-binding-of-xattr-inode-references
blobe98eccad23306e810681d11c878252aa768e64c3
1 ext4: strong binding of xattr inode references
3 From: Tahsin Erdogan <tahsin@google.com>
5 To verify that a xattr entry is not pointing to the wrong xattr inode,
6 we currently check that the target inode has EXT4_EA_INODE_FL flag set and
7 also the entry size matches the target inode size.
9 For stronger validation, also incorporate crc32c hash of the value into
10 the e_hash field. This is done regardless of whether the entry lives in
11 the inode body or external attribute block.
13 Signed-off-by: Tahsin Erdogan <tahsin@google.com>
14 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
15 ---
16 v2: naming updates to adapt to other patch changes in the series
18  fs/ext4/xattr.c | 104 +++++++++++++++++++++++++++++++++++---------------------
19  1 file changed, 65 insertions(+), 39 deletions(-)
21 diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
22 index 9cb15f2069bd..3f16dc979012 100644
23 --- a/fs/ext4/xattr.c
24 +++ b/fs/ext4/xattr.c
25 @@ -77,8 +77,8 @@ static void ext4_xattr_block_cache_insert(struct mb_cache *,
26  static struct buffer_head *
27  ext4_xattr_block_cache_find(struct inode *, struct ext4_xattr_header *,
28                             struct mb_cache_entry **);
29 -static void ext4_xattr_hash_entry(struct ext4_xattr_entry *entry,
30 -                                 void *value_base);
31 +static __le32 ext4_xattr_hash_entry(char *name, size_t name_len, __le32 *value,
32 +                                   size_t value_count);
33  static void ext4_xattr_rehash(struct ext4_xattr_header *);
35  static const struct xattr_handler * const ext4_xattr_handler_map[] = {
36 @@ -380,7 +380,9 @@ static int ext4_xattr_inode_iget(struct inode *parent, unsigned long ea_ino,
37  }
39  static int
40 -ext4_xattr_inode_verify_hash(struct inode *ea_inode, void *buffer, size_t size)
41 +ext4_xattr_inode_verify_hashes(struct inode *ea_inode,
42 +                              struct ext4_xattr_entry *entry, void *buffer,
43 +                              size_t size)
44  {
45         u32 hash;
47 @@ -388,23 +390,35 @@ ext4_xattr_inode_verify_hash(struct inode *ea_inode, void *buffer, size_t size)
48         hash = ext4_xattr_inode_hash(EXT4_SB(ea_inode->i_sb), buffer, size);
49         if (hash != ext4_xattr_inode_get_hash(ea_inode))
50                 return -EFSCORRUPTED;
52 +       if (entry) {
53 +               __le32 e_hash, tmp_data;
55 +               /* Verify entry hash. */
56 +               tmp_data = cpu_to_le32(hash);
57 +               e_hash = ext4_xattr_hash_entry(entry->e_name, entry->e_name_len,
58 +                                              &tmp_data, 1);
59 +               if (e_hash != entry->e_hash)
60 +                       return -EFSCORRUPTED;
61 +       }
62         return 0;
63  }
65  #define EXT4_XATTR_INODE_GET_PARENT(inode) ((__u32)(inode)->i_mtime.tv_sec)
67  /*
68 - * Read the value from the EA inode.
69 + * Read xattr value from the EA inode.
70   */
71  static int
72 -ext4_xattr_inode_get(struct inode *inode, unsigned long ea_ino, void *buffer,
73 -                    size_t size)
74 +ext4_xattr_inode_get(struct inode *inode, struct ext4_xattr_entry *entry,
75 +                    void *buffer, size_t size)
76  {
77         struct mb_cache *ea_inode_cache = EA_INODE_CACHE(inode);
78         struct inode *ea_inode;
79         int err;
81 -       err = ext4_xattr_inode_iget(inode, ea_ino, &ea_inode);
82 +       err = ext4_xattr_inode_iget(inode, le32_to_cpu(entry->e_value_inum),
83 +                                   &ea_inode);
84         if (err) {
85                 ea_inode = NULL;
86                 goto out;
87 @@ -422,7 +436,7 @@ ext4_xattr_inode_get(struct inode *inode, unsigned long ea_ino, void *buffer,
88         if (err)
89                 goto out;
91 -       err = ext4_xattr_inode_verify_hash(ea_inode, buffer, size);
92 +       err = ext4_xattr_inode_verify_hashes(ea_inode, entry, buffer, size);
93         /*
94          * Compatibility check for old Lustre ea_inode implementation. Old
95          * version does not have hash validation, but it has a backpointer
96 @@ -489,9 +503,8 @@ ext4_xattr_block_get(struct inode *inode, int name_index, const char *name,
97                 if (size > buffer_size)
98                         goto cleanup;
99                 if (entry->e_value_inum) {
100 -                       error = ext4_xattr_inode_get(inode,
101 -                                            le32_to_cpu(entry->e_value_inum),
102 -                                            buffer, size);
103 +                       error = ext4_xattr_inode_get(inode, entry, buffer,
104 +                                                    size);
105                         if (error)
106                                 goto cleanup;
107                 } else {
108 @@ -539,9 +552,8 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
109                 if (size > buffer_size)
110                         goto cleanup;
111                 if (entry->e_value_inum) {
112 -                       error = ext4_xattr_inode_get(inode,
113 -                                            le32_to_cpu(entry->e_value_inum),
114 -                                            buffer, size);
115 +                       error = ext4_xattr_inode_get(inode, entry, buffer,
116 +                                                    size);
117                         if (error)
118                                 goto cleanup;
119                 } else {
120 @@ -1387,8 +1399,8 @@ ext4_xattr_inode_cache_find(struct inode *inode, const void *value,
121                     (EXT4_I(ea_inode)->i_flags & EXT4_EA_INODE_FL) &&
122                     i_size_read(ea_inode) == value_len &&
123                     !ext4_xattr_inode_read(ea_inode, ea_data, value_len) &&
124 -                   !ext4_xattr_inode_verify_hash(ea_inode, ea_data,
125 -                                                 value_len) &&
126 +                   !ext4_xattr_inode_verify_hashes(ea_inode, NULL, ea_data,
127 +                                                   value_len) &&
128                     !memcmp(value, ea_data, value_len)) {
129                         mb_cache_entry_touch(ea_inode_cache, ce);
130                         mb_cache_entry_put(ea_inode_cache, ce);
131 @@ -1652,12 +1664,36 @@ static int ext4_xattr_set_entry(struct ext4_xattr_info *i,
132                 here->e_value_size = cpu_to_le32(i->value_len);
133         }
135 -       if (is_block) {
136 -               if (i->value)
137 -                       ext4_xattr_hash_entry(here, s->base);
138 -               ext4_xattr_rehash((struct ext4_xattr_header *)s->base);
139 +       if (i->value) {
140 +               __le32 hash = 0;
142 +               /* Entry hash calculation. */
143 +               if (in_inode) {
144 +                       __le32 crc32c_hash;
146 +                       /*
147 +                        * Feed crc32c hash instead of the raw value for entry
148 +                        * hash calculation. This is to avoid walking
149 +                        * potentially long value buffer again.
150 +                        */
151 +                       crc32c_hash = cpu_to_le32(
152 +                                      ext4_xattr_inode_get_hash(new_ea_inode));
153 +                       hash = ext4_xattr_hash_entry(here->e_name,
154 +                                                    here->e_name_len,
155 +                                                    &crc32c_hash, 1);
156 +               } else if (is_block) {
157 +                       __le32 *value = s->base + min_offs - new_size;
159 +                       hash = ext4_xattr_hash_entry(here->e_name,
160 +                                                    here->e_name_len, value,
161 +                                                    new_size >> 2);
162 +               }
163 +               here->e_hash = hash;
164         }
166 +       if (is_block)
167 +               ext4_xattr_rehash((struct ext4_xattr_header *)s->base);
169         ret = 0;
170  out:
171         iput(old_ea_inode);
172 @@ -2439,9 +2475,7 @@ static int ext4_xattr_move_to_block(handle_t *handle, struct inode *inode,
174         /* Save the entry name and the entry value */
175         if (entry->e_value_inum) {
176 -               error = ext4_xattr_inode_get(inode,
177 -                                            le32_to_cpu(entry->e_value_inum),
178 -                                            buffer, value_size);
179 +               error = ext4_xattr_inode_get(inode, entry, buffer, value_size);
180                 if (error)
181                         goto out;
182         } else {
183 @@ -2931,30 +2965,22 @@ ext4_xattr_block_cache_find(struct inode *inode,
184   *
185   * Compute the hash of an extended attribute.
186   */
187 -static void ext4_xattr_hash_entry(struct ext4_xattr_entry *entry,
188 -                                 void *value_base)
189 +static __le32 ext4_xattr_hash_entry(char *name, size_t name_len, __le32 *value,
190 +                                   size_t value_count)
192         __u32 hash = 0;
193 -       char *name = entry->e_name;
194 -       int n;
196 -       for (n = 0; n < entry->e_name_len; n++) {
197 +       while (name_len--) {
198                 hash = (hash << NAME_HASH_SHIFT) ^
199                        (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^
200                        *name++;
201         }
203 -       if (!entry->e_value_inum && entry->e_value_size) {
204 -               __le32 *value = (__le32 *)((char *)value_base +
205 -                       le16_to_cpu(entry->e_value_offs));
206 -               for (n = (le32_to_cpu(entry->e_value_size) +
207 -                    EXT4_XATTR_ROUND) >> EXT4_XATTR_PAD_BITS; n; n--) {
208 -                       hash = (hash << VALUE_HASH_SHIFT) ^
209 -                              (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^
210 -                              le32_to_cpu(*value++);
211 -               }
212 +       while (value_count--) {
213 +               hash = (hash << VALUE_HASH_SHIFT) ^
214 +                      (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^
215 +                      le32_to_cpu(*value++);
216         }
217 -       entry->e_hash = cpu_to_le32(hash);
218 +       return cpu_to_le32(hash);
221  #undef NAME_HASH_SHIFT
222 -- 
223 2.13.1.611.g7e3b11ae1-goog