Keep generation ambiguous for legacy hashes in check repair to prevent false CURRENT certification on size changes
Some checks are pending
Build / build-mac (push) Waiting to run
Build / build-linux (push) Waiting to run
Build / build-arm32 (push) Waiting to run
Build / build-win (push) Waiting to run
Build / build-freebsd (push) Waiting to run
Build / build-musl (push) Waiting to run
CodeQL Advanced / Analyze (c-cpp) (push) Waiting to run
CodeQL Advanced / Analyze (actions) (push) Waiting to run

This commit is contained in:
Andrea Mazzoleni 2026-09-07 11:58:31 +02:00
parent b82ee5ff56
commit c09b850047
3 changed files with 38 additions and 25 deletions

View file

@ -988,23 +988,33 @@ static int repair(struct snapraid_state* state, int rehash, block_off_t pos, uns
if (!mem_is_zero(buffer[failed[j].index], state->block_size))
current_generation_proven = 1;
} else {
size_t pos_size;
unsigned hash_kind;
pos_size = file_block_size(failed[j].file, failed[j].file_pos, state->block_size);
hash_kind = rehash ? state->prevhash : state->hash;
/*
* The stored CHG hash identifies OLD contents under the recovery invariant.
* Generation detection must compare hash identity only and must not apply the
* CURRENT logical-size padding requirement. With MuseAir the hash covers the
* complete canonical RAID block, so a mismatch proves candidate != OLD and
* therefore identifies this validated RAID solution as CURRENT.
* CURRENT logical-size padding requirement.
*
* Legacy hashes still depend on the logical size used to compute the hash.
* The OLD logical size is not available here, so size-changing CHG blocks may
* remain subject to the historical ambiguity with Murmur3/Spooky2.
* When the hash covers the complete canonical RAID block (memhash_is_block()),
* candidate identity is independent of logical size: a mismatch proves
* candidate != OLD and therefore identifies this validated RAID solution as CURRENT.
*
* For hashes that depend on the logical size (Murmur3/Spooky2), the OLD
* logical size is not available. Because a mismatch against CURRENT pos_size
* cannot distinguish candidate != OLD from a change in file size, the
* conservative approach keeps the generation ambiguous and does not treat
* the mismatch as proof of CURRENT.
*/
if (block_hash_cmp(state, rehash, failed[j].block, pos_size, buffer[failed[j].index]) != 0)
current_generation_proven = 1;
if (memhash_is_block(hash_kind)) {
size_t pos_size;
pos_size = file_block_size(failed[j].file, failed[j].file_pos, state->block_size);
if (block_hash_cmp(state, rehash, failed[j].block, pos_size, buffer[failed[j].index]) != 0)
current_generation_proven = 1;
}
}
}

View file

@ -779,29 +779,19 @@ void memhash_block(unsigned kind, const unsigned char* seed, void* digest, const
{
assert(logical_size <= block_size);
switch (kind) {
case HASH_MUSEAIR :
if (memhash_is_block(kind)) {
/*
* MuseAir defines the identity of the complete canonical RAID
* block. The caller must have zero-padded the buffer after the
* logical end of the file block.
* Canonical block hashes define the identity of the complete
* canonical RAID block. The caller must have zero-padded the
* buffer after the logical end of the file block.
*/
memhash(kind, seed, digest, src, block_size);
break;
case HASH_MURMUR3 :
case HASH_SPOOKY2 :
} else {
/*
* Preserve the historical SnapRAID hash semantics for content
* files using legacy hashes.
*/
memhash(kind, seed, digest, src, logical_size);
break;
default :
/* LCOV_EXCL_START */
log_fatal(EINTERNAL, "Internal inconsistency in hash function %u\n", kind);
exit(EXIT_FAILURE);
break;
/* LCOV_EXCL_STOP */
}
}

View file

@ -282,6 +282,19 @@ void memhash(unsigned kind, const unsigned char* seed, void* digest, const void*
*/
void memhash_block(unsigned kind, const unsigned char* seed, void* digest, const void* src, size_t logical_size, size_t block_size);
/**
* Return !=0 if the hash kind operates on the complete canonical RAID block.
*
* Legacy hashes (Murmur3, Spooky2) preserve historical semantics and hash
* only the logical file bytes.
* Modern hashes (MuseAir) hash the complete zero-padded RAID block up to
* block_size.
*/
static inline int memhash_is_block(unsigned kind)
{
return kind == HASH_MUSEAIR;
}
/**
* Return the hash name.
*/