提交 b9fc761e 编写于 作者: T Tahsin Erdogan 提交者: Theodore Ts'o

ext4: strong binding of xattr inode references

To verify that a xattr entry is not pointing to the wrong xattr inode,
we currently check that the target inode has EXT4_EA_INODE_FL flag set and
also the entry size matches the target inode size.

For stronger validation, also incorporate crc32c hash of the value into
the e_hash field. This is done regardless of whether the entry lives in
the inode body or external attribute block.
Signed-off-by: NTahsin Erdogan <tahsin@google.com>
Signed-off-by: NTheodore Ts'o <tytso@mit.edu>
上级 daf83281
...@@ -77,8 +77,8 @@ static void ext4_xattr_block_cache_insert(struct mb_cache *, ...@@ -77,8 +77,8 @@ static void ext4_xattr_block_cache_insert(struct mb_cache *,
static struct buffer_head * static struct buffer_head *
ext4_xattr_block_cache_find(struct inode *, struct ext4_xattr_header *, ext4_xattr_block_cache_find(struct inode *, struct ext4_xattr_header *,
struct mb_cache_entry **); struct mb_cache_entry **);
static void ext4_xattr_hash_entry(struct ext4_xattr_entry *entry, static __le32 ext4_xattr_hash_entry(char *name, size_t name_len, __le32 *value,
void *value_base); size_t value_count);
static void ext4_xattr_rehash(struct ext4_xattr_header *); static void ext4_xattr_rehash(struct ext4_xattr_header *);
static const struct xattr_handler * const ext4_xattr_handler_map[] = { static const struct xattr_handler * const ext4_xattr_handler_map[] = {
...@@ -380,7 +380,9 @@ static int ext4_xattr_inode_iget(struct inode *parent, unsigned long ea_ino, ...@@ -380,7 +380,9 @@ static int ext4_xattr_inode_iget(struct inode *parent, unsigned long ea_ino,
} }
static int static int
ext4_xattr_inode_verify_hash(struct inode *ea_inode, void *buffer, size_t size) ext4_xattr_inode_verify_hashes(struct inode *ea_inode,
struct ext4_xattr_entry *entry, void *buffer,
size_t size)
{ {
u32 hash; u32 hash;
...@@ -388,23 +390,35 @@ ext4_xattr_inode_verify_hash(struct inode *ea_inode, void *buffer, size_t size) ...@@ -388,23 +390,35 @@ ext4_xattr_inode_verify_hash(struct inode *ea_inode, void *buffer, size_t size)
hash = ext4_xattr_inode_hash(EXT4_SB(ea_inode->i_sb), buffer, size); hash = ext4_xattr_inode_hash(EXT4_SB(ea_inode->i_sb), buffer, size);
if (hash != ext4_xattr_inode_get_hash(ea_inode)) if (hash != ext4_xattr_inode_get_hash(ea_inode))
return -EFSCORRUPTED; return -EFSCORRUPTED;
if (entry) {
__le32 e_hash, tmp_data;
/* Verify entry hash. */
tmp_data = cpu_to_le32(hash);
e_hash = ext4_xattr_hash_entry(entry->e_name, entry->e_name_len,
&tmp_data, 1);
if (e_hash != entry->e_hash)
return -EFSCORRUPTED;
}
return 0; return 0;
} }
#define EXT4_XATTR_INODE_GET_PARENT(inode) ((__u32)(inode)->i_mtime.tv_sec) #define EXT4_XATTR_INODE_GET_PARENT(inode) ((__u32)(inode)->i_mtime.tv_sec)
/* /*
* Read the value from the EA inode. * Read xattr value from the EA inode.
*/ */
static int static int
ext4_xattr_inode_get(struct inode *inode, unsigned long ea_ino, void *buffer, ext4_xattr_inode_get(struct inode *inode, struct ext4_xattr_entry *entry,
size_t size) void *buffer, size_t size)
{ {
struct mb_cache *ea_inode_cache = EA_INODE_CACHE(inode); struct mb_cache *ea_inode_cache = EA_INODE_CACHE(inode);
struct inode *ea_inode; struct inode *ea_inode;
int err; int err;
err = ext4_xattr_inode_iget(inode, ea_ino, &ea_inode); err = ext4_xattr_inode_iget(inode, le32_to_cpu(entry->e_value_inum),
&ea_inode);
if (err) { if (err) {
ea_inode = NULL; ea_inode = NULL;
goto out; goto out;
...@@ -422,7 +436,7 @@ ext4_xattr_inode_get(struct inode *inode, unsigned long ea_ino, void *buffer, ...@@ -422,7 +436,7 @@ ext4_xattr_inode_get(struct inode *inode, unsigned long ea_ino, void *buffer,
if (err) if (err)
goto out; goto out;
err = ext4_xattr_inode_verify_hash(ea_inode, buffer, size); err = ext4_xattr_inode_verify_hashes(ea_inode, entry, buffer, size);
/* /*
* Compatibility check for old Lustre ea_inode implementation. Old * Compatibility check for old Lustre ea_inode implementation. Old
* version does not have hash validation, but it has a backpointer * version does not have hash validation, but it has a backpointer
...@@ -489,9 +503,8 @@ ext4_xattr_block_get(struct inode *inode, int name_index, const char *name, ...@@ -489,9 +503,8 @@ ext4_xattr_block_get(struct inode *inode, int name_index, const char *name,
if (size > buffer_size) if (size > buffer_size)
goto cleanup; goto cleanup;
if (entry->e_value_inum) { if (entry->e_value_inum) {
error = ext4_xattr_inode_get(inode, error = ext4_xattr_inode_get(inode, entry, buffer,
le32_to_cpu(entry->e_value_inum), size);
buffer, size);
if (error) if (error)
goto cleanup; goto cleanup;
} else { } else {
...@@ -539,9 +552,8 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name, ...@@ -539,9 +552,8 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
if (size > buffer_size) if (size > buffer_size)
goto cleanup; goto cleanup;
if (entry->e_value_inum) { if (entry->e_value_inum) {
error = ext4_xattr_inode_get(inode, error = ext4_xattr_inode_get(inode, entry, buffer,
le32_to_cpu(entry->e_value_inum), size);
buffer, size);
if (error) if (error)
goto cleanup; goto cleanup;
} else { } else {
...@@ -1400,8 +1412,8 @@ ext4_xattr_inode_cache_find(struct inode *inode, const void *value, ...@@ -1400,8 +1412,8 @@ ext4_xattr_inode_cache_find(struct inode *inode, const void *value,
(EXT4_I(ea_inode)->i_flags & EXT4_EA_INODE_FL) && (EXT4_I(ea_inode)->i_flags & EXT4_EA_INODE_FL) &&
i_size_read(ea_inode) == value_len && i_size_read(ea_inode) == value_len &&
!ext4_xattr_inode_read(ea_inode, ea_data, value_len) && !ext4_xattr_inode_read(ea_inode, ea_data, value_len) &&
!ext4_xattr_inode_verify_hash(ea_inode, ea_data, !ext4_xattr_inode_verify_hashes(ea_inode, NULL, ea_data,
value_len) && value_len) &&
!memcmp(value, ea_data, value_len)) { !memcmp(value, ea_data, value_len)) {
mb_cache_entry_touch(ea_inode_cache, ce); mb_cache_entry_touch(ea_inode_cache, ce);
mb_cache_entry_put(ea_inode_cache, ce); mb_cache_entry_put(ea_inode_cache, ce);
...@@ -1665,12 +1677,36 @@ static int ext4_xattr_set_entry(struct ext4_xattr_info *i, ...@@ -1665,12 +1677,36 @@ static int ext4_xattr_set_entry(struct ext4_xattr_info *i,
here->e_value_size = cpu_to_le32(i->value_len); here->e_value_size = cpu_to_le32(i->value_len);
} }
if (is_block) { if (i->value) {
if (i->value) __le32 hash = 0;
ext4_xattr_hash_entry(here, s->base);
ext4_xattr_rehash((struct ext4_xattr_header *)s->base); /* Entry hash calculation. */
if (in_inode) {
__le32 crc32c_hash;
/*
* Feed crc32c hash instead of the raw value for entry
* hash calculation. This is to avoid walking
* potentially long value buffer again.
*/
crc32c_hash = cpu_to_le32(
ext4_xattr_inode_get_hash(new_ea_inode));
hash = ext4_xattr_hash_entry(here->e_name,
here->e_name_len,
&crc32c_hash, 1);
} else if (is_block) {
__le32 *value = s->base + min_offs - new_size;
hash = ext4_xattr_hash_entry(here->e_name,
here->e_name_len, value,
new_size >> 2);
}
here->e_hash = hash;
} }
if (is_block)
ext4_xattr_rehash((struct ext4_xattr_header *)s->base);
ret = 0; ret = 0;
out: out:
iput(old_ea_inode); iput(old_ea_inode);
...@@ -2452,9 +2488,7 @@ static int ext4_xattr_move_to_block(handle_t *handle, struct inode *inode, ...@@ -2452,9 +2488,7 @@ static int ext4_xattr_move_to_block(handle_t *handle, struct inode *inode,
/* Save the entry name and the entry value */ /* Save the entry name and the entry value */
if (entry->e_value_inum) { if (entry->e_value_inum) {
error = ext4_xattr_inode_get(inode, error = ext4_xattr_inode_get(inode, entry, buffer, value_size);
le32_to_cpu(entry->e_value_inum),
buffer, value_size);
if (error) if (error)
goto out; goto out;
} else { } else {
...@@ -2944,30 +2978,22 @@ ext4_xattr_block_cache_find(struct inode *inode, ...@@ -2944,30 +2978,22 @@ ext4_xattr_block_cache_find(struct inode *inode,
* *
* Compute the hash of an extended attribute. * Compute the hash of an extended attribute.
*/ */
static void ext4_xattr_hash_entry(struct ext4_xattr_entry *entry, static __le32 ext4_xattr_hash_entry(char *name, size_t name_len, __le32 *value,
void *value_base) size_t value_count)
{ {
__u32 hash = 0; __u32 hash = 0;
char *name = entry->e_name;
int n;
for (n = 0; n < entry->e_name_len; n++) { while (name_len--) {
hash = (hash << NAME_HASH_SHIFT) ^ hash = (hash << NAME_HASH_SHIFT) ^
(hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^ (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^
*name++; *name++;
} }
while (value_count--) {
if (!entry->e_value_inum && entry->e_value_size) { hash = (hash << VALUE_HASH_SHIFT) ^
__le32 *value = (__le32 *)((char *)value_base + (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^
le16_to_cpu(entry->e_value_offs)); le32_to_cpu(*value++);
for (n = (le32_to_cpu(entry->e_value_size) +
EXT4_XATTR_ROUND) >> EXT4_XATTR_PAD_BITS; n; n--) {
hash = (hash << VALUE_HASH_SHIFT) ^
(hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^
le32_to_cpu(*value++);
}
} }
entry->e_hash = cpu_to_le32(hash); return cpu_to_le32(hash);
} }
#undef NAME_HASH_SHIFT #undef NAME_HASH_SHIFT
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册