Skip to content

Commit f3bbac3

Browse files
committed
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: kernel 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: 3bc753c ("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: Linus Torvalds <torvalds@linux-foundation.org>
1 parent f883675 commit f3bbac3

File tree

1 file changed

+39
-2
lines changed

1 file changed

+39
-2
lines changed

fs/ext4/xattr.c

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ ext4_xattr_block_cache_find(struct inode *, struct ext4_xattr_header *,
8181
struct mb_cache_entry **);
8282
static __le32 ext4_xattr_hash_entry(char *name, size_t name_len, __le32 *value,
8383
size_t value_count);
84+
static __le32 ext4_xattr_hash_entry_signed(char *name, size_t name_len, __le32 *value,
85+
size_t value_count);
8486
static void ext4_xattr_rehash(struct ext4_xattr_header *);
8587

8688
static const struct xattr_handler * const ext4_xattr_handler_map[] = {
@@ -470,8 +472,21 @@ ext4_xattr_inode_verify_hashes(struct inode *ea_inode,
470472
tmp_data = cpu_to_le32(hash);
471473
e_hash = ext4_xattr_hash_entry(entry->e_name, entry->e_name_len,
472474
&tmp_data, 1);
473-
if (e_hash != entry->e_hash)
474-
return -EFSCORRUPTED;
475+
/* All good? */
476+
if (e_hash == entry->e_hash)
477+
return 0;
478+
479+
/*
480+
* Not good. Maybe the entry hash was calculated
481+
* using the buggy signed char version?
482+
*/
483+
e_hash = ext4_xattr_hash_entry_signed(entry->e_name, entry->e_name_len,
484+
&tmp_data, 1);
485+
if (e_hash == entry->e_hash)
486+
return 0;
487+
488+
/* Still no match - bad */
489+
return -EFSCORRUPTED;
475490
}
476491
return 0;
477492
}
@@ -3091,6 +3106,28 @@ static __le32 ext4_xattr_hash_entry(char *name, size_t name_len, __le32 *value,
30913106
return cpu_to_le32(hash);
30923107
}
30933108

3109+
/*
3110+
* ext4_xattr_hash_entry_signed()
3111+
*
3112+
* Compute the hash of an extended attribute incorrectly.
3113+
*/
3114+
static __le32 ext4_xattr_hash_entry_signed(char *name, size_t name_len, __le32 *value, size_t value_count)
3115+
{
3116+
__u32 hash = 0;
3117+
3118+
while (name_len--) {
3119+
hash = (hash << NAME_HASH_SHIFT) ^
3120+
(hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^
3121+
(signed char)*name++;
3122+
}
3123+
while (value_count--) {
3124+
hash = (hash << VALUE_HASH_SHIFT) ^
3125+
(hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^
3126+
le32_to_cpu(*value++);
3127+
}
3128+
return cpu_to_le32(hash);
3129+
}
3130+
30943131
#undef NAME_HASH_SHIFT
30953132
#undef VALUE_HASH_SHIFT
30963133

0 commit comments

Comments
 (0)