Commit f3bbac32 authored by Linus Torvalds's avatar Linus Torvalds

ext4: deal with legacy signed xattr name hash values

We potentially have old hashes of the xattr names generated on systems
with signed 'char' types.  Now that everybody uses '-funsigned-char',
those hashes will no longer match.

This only happens if you use xattrs names that have the high bit set,
which probably doesn't happen in practice, but the xfstest generic/454
shows it.

Instead of adding a new "signed xattr hash filesystem" bit and having to
deal with all the possible combinations, just calculate the hash both
ways if the first one fails, and always generate new hashes with the
proper unsigned char version.
Reported-by: default avatarkernel test robot <oliver.sang@intel.com>
Link: https://lore.kernel.org/oe-lkp/202212291509.704a11c9-oliver.sang@intel.com
Link: https://lore.kernel.org/all/CAHk-=whUNjwqZXa-MH9KMmc_CpQpoFKFjAB9ZKHuu=TbsouT4A@mail.gmail.com/
Exposed-by: 3bc753c0 ("kbuild: treat char as always unsigned")
Cc: Eric Biggers <ebiggers@kernel.org>
Cc: Andreas Dilger <adilger@dilger.ca>
Cc: Theodore Ts'o <tytso@mit.edu>,
Cc: Jason Donenfeld <Jason@zx2c4.com>
Cc: Masahiro Yamada <masahiroy@kernel.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent f883675b
...@@ -81,6 +81,8 @@ ext4_xattr_block_cache_find(struct inode *, struct ext4_xattr_header *, ...@@ -81,6 +81,8 @@ ext4_xattr_block_cache_find(struct inode *, struct ext4_xattr_header *,
struct mb_cache_entry **); struct mb_cache_entry **);
static __le32 ext4_xattr_hash_entry(char *name, size_t name_len, __le32 *value, static __le32 ext4_xattr_hash_entry(char *name, size_t name_len, __le32 *value,
size_t value_count); size_t value_count);
static __le32 ext4_xattr_hash_entry_signed(char *name, size_t name_len, __le32 *value,
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[] = {
...@@ -470,8 +472,21 @@ ext4_xattr_inode_verify_hashes(struct inode *ea_inode, ...@@ -470,8 +472,21 @@ ext4_xattr_inode_verify_hashes(struct inode *ea_inode,
tmp_data = cpu_to_le32(hash); tmp_data = cpu_to_le32(hash);
e_hash = ext4_xattr_hash_entry(entry->e_name, entry->e_name_len, e_hash = ext4_xattr_hash_entry(entry->e_name, entry->e_name_len,
&tmp_data, 1); &tmp_data, 1);
if (e_hash != entry->e_hash) /* All good? */
return -EFSCORRUPTED; if (e_hash == entry->e_hash)
return 0;
/*
* Not good. Maybe the entry hash was calculated
* using the buggy signed char version?
*/
e_hash = ext4_xattr_hash_entry_signed(entry->e_name, entry->e_name_len,
&tmp_data, 1);
if (e_hash == entry->e_hash)
return 0;
/* Still no match - bad */
return -EFSCORRUPTED;
} }
return 0; return 0;
} }
...@@ -3091,6 +3106,28 @@ static __le32 ext4_xattr_hash_entry(char *name, size_t name_len, __le32 *value, ...@@ -3091,6 +3106,28 @@ static __le32 ext4_xattr_hash_entry(char *name, size_t name_len, __le32 *value,
return cpu_to_le32(hash); return cpu_to_le32(hash);
} }
/*
* ext4_xattr_hash_entry_signed()
*
* Compute the hash of an extended attribute incorrectly.
*/
static __le32 ext4_xattr_hash_entry_signed(char *name, size_t name_len, __le32 *value, size_t value_count)
{
__u32 hash = 0;
while (name_len--) {
hash = (hash << NAME_HASH_SHIFT) ^
(hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^
(signed char)*name++;
}
while (value_count--) {
hash = (hash << VALUE_HASH_SHIFT) ^
(hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^
le32_to_cpu(*value++);
}
return cpu_to_le32(hash);
}
#undef NAME_HASH_SHIFT #undef NAME_HASH_SHIFT
#undef VALUE_HASH_SHIFT #undef VALUE_HASH_SHIFT
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment