Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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: 3bc753c06dd0 ("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>

+39 -2
+39 -2
fs/ext4/xattr.c
··· 81 81 struct mb_cache_entry **); 82 82 static __le32 ext4_xattr_hash_entry(char *name, size_t name_len, __le32 *value, 83 83 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); 84 86 static void ext4_xattr_rehash(struct ext4_xattr_header *); 85 87 86 88 static const struct xattr_handler * const ext4_xattr_handler_map[] = { ··· 472 470 tmp_data = cpu_to_le32(hash); 473 471 e_hash = ext4_xattr_hash_entry(entry->e_name, entry->e_name_len, 474 472 &tmp_data, 1); 475 - if (e_hash != entry->e_hash) 476 - return -EFSCORRUPTED; 473 + /* All good? */ 474 + if (e_hash == entry->e_hash) 475 + return 0; 476 + 477 + /* 478 + * Not good. Maybe the entry hash was calculated 479 + * using the buggy signed char version? 480 + */ 481 + e_hash = ext4_xattr_hash_entry_signed(entry->e_name, entry->e_name_len, 482 + &tmp_data, 1); 483 + if (e_hash == entry->e_hash) 484 + return 0; 485 + 486 + /* Still no match - bad */ 487 + return -EFSCORRUPTED; 477 488 } 478 489 return 0; 479 490 } ··· 3097 3082 hash = (hash << NAME_HASH_SHIFT) ^ 3098 3083 (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^ 3099 3084 *name++; 3085 + } 3086 + while (value_count--) { 3087 + hash = (hash << VALUE_HASH_SHIFT) ^ 3088 + (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^ 3089 + le32_to_cpu(*value++); 3090 + } 3091 + return cpu_to_le32(hash); 3092 + } 3093 + 3094 + /* 3095 + * ext4_xattr_hash_entry_signed() 3096 + * 3097 + * Compute the hash of an extended attribute incorrectly. 3098 + */ 3099 + static __le32 ext4_xattr_hash_entry_signed(char *name, size_t name_len, __le32 *value, size_t value_count) 3100 + { 3101 + __u32 hash = 0; 3102 + 3103 + while (name_len--) { 3104 + hash = (hash << NAME_HASH_SHIFT) ^ 3105 + (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^ 3106 + (signed char)*name++; 3100 3107 } 3101 3108 while (value_count--) { 3102 3109 hash = (hash << VALUE_HASH_SHIFT) ^