Merge bitcoin/bitcoin#36286: crypto: Fix MuHash3072 division by itself
What changed, and why it matters
This commit fixes a bug in Bitcoin Core's MuHash3072 cryptographic code where dividing a MuHash object by itself (x /= x) produced the wrong mathematical result. The fix is straightforward: the code now saves the divisor's numerator before modifying it, so the second multiplication step uses the original value rather than the already-changed one. The bug only affects self-division, and the project maintainers state that no live node or index code actually performs this operation, so running Bitcoin nodes are not affected. Tests and fuzzing targets were updated to catch this case in the future.
No urgent action for node operators; the bug is not reachable in production. Developers should ensure the patch is included in the next release and that the new regression test and fuzz target changes are present.
Security signals we found
Cryptographic correctness bug in MuHash3072 division operator
Self-aliasing in operator/= produces incorrect 1/D result instead of empty set
No production code path identified that triggers self-division
Fix includes regression test and improved fuzz coverage
Evidence from the diff
In src/crypto/muhash.cpp, MuHash3072::operator/= was aliasing-sensitive. When *this and div refer to the same object, the first line (m_numerator.Multiply(div.m_denominator)) mutates m_numerator, so the second line (m_denominator.Multiply(div.m_numerator)) reads the modified numerator instead of the original divisor numerator. The patch copies div.m_numerator into a local Num3072 before any mutation, then uses that copy for the denominator multiplication. The test in src/test/crypto_tests.cpp adds a self-division check, and the fuzz target in src/test/fuzz/muhash.cpp ensures the denominator is not always 1 so the bug would be exposed.
Changed components
src/crypto/muhash.cppsrc/test/crypto_tests.cppsrc/test/fuzz/muhash.cppInspect captured patch +11 / −1
### src/crypto/muhash.cpp
@@ -569,8 +569,9 @@ MuHash3072& MuHash3072::operator*=(const MuHash3072& mul) noexcept
MuHash3072& MuHash3072::operator/=(const MuHash3072& div) noexcept
{
+ const Num3072 div_numerator{div.m_numerator}; // div may alias *this
m_numerator.Multiply(div.m_denominator);
- m_denominator.Multiply(div.m_numerator);
+ m_denominator.Multiply(div_numerator);
return *this;
}
### src/test/crypto_tests.cpp
@@ -1240,6 +1240,12 @@ BOOST_AUTO_TEST_CASE(muhash_tests)
a.Finalize(out2);
BOOST_CHECK_EQUAL(out, out2);
+
+ // Self-division must yield the empty set
+ y /= x; // x=X, y=Y*X/X
+ y /= y; // x=X, y=1
+ y.Finalize(out);
+ BOOST_CHECK_EQUAL(out, out2);
}
MuHash3072 acc = FromInt(0);
### src/test/fuzz/muhash.cpp
@@ -199,6 +199,9 @@ FUZZ_TARGET(muhash)
},
[&] {
// Test that dividing a MuHash by itself brings it back to its initial state
+ // Insert only multiplies the numerator, so without the Remove the denominator
+ // is still 1 and the division is trivially correct
+ muhash.Remove(data);
muhash /= muhash;
muhash.Finalize(out);
out2 = initial_state_hash;Why this scored 24/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.