[php-src] PR #24555: Fix GOST and Snefru digests of 512 MiB or more

From: Date: Thu, 22 Oct 2026 15:55:39 +0000
Subject: [php-src] PR #24555: Fix GOST and Snefru digests of 512 MiB or more
Groups: php.git-pulls 
Request: Send a blank email to git-pulls+get-39717@lists.php.net to get a copy of this message
Pull Request: https://github.com/php/php-src/pull/24555 Author: Marti2203 PHP_GOSTUpdate() keeps the message length in bits as two 32-bit words and updates it like this: ```c if ((MAX32 - context->count[0]) < (len * 8)) { context->count[1]++; context->count[0] = MAX32 - context->count[0]; context->count[0] = (len * 8) - context->count[0]; } else { context->count[0] += len * 8; } ``` When the low word wraps, the new low word is one too large, because MAX32 is 2^32 - 1 where 2^32 is needed. A single update of 2^30 bytes or more (2^33 bits) should carry more than once, but only one is added to the high word. PHP_SNEFRUUpdate() has the same code and also computes the bit length of the update as (uint32_t) len * 8. The length is part of the final block, so gost, gost-crypto and snefru/snefru256 give wrong digests for every input of 2^29 bytes (512 MiB) or more. This also applies to hash_file() and to incremental hash_update() calls, because the error occurs in whichever update crosses the boundary. Smaller inputs are not affected. ### Reproducer Digests of N bytes of a, compared with RHash 1.4.4 (an independent implementation) built from source: | N | algo | RHash | PHP before | PHP after | |---|---|---|---|---| | 536870911 | gost | a3464828... | a3464828... | a3464828... | | 536870911 | snefru | 0348bb73... | 0348bb73... | 0348bb73... | | 536870912 | gost | 6c30acc1... | 990c57fe... | 6c30acc1... | | 536870912 | snefru | 0842a8e7... | d2727e81... | 0842a8e7... | | 629145600 | gost | 5e8bdc17... | 985f7997... | 5e8bdc17... | The 536870912 values before the fix are the same for one hash() call and for 512 hash_update() calls of 1 MiB. After the fix, a single update of 2^30 + 5 bytes gives the same digest as 1024 updates of 1 MiB plus one of 5 bytes, for both algorithms. That case exercises the multiple carry. ### Fix Compute the new counter with 64-bit arithmetic and store it back in the two words. The context layout and its serialization are unchanged. ### Test ext/hash/tests/gost_snefru_512mib.phpt hashes 2^29 bytes with 512 hash_update() calls of 1 MiB for gost and snefru, plus one hash('snefru', ...) call on a 512 MiB string. The expected digests are from RHash. The test needs about 512 MiB of memory and takes about 40 s, so like the other large-memory tests it only runs with RUN_RESOURCE_HEAVY_TESTS set, on 64-bit platforms, and not with SKIP_SLOW_TESTS. It sets memory_limit=-1. - Without the fix: fails with 990c57fe... / d2727e81... for all three lines. - With the fix: passes (about 41 s). Without RUN_RESOURCE_HEAVY_TESTS, it is skipped. - ext/hash/tests: 77 passed, 8 skipped (including the existing gost.phpt and snefru.phpt vectors). This issue was found by an LLM-Enhanced Static Analysis Engine and verified manually. 🤖 Generated with [Claude Code](https://claude.com/claude-code)

« previous php.git-pulls (#39717) next »